diff options
280 files changed, 5226 insertions, 1718 deletions
diff --git a/Cargo.lock b/Cargo.lock index ad01ef5e41f..ff7abca4762 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,7 @@ dependencies = [ name = "cargo-miri" version = "0.1.0" dependencies = [ - "cargo_metadata 0.15.0", + "cargo_metadata 0.15.3", "directories", "rustc-build-sysroot", "rustc-workspace-hack", @@ -540,15 +540,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.0" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver", "serde", "serde_json", + "thiserror", ] [[package]] @@ -732,6 +733,7 @@ dependencies = [ name = "clippy" version = "0.1.69" dependencies = [ + "clap 4.1.4", "clippy_lints", "clippy_utils", "compiletest_rs", @@ -762,7 +764,7 @@ name = "clippy_dev" version = "0.0.1" dependencies = [ "aho-corasick", - "clap 3.2.20", + "clap 4.1.4", "indoc", "itertools", "opener", @@ -774,7 +776,7 @@ dependencies = [ name = "clippy_lints" version = "0.1.69" dependencies = [ - "cargo_metadata 0.14.0", + "cargo_metadata 0.15.3", "clippy_utils", "declare_clippy_lint", "if_chain", @@ -4773,6 +4775,7 @@ checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ + "itertools", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -5864,7 +5867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f" dependencies = [ "bstr 1.0.1", - "cargo_metadata 0.15.0", + "cargo_metadata 0.15.3", "color-eyre", "colored", "crossbeam-channel", diff --git a/RELEASES.md b/RELEASES.md index a63d4e8a043..00d0171de6d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,10 @@ +Version 1.67.1 (2023-02-09) +=========================== + +- [Fix interoperability with thin archives.](https://github.com/rust-lang/rust/pull/107360) +- [Fix an internal error in the compiler build process.](https://github.com/rust-lang/rust/pull/105624) +- [Downgrade `clippy::uninlined_format_args` to pedantic.](https://github.com/rust-lang/rust-clippy/pull/10265) + Version 1.67.0 (2023-01-26) ========================== diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 2440f20502a..237e063d8d1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -343,11 +343,11 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let note = match closure_kind_ty.to_opt_closure_kind() { Some(ty::ClosureKind::Fn) => { "closure implements `Fn`, so references to captured variables \ - can't escape the closure" + can't escape the closure" } Some(ty::ClosureKind::FnMut) => { "closure implements `FnMut`, so references to captured variables \ - can't escape the closure" + can't escape the closure" } Some(ty::ClosureKind::FnOnce) => { bug!("BrEnv in a `FnOnce` closure"); @@ -364,7 +364,11 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::BoundRegionKind::BrAnon(..) => None, }, - ty::ReLateBound(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => None, + ty::ReLateBound(..) + | ty::ReVar(..) + | ty::RePlaceholder(..) + | ty::ReErased + | ty::ReError(_) => None, } } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index e0e814cfc0a..c7b22d5f2e6 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -91,11 +91,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { } None => { subst_regions.push(vid); - infcx.tcx.sess.delay_span_bug( + infcx.tcx.re_error_with_message( concrete_type.span, "opaque type with non-universal region substs", - ); - infcx.tcx.lifetimes.re_static + ) } } }; diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index c3dfeedc205..6a3748fded5 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -187,6 +187,7 @@ pub(crate) struct PlaceholderIndices { } impl PlaceholderIndices { + /// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion` pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { let (index, _) = self.indices.insert_full(placeholder); index.into() diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 82ff862479e..2dd24fe0340 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -8,6 +8,7 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; @@ -217,8 +218,27 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.inverse_outlives.add(fr_b, fr_a); } + #[instrument(level = "debug", skip(self))] pub(crate) fn create(mut self) -> CreateResult<'tcx> { let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id()); + + // Insert the facts we know from the predicates. Why? Why not. + let param_env = self.param_env; + self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); + + // - outlives is reflexive, so `'r: 'r` for every region `'r` + // - `'static: 'r` for every region `'r` + // - `'r: 'fn_body` for every (other) universally quantified + // region `'r`, all of which are provided by our caller + let fr_static = self.universal_regions.fr_static; + let fr_fn_body = self.universal_regions.fr_fn_body; + for fr in self.universal_regions.universal_regions() { + debug!("build: relating free region {:?} to itself and to 'static", fr); + self.relate_universal_regions(fr, fr); + self.relate_universal_regions(fr_static, fr); + self.relate_universal_regions(fr, fr_fn_body); + } + let unnormalized_input_output_tys = self .universal_regions .unnormalized_input_tys @@ -236,78 +256,58 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // the `relations` is built. let mut normalized_inputs_and_output = Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); - let constraint_sets: Vec<_> = unnormalized_input_output_tys - .flat_map(|ty| { - debug!("build: input_or_output={:?}", ty); - // We add implied bounds from both the unnormalized and normalized ty. - // See issue #87748 - let constraints_implied1 = self.add_implied_bounds(ty); - let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self - .param_env - .and(type_op::normalize::Normalize::new(ty)) - .fully_perform(self.infcx) - .unwrap_or_else(|_| { - let reported = self - .infcx - .tcx - .sess - .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); - TypeOpOutput { - output: self.infcx.tcx.ty_error_with_guaranteed(reported), - constraints: None, - error_info: None, - } - }); - // Note: we need this in examples like - // ``` - // trait Foo { - // type Bar; - // fn foo(&self) -> &Self::Bar; - // } - // impl Foo for () { - // type Bar = (); - // fn foo(&self) -> &() {} - // } - // ``` - // Both &Self::Bar and &() are WF - let constraints_implied2 = - if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None }; - normalized_inputs_and_output.push(norm_ty); - constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2) - }) - .collect(); + let mut constraints = vec![]; + for ty in unnormalized_input_output_tys { + debug!("build: input_or_output={:?}", ty); + // We add implied bounds from both the unnormalized and normalized ty. + // See issue #87748 + let constraints_unnorm = self.add_implied_bounds(ty); + if let Some(c) = constraints_unnorm { + constraints.push(c) + } + let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self + .param_env + .and(type_op::normalize::Normalize::new(ty)) + .fully_perform(self.infcx) + .unwrap_or_else(|_| { + self.infcx + .tcx + .sess + .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); + TypeOpOutput { + output: self.infcx.tcx.ty_error(), + constraints: None, + error_info: None, + } + }); + if let Some(c) = constraints_normalize { + constraints.push(c) + } - // Insert the facts we know from the predicates. Why? Why not. - let param_env = self.param_env; - self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); + // Note: we need this in examples like + // ``` + // trait Foo { + // type Bar; + // fn foo(&self) -> &Self::Bar; + // } + // impl Foo for () { + // type Bar = (); + // fn foo(&self) ->&() {} + // } + // ``` + // Both &Self::Bar and &() are WF + if ty != norm_ty { + let constraints_norm = self.add_implied_bounds(norm_ty); + if let Some(c) = constraints_norm { + constraints.push(c) + } + } - // Finally: - // - outlives is reflexive, so `'r: 'r` for every region `'r` - // - `'static: 'r` for every region `'r` - // - `'r: 'fn_body` for every (other) universally quantified - // region `'r`, all of which are provided by our caller - let fr_static = self.universal_regions.fr_static; - let fr_fn_body = self.universal_regions.fr_fn_body; - for fr in self.universal_regions.universal_regions() { - debug!("build: relating free region {:?} to itself and to 'static", fr); - self.relate_universal_regions(fr, fr); - self.relate_universal_regions(fr_static, fr); - self.relate_universal_regions(fr, fr_fn_body); + normalized_inputs_and_output.push(norm_ty); } - for data in &constraint_sets { - constraint_conversion::ConstraintConversion::new( - self.infcx, - &self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - Locations::All(span), - span, - ConstraintCategory::Internal, - &mut self.constraints, - ) - .convert_all(data); + for c in constraints { + self.push_region_constraints(c, span); } CreateResult { @@ -321,6 +321,24 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } + #[instrument(skip(self, data), level = "debug")] + fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) { + debug!("constraints generated: {:#?}", data); + + constraint_conversion::ConstraintConversion::new( + self.infcx, + &self.universal_regions, + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + Locations::All(span), + span, + ConstraintCategory::Internal, + &mut self.constraints, + ) + .convert_all(data); + } + /// Update the type of a single local, which should represent /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come @@ -332,6 +350,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) .fully_perform(self.infcx) .unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty)); + debug!(?bounds, ?constraints); self.add_outlives_bounds(bounds); constraints } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5b52846562f..64c96281ed9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -910,6 +910,8 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { } impl<'tcx> MirTypeckRegionConstraints<'tcx> { + /// Creates a `Region` for a given `PlaceholderRegion`, or returns the + /// region that corresponds to a previously created one. fn placeholder_region( &mut self, infcx: &InferCtxt<'tcx>, diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 5380913f5c8..56930c89b2c 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -167,6 +167,9 @@ struct UniversalRegionIndices<'tcx> { /// contains an entry for `ReStatic` -- it might be nice to just /// use a substs, and then handle `ReStatic` another way. indices: FxHashMap<ty::Region<'tcx>, RegionVid>, + + /// The vid assigned to `'static`. Used only for diagnostics. + pub fr_static: RegionVid, } #[derive(Debug, PartialEq)] @@ -609,7 +612,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let subst_mapping = iter::zip(identity_substs.regions(), fr_substs.regions().map(|r| r.to_region_vid())); - UniversalRegionIndices { indices: global_mapping.chain(subst_mapping).collect() } + UniversalRegionIndices { indices: global_mapping.chain(subst_mapping).collect(), fr_static } } fn compute_inputs_and_output( @@ -821,6 +824,11 @@ impl<'tcx> UniversalRegionIndices<'tcx> { pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { if let ty::ReVar(..) = *r { r.to_region_vid() + } else if r.is_error() { + // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the + // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if + // errors are being emitted and 2) it leaves the happy path unaffected. + self.fr_static } else { *self .indices diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index c0daf69e98e..9d3ed3ac5d0 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -21,6 +21,7 @@ jobs: cargo fmt --check rustfmt --check build_system/mod.rs + build: runs-on: ${{ matrix.os }} timeout-minutes: 60 @@ -33,7 +34,7 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-20.04 # FIXME switch to ubuntu-22.04 once #1303 is fixed + - os: ubuntu-latest env: TARGET_TRIPLE: x86_64-unknown-linux-gnu - os: macos-latest @@ -112,23 +113,6 @@ jobs: TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} run: ./y.rs test - - name: Package prebuilt cg_clif - run: tar cvfJ cg_clif.tar.xz dist - - - name: Upload prebuilt cg_clif - if: matrix.os == 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' - uses: actions/upload-artifact@v3 - with: - name: cg_clif-${{ matrix.env.TARGET_TRIPLE }} - path: cg_clif.tar.xz - - - name: Upload prebuilt cg_clif (cross compile) - if: matrix.os != 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - uses: actions/upload-artifact@v3 - with: - name: cg_clif-${{ runner.os }}-cross-x86_64-mingw - path: cg_clif.tar.xz - abi_cafe: runs-on: ${{ matrix.os }} @@ -185,3 +169,129 @@ jobs: env: TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} run: ./y.rs abi-cafe + + + bench: + runs-on: ubuntu-latest + timeout-minutes: 60 + + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v3 + + - name: Cache cargo target dir + uses: actions/cache@v3 + with: + path: build/cg_clif + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Cache cargo bin dir + uses: actions/cache@v3 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-bin-dir-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Use sparse cargo registry + run: | + cat >> ~/.cargo/config.toml <<EOF + [unstable] + sparse-registry = true + EOF + + - name: Install hyperfine + run: cargo install hyperfine || true + + - name: Prepare dependencies + run: ./y.rs prepare + + - name: Build + run: CI_OPT=1 ./y.rs build --sysroot none + + - name: Benchmark + run: CI_OPT=1 ./y.rs bench + + + dist: + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + + defaults: + run: + shell: bash + + strategy: + fail-fast: false + matrix: + include: + # FIXME update at some point in the future once most distros use a newer glibc + - os: ubuntu-20.04 + env: + TARGET_TRIPLE: x86_64-unknown-linux-gnu + - os: macos-latest + env: + TARGET_TRIPLE: x86_64-apple-darwin + # cross-compile from Linux to Windows using mingw + - os: ubuntu-latest + env: + TARGET_TRIPLE: x86_64-pc-windows-gnu + - os: windows-latest + env: + TARGET_TRIPLE: x86_64-pc-windows-msvc + - os: windows-latest + env: + TARGET_TRIPLE: x86_64-pc-windows-gnu + + steps: + - uses: actions/checkout@v3 + + - name: Cache cargo target dir + uses: actions/cache@v3 + with: + path: build/cg_clif + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-dist-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Set MinGW as the default toolchain + if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' + run: rustup set default-host x86_64-pc-windows-gnu + + - name: Install MinGW toolchain and wine + if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' + run: | + sudo apt-get update + sudo apt-get install -y gcc-mingw-w64-x86-64 wine-stable + + - name: Use sparse cargo registry + run: | + cat >> ~/.cargo/config.toml <<EOF + [unstable] + sparse-registry = true + EOF + + - name: Prepare dependencies + run: ./y.rs prepare + + - name: Build backend + run: CI_OPT=1 ./y.rs build --sysroot none + + - name: Build sysroot + run: CI_OPT=1 ./y.rs build + + - name: Package prebuilt cg_clif + run: tar cvfJ cg_clif.tar.xz dist + + - name: Upload prebuilt cg_clif + if: matrix.os == 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' + uses: actions/upload-artifact@v3 + with: + name: cg_clif-${{ matrix.env.TARGET_TRIPLE }} + path: cg_clif.tar.xz + + - name: Upload prebuilt cg_clif (cross compile) + if: matrix.os != 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' + uses: actions/upload-artifact@v3 + with: + name: cg_clif-${{ runner.os }}-cross-x86_64-mingw + path: cg_clif.tar.xz diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index 24f15fc8521..b7e0b68a2a2 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs index dbee9be04ee..0da27f529b3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs +++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs @@ -6,11 +6,10 @@ use super::prepare::GitRepo; use super::utils::{spawn_and_wait, CargoProject, Compiler}; use super::SysrootKind; -pub(crate) static ABI_CAFE_REPO: GitRepo = +static ABI_CAFE_REPO: GitRepo = GitRepo::github("Gankra", "abi-cafe", "4c6dc8c9c687e2b3a760ff2176ce236872b37212", "abi-cafe"); -pub(crate) static ABI_CAFE: CargoProject = - CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe"); +static ABI_CAFE: CargoProject = CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe"); pub(crate) fn run( channel: &str, @@ -19,6 +18,9 @@ pub(crate) fn run( cg_clif_dylib: &Path, bootstrap_host_compiler: &Compiler, ) { + ABI_CAFE_REPO.fetch(dirs); + spawn_and_wait(ABI_CAFE.fetch("cargo", &bootstrap_host_compiler.rustc, dirs)); + eprintln!("Building sysroot for abi-cafe"); build_sysroot::build_sysroot( dirs, diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs index 01d44dafbdd..a9a851d0a8a 100644 --- a/compiler/rustc_codegen_cranelift/build_system/bench.rs +++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs @@ -5,9 +5,9 @@ use std::path::Path; use super::path::{Dirs, RelPath}; use super::prepare::GitRepo; use super::rustc_info::get_file_name; -use super::utils::{hyperfine_command, is_ci, spawn_and_wait, CargoProject, Compiler}; +use super::utils::{hyperfine_command, spawn_and_wait, CargoProject, Compiler}; -pub(crate) static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github( +static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github( "ebobby", "simple-raytracer", "804a7a21b9e673a482797aa289a18ed480e4d813", @@ -15,10 +15,10 @@ pub(crate) static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github( ); // Use a separate target dir for the initial LLVM build to reduce unnecessary recompiles -pub(crate) static SIMPLE_RAYTRACER_LLVM: CargoProject = +static SIMPLE_RAYTRACER_LLVM: CargoProject = CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer_llvm"); -pub(crate) static SIMPLE_RAYTRACER: CargoProject = +static SIMPLE_RAYTRACER: CargoProject = CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer"); pub(crate) fn benchmark(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { @@ -32,6 +32,15 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { std::process::exit(1); } + if !SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).exists() { + SIMPLE_RAYTRACER_REPO.fetch(dirs); + spawn_and_wait(SIMPLE_RAYTRACER.fetch( + &bootstrap_host_compiler.cargo, + &bootstrap_host_compiler.rustc, + dirs, + )); + } + eprintln!("[LLVM BUILD] simple-raytracer"); let build_cmd = SIMPLE_RAYTRACER_LLVM.build(bootstrap_host_compiler, dirs); spawn_and_wait(build_cmd); @@ -45,10 +54,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { ) .unwrap(); - let run_runs = env::var("RUN_RUNS") - .unwrap_or(if is_ci() { "2" } else { "10" }.to_string()) - .parse() - .unwrap(); + let bench_runs = env::var("BENCH_RUNS").unwrap_or_else(|_| "10".to_string()).parse().unwrap(); eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); let cargo_clif = @@ -57,24 +63,24 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { let target_dir = SIMPLE_RAYTRACER.target_dir(dirs); let clean_cmd = format!( - "cargo clean --manifest-path {manifest_path} --target-dir {target_dir}", + "RUSTC=rustc cargo clean --manifest-path {manifest_path} --target-dir {target_dir}", manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); let llvm_build_cmd = format!( - "cargo build --manifest-path {manifest_path} --target-dir {target_dir}", + "RUSTC=rustc cargo build --manifest-path {manifest_path} --target-dir {target_dir}", manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); let clif_build_cmd = format!( - "{cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir}", + "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir}", cargo_clif = cargo_clif.display(), manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); let bench_compile = - hyperfine_command(1, run_runs, Some(&clean_cmd), &llvm_build_cmd, &clif_build_cmd); + hyperfine_command(1, bench_runs, Some(&clean_cmd), &llvm_build_cmd, &clif_build_cmd); spawn_and_wait(bench_compile); @@ -87,7 +93,7 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { let mut bench_run = hyperfine_command( 0, - run_runs, + bench_runs, None, Path::new(".").join(get_file_name("raytracer_cg_llvm", "bin")).to_str().unwrap(), Path::new(".").join(get_file_name("raytracer_cg_clif", "bin")).to_str().unwrap(), diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index 514404305a3..4b740fa2db6 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use super::path::{Dirs, RelPath}; use super::rustc_info::get_file_name; -use super::utils::{is_ci, CargoProject, Compiler}; +use super::utils::{is_ci, is_ci_opt, CargoProject, Compiler}; pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif"); @@ -26,7 +26,9 @@ pub(crate) fn build_backend( // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway cmd.env("CARGO_BUILD_INCREMENTAL", "false"); - cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true"); + if !is_ci_opt() { + cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true"); + } } if use_unstable_features { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index bd04fdbe304..2e04f2c6811 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -248,6 +248,9 @@ fn build_clif_sysroot_for_triple( build_cmd.arg("--release"); } build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif"); + if compiler.triple.contains("apple") { + build_cmd.env("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO", "packed"); + } spawn_and_wait(build_cmd); for entry in fs::read_dir(build_dir.join("deps")).unwrap() { diff --git a/compiler/rustc_codegen_cranelift/build_system/mod.rs b/compiler/rustc_codegen_cranelift/build_system/mod.rs index 8dcbe8de189..8a53acdf7e3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/mod.rs +++ b/compiler/rustc_codegen_cranelift/build_system/mod.rs @@ -2,7 +2,7 @@ use std::env; use std::path::PathBuf; use std::process; -use self::utils::{is_ci, Compiler}; +use self::utils::{is_ci, is_ci_opt, Compiler}; mod abi_cafe; mod bench; @@ -53,8 +53,10 @@ pub fn main() { // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway env::set_var("CARGO_BUILD_INCREMENTAL", "false"); - // Enable the Cranelift verifier - env::set_var("CG_CLIF_ENABLE_VERIFIER", "1"); + if !is_ci_opt() { + // Enable the Cranelift verifier + env::set_var("CG_CLIF_ENABLE_VERIFIER", "1"); + } } let mut args = env::args().skip(1); diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index f25a81dc234..50b1b7836de 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -11,22 +11,18 @@ use super::utils::{copy_dir_recursively, git_command, retry_spawn_and_wait, spaw pub(crate) fn prepare(dirs: &Dirs) { RelPath::DOWNLOAD.ensure_fresh(dirs); - spawn_and_wait(super::build_backend::CG_CLIF.fetch("cargo", dirs)); + spawn_and_wait(super::build_backend::CG_CLIF.fetch("cargo", "rustc", dirs)); prepare_sysroot(dirs); - spawn_and_wait(super::build_sysroot::STANDARD_LIBRARY.fetch("cargo", dirs)); - spawn_and_wait(super::tests::LIBCORE_TESTS.fetch("cargo", dirs)); + spawn_and_wait(super::build_sysroot::STANDARD_LIBRARY.fetch("cargo", "rustc", dirs)); + spawn_and_wait(super::tests::LIBCORE_TESTS.fetch("cargo", "rustc", dirs)); - super::abi_cafe::ABI_CAFE_REPO.fetch(dirs); - spawn_and_wait(super::abi_cafe::ABI_CAFE.fetch("cargo", dirs)); super::tests::RAND_REPO.fetch(dirs); - spawn_and_wait(super::tests::RAND.fetch("cargo", dirs)); + spawn_and_wait(super::tests::RAND.fetch("cargo", "rustc", dirs)); super::tests::REGEX_REPO.fetch(dirs); - spawn_and_wait(super::tests::REGEX.fetch("cargo", dirs)); + spawn_and_wait(super::tests::REGEX.fetch("cargo", "rustc", dirs)); super::tests::PORTABLE_SIMD_REPO.fetch(dirs); - spawn_and_wait(super::tests::PORTABLE_SIMD.fetch("cargo", dirs)); - super::bench::SIMPLE_RAYTRACER_REPO.fetch(dirs); - spawn_and_wait(super::bench::SIMPLE_RAYTRACER.fetch("cargo", dirs)); + spawn_and_wait(super::tests::PORTABLE_SIMD.fetch("cargo", "rustc", dirs)); } fn prepare_sysroot(dirs: &Dirs) { @@ -80,7 +76,7 @@ impl GitRepo { } } - fn fetch(&self, dirs: &Dirs) { + pub(crate) fn fetch(&self, dirs: &Dirs) { match self.url { GitRepoUrl::Github { user, repo } => { clone_repo_shallow_github( diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index dcfadd73756..e9486888f86 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -1,4 +1,3 @@ -use super::bench::SIMPLE_RAYTRACER; use super::build_sysroot::{self, SYSROOT_SRC}; use super::config; use super::path::{Dirs, RelPath}; @@ -134,10 +133,6 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(build_cmd); } }), - TestCase::custom("test.simple-raytracer", &|runner| { - SIMPLE_RAYTRACER.clean(&runner.dirs); - spawn_and_wait(SIMPLE_RAYTRACER.build(&runner.target_compiler, &runner.dirs)); - }), TestCase::custom("test.libcore", &|runner| { LIBCORE_TESTS.clean(&runner.dirs); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index da2a94a0a4f..abc5bab4942 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -121,10 +121,18 @@ impl CargoProject { } #[must_use] - pub(crate) fn fetch(&self, cargo: impl AsRef<Path>, dirs: &Dirs) -> Command { + pub(crate) fn fetch( + &self, + cargo: impl AsRef<Path>, + rustc: impl AsRef<Path>, + dirs: &Dirs, + ) -> Command { let mut cmd = Command::new(cargo.as_ref()); - cmd.arg("fetch").arg("--manifest-path").arg(self.manifest_path(dirs)); + cmd.env("RUSTC", rustc.as_ref()) + .arg("fetch") + .arg("--manifest-path") + .arg(self.manifest_path(dirs)); cmd } @@ -271,5 +279,9 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) { } pub(crate) fn is_ci() -> bool { - env::var("CI").as_deref() == Ok("true") + env::var("CI").is_ok() +} + +pub(crate) fn is_ci_opt() -> bool { + env::var("CI_OPT").is_ok() } diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index d49cc90791a..d6e3924a24d 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -44,7 +44,6 @@ aot.issue-72793 testsuite.extended_sysroot test.rust-random/rand -test.simple-raytracer test.libcore test.regex-shootout-regex-dna test.regex diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 77345b9a17c..40fb54b9159 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-01-20" +channel = "nightly-2023-02-06" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 07c9ae6ee9f..e14a129dbc2 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -91,6 +91,7 @@ rm tests/ui/proc-macro/proc-macro-deprecated-attr.rs # same rm tests/ui/proc-macro/quote-debug.rs # same rm tests/ui/proc-macro/no-missing-docs.rs # same rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs # same +rm tests/ui/proc-macro/allowed-signatures.rs # same # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended diff --git a/compiler/rustc_codegen_cranelift/src/abi/comments.rs b/compiler/rustc_codegen_cranelift/src/abi/comments.rs index 7f4619b5c94..abf63e33c35 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/comments.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/comments.rs @@ -98,12 +98,12 @@ pub(super) fn add_local_place_comments<'tcx>( } CPlaceInner::VarPair(place_local, var1, var2) => { assert_eq!(local, place_local); - ("ssa", Cow::Owned(format!(",var=({}, {})", var1.index(), var2.index()))) + ("ssa", Cow::Owned(format!("var=({}, {})", var1.index(), var2.index()))) } CPlaceInner::VarLane(_local, _var, _lane) => unreachable!(), CPlaceInner::Addr(ptr, meta) => { let meta = if let Some(meta) = meta { - Cow::Owned(format!(",meta={}", meta)) + Cow::Owned(format!("meta={}", meta)) } else { Cow::Borrowed("") }; diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 8508227179a..1c73957ca57 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -70,37 +70,13 @@ fn codegen_inner( params: arg_tys.iter().cloned().map(AbiParam::new).collect(), returns: output.into_iter().map(AbiParam::new).collect(), }; - - let caller_name = format!("__rust_{}", method.name); - let callee_name = kind.fn_name(method.name); - - let func_id = module.declare_function(&caller_name, Linkage::Export, &sig).unwrap(); - - let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap(); - - let mut ctx = Context::new(); - ctx.func.signature = sig.clone(); - { - let mut func_ctx = FunctionBuilderContext::new(); - let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); - - let block = bcx.create_block(); - bcx.switch_to_block(block); - let args = arg_tys - .into_iter() - .map(|ty| bcx.append_block_param(block, ty)) - .collect::<Vec<Value>>(); - - let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func); - let call_inst = bcx.ins().call(callee_func_ref, &args); - let results = bcx.inst_results(call_inst).to_vec(); // Clone to prevent borrow error - - bcx.ins().return_(&results); - bcx.seal_all_blocks(); - bcx.finalize(); - } - module.define_function(func_id, &mut ctx).unwrap(); - unwind_context.add_function(func_id, &ctx, module.isa()); + crate::common::create_wrapper_function( + module, + unwind_context, + sig, + &format!("__rust_{}", method.name), + &kind.fn_name(method.name), + ); } let sig = Signature { @@ -108,36 +84,13 @@ fn codegen_inner( params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], returns: vec![], }; - - let callee_name = alloc_error_handler_kind.fn_name(sym::oom); - - let func_id = - module.declare_function("__rust_alloc_error_handler", Linkage::Export, &sig).unwrap(); - - let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap(); - - let mut ctx = Context::new(); - ctx.func.signature = sig; - { - let mut func_ctx = FunctionBuilderContext::new(); - let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); - - let block = bcx.create_block(); - bcx.switch_to_block(block); - let args = (&[usize_ty, usize_ty]) - .iter() - .map(|&ty| bcx.append_block_param(block, ty)) - .collect::<Vec<Value>>(); - - let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func); - bcx.ins().call(callee_func_ref, &args); - - bcx.ins().trap(TrapCode::UnreachableCodeReached); - bcx.seal_all_blocks(); - bcx.finalize(); - } - module.define_function(func_id, &mut ctx).unwrap(); - unwind_context.add_function(func_id, &ctx, module.isa()); + crate::common::create_wrapper_function( + module, + unwind_context, + sig, + "__rust_alloc_error_handler", + &alloc_error_handler_kind.fn_name(sym::oom), + ); let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap(); let mut data_ctx = DataContext::new(); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index dffb2ed8f4f..189d952a92f 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -21,23 +21,6 @@ pub(crate) struct CodegenedFunction { func_debug_cx: Option<FunctionDebugContext>, } -#[cfg_attr(not(feature = "jit"), allow(dead_code))] -pub(crate) fn codegen_and_compile_fn<'tcx>( - tcx: TyCtxt<'tcx>, - cx: &mut crate::CodegenCx, - cached_context: &mut Context, - module: &mut dyn Module, - instance: Instance<'tcx>, -) { - let _inst_guard = - crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name)); - - let cached_func = std::mem::replace(&mut cached_context.func, Function::new()); - let codegened_func = codegen_fn(tcx, cx, cached_func, module, instance); - - compile_fn(cx, cached_context, module, codegened_func); -} - pub(crate) fn codegen_fn<'tcx>( tcx: TyCtxt<'tcx>, cx: &mut crate::CodegenCx, @@ -47,6 +30,9 @@ pub(crate) fn codegen_fn<'tcx>( ) -> CodegenedFunction { debug_assert!(!instance.substs.needs_infer()); + let symbol_name = tcx.symbol_name(instance).name.to_string(); + let _timer = tcx.prof.generic_activity_with_arg("codegen fn", &*symbol_name); + let mir = tcx.instance_mir(instance.def); let _mir_guard = crate::PrintOnPanic(|| { let mut buf = Vec::new(); @@ -58,7 +44,6 @@ pub(crate) fn codegen_fn<'tcx>( }); // Declare function - let symbol_name = tcx.symbol_name(instance).name.to_string(); let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance); let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap(); @@ -112,7 +97,7 @@ pub(crate) fn codegen_fn<'tcx>( next_ssa_var: 0, }; - tcx.sess.time("codegen clif ir", || codegen_fn_body(&mut fx, start_block)); + tcx.prof.generic_activity("codegen clif ir").run(|| codegen_fn_body(&mut fx, start_block)); fx.bcx.seal_all_blocks(); fx.bcx.finalize(); @@ -146,6 +131,9 @@ pub(crate) fn compile_fn( module: &mut dyn Module, codegened_func: CodegenedFunction, ) { + let _timer = + cx.profiler.generic_activity_with_arg("compile function", &*codegened_func.symbol_name); + let clif_comments = codegened_func.clif_comments; // Store function in context @@ -191,9 +179,30 @@ pub(crate) fn compile_fn( }; // Define function - cx.profiler.verbose_generic_activity("define function").run(|| { + cx.profiler.generic_activity("define function").run(|| { context.want_disasm = cx.should_write_ir; module.define_function(codegened_func.func_id, context).unwrap(); + + if cx.profiler.enabled() { + let mut recording_args = false; + cx.profiler + .generic_activity_with_arg_recorder( + "define function (clif pass timings)", + |recorder| { + let pass_times = cranelift_codegen::timing::take_current(); + // Replace newlines with | as measureme doesn't allow control characters like + // newlines inside strings. + recorder.record_arg(format!("{}", pass_times).replace("\n", " | ")); + recording_args = true; + }, + ) + .run(|| { + if recording_args { + // Wait a tiny bit to ensure chrome's profiler doesn't hide the event + std::thread::sleep(std::time::Duration::from_nanos(2)) + } + }); + } }); if cx.should_write_ir { @@ -220,7 +229,7 @@ pub(crate) fn compile_fn( let isa = module.isa(); let debug_context = &mut cx.debug_context; let unwind_context = &mut cx.unwind_context; - cx.profiler.verbose_generic_activity("generate debug info").run(|| { + cx.profiler.generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { codegened_func.func_debug_cx.unwrap().finalize( debug_context, @@ -237,7 +246,7 @@ pub(crate) fn verify_func( writer: &crate::pretty_clif::CommentWriter, func: &Function, ) { - tcx.sess.time("verify clif ir", || { + tcx.prof.generic_activity("verify clif ir").run(|| { let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder()); match cranelift_codegen::verify_function(&func, &flags) { Ok(_) => {} @@ -273,7 +282,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); return; } - fx.tcx.sess.time("codegen prelude", || crate::abi::codegen_fn_prelude(fx, start_block)); + fx.tcx + .prof + .generic_activity("codegen prelude") + .run(|| crate::abi::codegen_fn_prelude(fx, start_block)); for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() { let block = fx.get_block(bb); @@ -434,7 +446,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { cleanup: _, from_hir_call: _, } => { - fx.tcx.sess.time("codegen call", || { + fx.tcx.prof.generic_activity("codegen call").run(|| { crate::abi::codegen_terminator_call( fx, mir::SourceInfo { span: *fn_span, ..source_info }, @@ -778,17 +790,30 @@ fn codegen_stmt<'tcx>( let val = CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), val.into()); lval.write_cvalue(fx, val); } - Rvalue::Aggregate(ref kind, ref operands) => match kind.as_ref() { - AggregateKind::Array(_ty) => { - for (i, operand) in operands.iter().enumerate() { - let operand = codegen_operand(fx, operand); - let index = fx.bcx.ins().iconst(fx.pointer_type, i as i64); - let to = lval.place_index(fx, index); - to.write_cvalue(fx, operand); + Rvalue::Aggregate(ref kind, ref operands) => { + let (variant_index, variant_dest, active_field_index) = match **kind { + mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { + let variant_dest = lval.downcast_variant(fx, variant_index); + (variant_index, variant_dest, active_field_index) } + _ => (VariantIdx::from_u32(0), lval, None), + }; + if active_field_index.is_some() { + assert_eq!(operands.len(), 1); + } + for (i, operand) in operands.iter().enumerate() { + let operand = codegen_operand(fx, operand); + let field_index = active_field_index.unwrap_or(i); + let to = if let mir::AggregateKind::Array(_) = **kind { + let index = fx.bcx.ins().iconst(fx.pointer_type, field_index as i64); + variant_dest.place_index(fx, index) + } else { + variant_dest.place_field(fx, mir::Field::new(field_index)) + }; + to.write_cvalue(fx, operand); } - _ => unreachable!("shouldn't exist at codegen {:?}", to_place_and_rval.1), - }, + crate::discriminant::codegen_set_discriminant(fx, lval, variant_index); + } } } StatementKind::StorageLive(_) diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index f41af3a9e63..a8be0d32cc8 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -254,6 +254,44 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool { } } +pub(crate) fn create_wrapper_function( + module: &mut dyn Module, + unwind_context: &mut UnwindContext, + sig: Signature, + wrapper_name: &str, + callee_name: &str, +) { + let wrapper_func_id = module.declare_function(wrapper_name, Linkage::Export, &sig).unwrap(); + let callee_func_id = module.declare_function(callee_name, Linkage::Import, &sig).unwrap(); + + let mut ctx = Context::new(); + ctx.func.signature = sig; + { + let mut func_ctx = FunctionBuilderContext::new(); + let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + + let block = bcx.create_block(); + bcx.switch_to_block(block); + let func = &mut bcx.func.stencil; + let args = func + .signature + .params + .iter() + .map(|param| func.dfg.append_block_param(block, param.value_type)) + .collect::<Vec<Value>>(); + + let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func); + let call_inst = bcx.ins().call(callee_func_ref, &args); + let results = bcx.inst_results(call_inst).to_vec(); // Clone to prevent borrow error + + bcx.ins().return_(&results); + bcx.seal_all_blocks(); + bcx.finalize(); + } + module.define_function(wrapper_func_id, &mut ctx).unwrap(); + unwind_context.add_function(wrapper_func_id, &ctx, module.isa()); +} + pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) cx: &'clif mut crate::CodegenCx, pub(crate) module: &'m mut dyn Module, diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index c6a247cf59e..8a53baa763a 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -1,14 +1,33 @@ +#[cfg(all(unix, feature = "jit"))] +use std::ffi::c_int; +#[cfg(feature = "jit")] +use std::ffi::c_void; + +// FIXME replace with core::ffi::c_size_t once stablized +#[allow(non_camel_case_types)] +#[cfg(feature = "jit")] +type size_t = usize; + macro_rules! builtin_functions { - ($register:ident; $(fn $name:ident($($arg_name:ident: $arg_ty:ty),*) -> $ret_ty:ty;)*) => { + ( + $register:ident; + $( + $(#[$attr:meta])? + fn $name:ident($($arg_name:ident: $arg_ty:ty),*) -> $ret_ty:ty; + )* + ) => { #[cfg(feature = "jit")] #[allow(improper_ctypes)] extern "C" { - $(fn $name($($arg_name: $arg_ty),*) -> $ret_ty;)* + $( + $(#[$attr])? + fn $name($($arg_name: $arg_ty),*) -> $ret_ty; + )* } #[cfg(feature = "jit")] pub(crate) fn $register(builder: &mut cranelift_jit::JITBuilder) { - for (name, val) in [$((stringify!($name), $name as *const u8)),*] { + for (name, val) in [$($(#[$attr])? (stringify!($name), $name as *const u8)),*] { builder.symbol(name, val); } } @@ -40,4 +59,17 @@ builtin_functions! { fn __fixdfti(f: f64) -> i128; fn __fixunssfti(f: f32) -> u128; fn __fixunsdfti(f: f64) -> u128; + + // allocator + // NOTE: These need to be mentioned here despite not being part of compiler_builtins because + // newer glibc resolve dlsym("malloc") to libc.so despite the override in the rustc binary to + // use jemalloc. Libraries opened with dlopen still get the jemalloc version, causing multiple + // allocators to be mixed, resulting in a crash. + fn calloc(nobj: size_t, size: size_t) -> *mut c_void; + #[cfg(unix)] + fn posix_memalign(memptr: *mut *mut c_void, align: size_t, size: size_t) -> c_int; + fn malloc(size: size_t) -> *mut c_void; + fn realloc(p: *mut c_void, size: size_t) -> *mut c_void; + fn free(p: *mut c_void) -> (); + } diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs index 45522fb1a4c..263401e1c4b 100644 --- a/compiler/rustc_codegen_cranelift/src/config.rs +++ b/compiler/rustc_codegen_cranelift/src/config.rs @@ -42,12 +42,6 @@ pub struct BackendConfig { /// Defaults to the value of `CG_CLIF_JIT_ARGS`. pub jit_args: Vec<String>, - /// Display the time it took to perform codegen for a crate. - /// - /// Defaults to true when the `CG_CLIF_DISPLAY_CG_TIME` env var is set to 1 or false otherwise. - /// Can be set using `-Cllvm-args=display_cg_time=...`. - pub display_cg_time: bool, - /// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run /// once before passing the clif ir to Cranelift for compilation. /// @@ -73,7 +67,6 @@ impl Default for BackendConfig { let args = std::env::var("CG_CLIF_JIT_ARGS").unwrap_or_else(|_| String::new()); args.split(' ').map(|arg| arg.to_string()).collect() }, - display_cg_time: bool_env_var("CG_CLIF_DISPLAY_CG_TIME"), enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"), disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"), } @@ -92,7 +85,6 @@ impl BackendConfig { if let Some((name, value)) = opt.split_once('=') { match name { "mode" => config.codegen_mode = value.parse()?, - "display_cg_time" => config.display_cg_time = parse_bool(name, value)?, "enable_verifier" => config.enable_verifier = parse_bool(name, value)?, "disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?, _ => return Err(format!("Unknown option `{}`", name)), diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index d4494a9e45d..58b01dfb5b0 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -272,25 +272,25 @@ fn module_codegen( ConcurrencyLimiterToken, ), ) -> OngoingModuleCodegen { - let (cgu_name, mut cx, mut module, codegened_functions) = tcx.sess.time("codegen cgu", || { - let cgu = tcx.codegen_unit(cgu_name); - let mono_items = cgu.items_in_deterministic_order(tcx); - - let mut module = make_module(tcx.sess, &backend_config, cgu_name.as_str().to_string()); - - let mut cx = crate::CodegenCx::new( - tcx, - backend_config.clone(), - module.isa(), - tcx.sess.opts.debuginfo != DebugInfo::None, - cgu_name, - ); - super::predefine_mono_items(tcx, &mut module, &mono_items); - let mut codegened_functions = vec![]; - for (mono_item, _) in mono_items { - match mono_item { - MonoItem::Fn(inst) => { - tcx.sess.time("codegen fn", || { + let (cgu_name, mut cx, mut module, codegened_functions) = + tcx.prof.verbose_generic_activity_with_arg("codegen cgu", cgu_name.as_str()).run(|| { + let cgu = tcx.codegen_unit(cgu_name); + let mono_items = cgu.items_in_deterministic_order(tcx); + + let mut module = make_module(tcx.sess, &backend_config, cgu_name.as_str().to_string()); + + let mut cx = crate::CodegenCx::new( + tcx, + backend_config.clone(), + module.isa(), + tcx.sess.opts.debuginfo != DebugInfo::None, + cgu_name, + ); + super::predefine_mono_items(tcx, &mut module, &mono_items); + let mut codegened_functions = vec![]; + for (mono_item, _) in mono_items { + match mono_item { + MonoItem::Fn(inst) => { let codegened_function = crate::base::codegen_fn( tcx, &mut cx, @@ -299,53 +299,68 @@ fn module_codegen( inst, ); codegened_functions.push(codegened_function); - }); - } - MonoItem::Static(def_id) => { - crate::constant::codegen_static(tcx, &mut module, def_id) - } - MonoItem::GlobalAsm(item_id) => { - crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id); + } + MonoItem::Static(def_id) => { + crate::constant::codegen_static(tcx, &mut module, def_id) + } + MonoItem::GlobalAsm(item_id) => { + crate::global_asm::codegen_global_asm_item( + tcx, + &mut cx.global_asm, + item_id, + ); + } } } - } - crate::main_shim::maybe_create_entry_wrapper( - tcx, - &mut module, - &mut cx.unwind_context, - false, - cgu.is_primary(), - ); + crate::main_shim::maybe_create_entry_wrapper( + tcx, + &mut module, + &mut cx.unwind_context, + false, + cgu.is_primary(), + ); - let cgu_name = cgu.name().as_str().to_owned(); + let cgu_name = cgu.name().as_str().to_owned(); - (cgu_name, cx, module, codegened_functions) - }); + (cgu_name, cx, module, codegened_functions) + }); OngoingModuleCodegen::Async(std::thread::spawn(move || { - cx.profiler.clone().verbose_generic_activity("compile functions").run(|| { - let mut cached_context = Context::new(); - for codegened_func in codegened_functions { - crate::base::compile_fn(&mut cx, &mut cached_context, &mut module, codegened_func); - } - }); + cx.profiler.clone().verbose_generic_activity_with_arg("compile functions", &*cgu_name).run( + || { + let mut cached_context = Context::new(); + for codegened_func in codegened_functions { + crate::base::compile_fn( + &mut cx, + &mut cached_context, + &mut module, + codegened_func, + ); + } + }, + ); - let global_asm_object_file = - cx.profiler.verbose_generic_activity("compile assembly").run(|| { + let global_asm_object_file = cx + .profiler + .verbose_generic_activity_with_arg("compile assembly", &*cgu_name) + .run(|| { crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm) })?; - let codegen_result = cx.profiler.verbose_generic_activity("write object file").run(|| { - emit_cgu( - &global_asm_config.output_filenames, - &cx.profiler, - cgu_name, - module, - cx.debug_context, - cx.unwind_context, - global_asm_object_file, - ) - }); + let codegen_result = cx + .profiler + .verbose_generic_activity_with_arg("write object file", &*cgu_name) + .run(|| { + emit_cgu( + &global_asm_config.output_filenames, + &cx.profiler, + cgu_name, + module, + cx.debug_context, + cx.unwind_context, + global_asm_object_file, + ) + }); std::mem::drop(token); codegen_result })) @@ -375,7 +390,7 @@ pub(crate) fn run_aot( let mut concurrency_limiter = ConcurrencyLimiter::new(tcx.sess, cgus.len()); - let modules = super::time(tcx, backend_config.display_cg_time, "codegen mono items", || { + let modules = tcx.sess.time("codegen mono items", || { cgus.iter() .map(|cgu| { let cgu_reuse = if backend_config.disable_incr_cache { @@ -437,7 +452,6 @@ pub(crate) fn run_aot( }; let metadata_module = if need_metadata_module { - let _timer = tcx.prof.generic_activity("codegen crate metadata"); let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || { use rustc_middle::mir::mono::CodegenUnitNameBuilder; diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index be1b8c9ead3..8b5a2da2c59 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -121,22 +121,20 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { .into_iter() .collect::<Vec<(_, (_, _))>>(); - super::time(tcx, backend_config.display_cg_time, "codegen mono items", || { + tcx.sess.time("codegen mono items", || { super::predefine_mono_items(tcx, &mut jit_module, &mono_items); for (mono_item, _) in mono_items { match mono_item { MonoItem::Fn(inst) => match backend_config.codegen_mode { CodegenMode::Aot => unreachable!(), CodegenMode::Jit => { - tcx.sess.time("codegen fn", || { - crate::base::codegen_and_compile_fn( - tcx, - &mut cx, - &mut cached_context, - &mut jit_module, - inst, - ) - }); + codegen_and_compile_fn( + tcx, + &mut cx, + &mut cached_context, + &mut jit_module, + inst, + ); } CodegenMode::JitLazy => { codegen_shim(tcx, &mut cx, &mut cached_context, &mut jit_module, inst) @@ -219,6 +217,24 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { } } +pub(crate) fn codegen_and_compile_fn<'tcx>( + tcx: TyCtxt<'tcx>, + cx: &mut crate::CodegenCx, + cached_context: &mut Context, + module: &mut dyn Module, + instance: Instance<'tcx>, +) { + tcx.prof.generic_activity("codegen and compile fn").run(|| { + let _inst_guard = + crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name)); + + let cached_func = std::mem::replace(&mut cached_context.func, Function::new()); + let codegened_func = crate::base::codegen_fn(tcx, cx, cached_func, module, instance); + + crate::base::compile_fn(cx, cached_context, module, codegened_func); + }); +} + extern "C" fn clif_jit_fn( instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8, @@ -271,15 +287,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> false, Symbol::intern("dummy_cgu_name"), ); - tcx.sess.time("codegen fn", || { - crate::base::codegen_and_compile_fn( - tcx, - &mut cx, - &mut Context::new(), - jit_module, - instance, - ) - }); + codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance); assert!(cx.global_asm.is_empty()); jit_module.finalize_definitions().unwrap(); diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index 6e925cea277..d09d3a52975 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -17,7 +17,7 @@ fn predefine_mono_items<'tcx>( module: &mut dyn Module, mono_items: &[(MonoItem<'tcx>, (RLinkage, Visibility))], ) { - tcx.sess.time("predefine functions", || { + tcx.prof.generic_activity("predefine functions").run(|| { let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE); for &(mono_item, (linkage, visibility)) in mono_items { match mono_item { @@ -39,16 +39,3 @@ fn predefine_mono_items<'tcx>( } }); } - -fn time<R>(tcx: TyCtxt<'_>, display: bool, name: &'static str, f: impl FnOnce() -> R) -> R { - if display { - println!("[{:<30}: {}] start", tcx.crate_name(LOCAL_CRATE), name); - let before = std::time::Instant::now(); - let res = tcx.sess.time(name, f); - let after = std::time::Instant::now(); - println!("[{:<30}: {}] end time: {:?}", tcx.crate_name(LOCAL_CRATE), name, after - before); - res - } else { - tcx.sess.time(name, f) - } -} diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index dcbcaba30fe..46c78ce6a1e 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -7,7 +7,7 @@ use std::process::{Command, Stdio}; use std::sync::Arc; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; -use rustc_hir::ItemId; +use rustc_hir::{InlineAsmOperand, ItemId}; use rustc_session::config::{OutputFilenames, OutputType}; use crate::prelude::*; @@ -23,7 +23,46 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, for piece in asm.template { match *piece { InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s), - InlineAsmTemplatePiece::Placeholder { .. } => todo!(), + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: op_sp } => { + match asm.operands[operand_idx].0 { + InlineAsmOperand::Const { ref anon_const } => { + let const_value = + tcx.const_eval_poly(anon_const.def_id.to_def_id()).unwrap_or_else( + |_| span_bug!(op_sp, "asm const cannot be resolved"), + ); + let ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); + let string = rustc_codegen_ssa::common::asm_const_to_str( + tcx, + op_sp, + const_value, + RevealAllLayoutCx(tcx).layout_of(ty), + ); + global_asm.push_str(&string); + } + InlineAsmOperand::SymFn { anon_const } => { + let ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); + let instance = match ty.kind() { + &ty::FnDef(def_id, substs) => Instance::new(def_id, substs), + _ => span_bug!(op_sp, "asm sym is not a function"), + }; + let symbol = tcx.symbol_name(instance); + // FIXME handle the case where the function was made private to the + // current codegen unit + global_asm.push_str(symbol.name); + } + InlineAsmOperand::SymStatic { path: _, def_id } => { + let instance = Instance::mono(tcx, def_id).polymorphize(tcx); + let symbol = tcx.symbol_name(instance); + global_asm.push_str(symbol.name); + } + InlineAsmOperand::In { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::InOut { .. } + | InlineAsmOperand::SplitInOut { .. } => { + span_bug!(op_sp, "invalid operand type for global_asm!") + } + } + } } } global_asm.push_str("\n.att_syntax\n\n"); diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 3fcc84d3929..6206fbf7dd5 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -9,9 +9,33 @@ use rustc_middle::mir::InlineAsmOperand; use rustc_span::sym; use rustc_target::asm::*; +enum CInlineAsmOperand<'tcx> { + In { + reg: InlineAsmRegOrRegClass, + value: CValue<'tcx>, + }, + Out { + reg: InlineAsmRegOrRegClass, + late: bool, + place: Option<CPlace<'tcx>>, + }, + InOut { + reg: InlineAsmRegOrRegClass, + _late: bool, + in_value: CValue<'tcx>, + out_place: Option<CPlace<'tcx>>, + }, + Const { + value: String, + }, + Symbol { + symbol: String, + }, +} + pub(crate) fn codegen_inline_asm<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - _span: Span, + span: Span, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperand<'tcx>], options: InlineAsmOptions, @@ -198,6 +222,81 @@ pub(crate) fn codegen_inline_asm<'tcx>( } } + let operands = operands + .into_iter() + .map(|operand| match *operand { + InlineAsmOperand::In { reg, ref value } => { + CInlineAsmOperand::In { reg, value: crate::base::codegen_operand(fx, value) } + } + InlineAsmOperand::Out { reg, late, ref place } => CInlineAsmOperand::Out { + reg, + late, + place: place.map(|place| crate::base::codegen_place(fx, place)), + }, + InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => { + CInlineAsmOperand::InOut { + reg, + _late: late, + in_value: crate::base::codegen_operand(fx, in_value), + out_place: out_place.map(|place| crate::base::codegen_place(fx, place)), + } + } + InlineAsmOperand::Const { ref value } => { + let (const_value, ty) = crate::constant::eval_mir_constant(fx, &*value) + .unwrap_or_else(|| span_bug!(span, "asm const cannot be resolved")); + let value = rustc_codegen_ssa::common::asm_const_to_str( + fx.tcx, + span, + const_value, + fx.layout_of(ty), + ); + CInlineAsmOperand::Const { value } + } + InlineAsmOperand::SymFn { ref value } => { + let literal = fx.monomorphize(value.literal); + if let ty::FnDef(def_id, substs) = *literal.ty().kind() { + let instance = ty::Instance::resolve_for_fn_ptr( + fx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap(); + let symbol = fx.tcx.symbol_name(instance); + + // Pass a wrapper rather than the function itself as the function itself may not + // be exported from the main codegen unit and may thus be unreachable from the + // object file created by an external assembler. + let inline_asm_index = fx.cx.inline_asm_index.get(); + fx.cx.inline_asm_index.set(inline_asm_index + 1); + let wrapper_name = format!( + "__inline_asm_{}_wrapper_n{}", + fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), + inline_asm_index + ); + let sig = + get_function_sig(fx.tcx, fx.target_config.default_call_conv, instance); + create_wrapper_function( + fx.module, + &mut fx.cx.unwind_context, + sig, + &wrapper_name, + symbol.name, + ); + + CInlineAsmOperand::Symbol { symbol: wrapper_name } + } else { + span_bug!(span, "invalid type for asm sym (fn)"); + } + } + InlineAsmOperand::SymStatic { def_id } => { + assert!(fx.tcx.is_static(def_id)); + let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + CInlineAsmOperand::Symbol { symbol: fx.tcx.symbol_name(instance).name.to_owned() } + } + }) + .collect::<Vec<_>>(); + let mut inputs = Vec::new(); let mut outputs = Vec::new(); @@ -206,7 +305,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( arch: fx.tcx.sess.asm_arch.unwrap(), enclosing_def_id: fx.instance.def_id(), template, - operands, + operands: &operands, options, registers: Vec::new(), stack_slots_clobber: Vec::new(), @@ -229,36 +328,22 @@ pub(crate) fn codegen_inline_asm<'tcx>( fx.cx.global_asm.push_str(&generated_asm); for (i, operand) in operands.iter().enumerate() { - match *operand { - InlineAsmOperand::In { reg: _, ref value } => { - inputs.push(( - asm_gen.stack_slots_input[i].unwrap(), - crate::base::codegen_operand(fx, value).load_scalar(fx), - )); - } - InlineAsmOperand::Out { reg: _, late: _, place } => { + match operand { + CInlineAsmOperand::In { reg: _, value } => { + inputs.push((asm_gen.stack_slots_input[i].unwrap(), value.load_scalar(fx))); + } + CInlineAsmOperand::Out { reg: _, late: _, place } => { if let Some(place) = place { - outputs.push(( - asm_gen.stack_slots_output[i].unwrap(), - crate::base::codegen_place(fx, place), - )); + outputs.push((asm_gen.stack_slots_output[i].unwrap(), place.clone())); } } - InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { - inputs.push(( - asm_gen.stack_slots_input[i].unwrap(), - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - )); + CInlineAsmOperand::InOut { reg: _, _late: _, in_value, out_place } => { + inputs.push((asm_gen.stack_slots_input[i].unwrap(), in_value.load_scalar(fx))); if let Some(out_place) = out_place { - outputs.push(( - asm_gen.stack_slots_output[i].unwrap(), - crate::base::codegen_place(fx, out_place), - )); + outputs.push((asm_gen.stack_slots_output[i].unwrap(), out_place.clone())); } } - InlineAsmOperand::Const { value: _ } => todo!(), - InlineAsmOperand::SymFn { value: _ } => todo!(), - InlineAsmOperand::SymStatic { def_id: _ } => todo!(), + CInlineAsmOperand::Const { value: _ } | CInlineAsmOperand::Symbol { symbol: _ } => {} } } @@ -280,7 +365,7 @@ struct InlineAssemblyGenerator<'a, 'tcx> { arch: InlineAsmArch, enclosing_def_id: DefId, template: &'a [InlineAsmTemplatePiece], - operands: &'a [InlineAsmOperand<'tcx>], + operands: &'a [CInlineAsmOperand<'tcx>], options: InlineAsmOptions, registers: Vec<Option<InlineAsmReg>>, stack_slots_clobber: Vec<Option<Size>>, @@ -304,18 +389,20 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Add explicit registers to the allocated set. for (i, operand) in self.operands.iter().enumerate() { match *operand { - InlineAsmOperand::In { reg: InlineAsmRegOrRegClass::Reg(reg), .. } => { + CInlineAsmOperand::In { reg: InlineAsmRegOrRegClass::Reg(reg), .. } => { regs[i] = Some(reg); allocated.entry(reg).or_default().0 = true; } - InlineAsmOperand::Out { - reg: InlineAsmRegOrRegClass::Reg(reg), late: true, .. + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(reg), + late: true, + .. } => { regs[i] = Some(reg); allocated.entry(reg).or_default().1 = true; } - InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(reg), .. } - | InlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::Reg(reg), .. } => { + CInlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(reg), .. } + | CInlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::Reg(reg), .. } => { regs[i] = Some(reg); allocated.insert(reg, (true, true)); } @@ -326,12 +413,12 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate out/inout/inlateout registers first because they are more constrained. for (i, operand) in self.operands.iter().enumerate() { match *operand { - InlineAsmOperand::Out { + CInlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::RegClass(class), late: false, .. } - | InlineAsmOperand::InOut { + | CInlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::RegClass(class), .. } => { let mut alloc_reg = None; @@ -360,7 +447,7 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate in/lateout. for (i, operand) in self.operands.iter().enumerate() { match *operand { - InlineAsmOperand::In { reg: InlineAsmRegOrRegClass::RegClass(class), .. } => { + CInlineAsmOperand::In { reg: InlineAsmRegOrRegClass::RegClass(class), .. } => { let mut alloc_reg = None; for ® in &map[&class] { let mut used = false; @@ -380,7 +467,7 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { regs[i] = Some(reg); allocated.entry(reg).or_default().0 = true; } - InlineAsmOperand::Out { + CInlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::RegClass(class), late: true, .. @@ -455,7 +542,7 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for inout for (i, operand) in self.operands.iter().enumerate() { match *operand { - InlineAsmOperand::InOut { reg, out_place: Some(_), .. } => { + CInlineAsmOperand::InOut { reg, out_place: Some(_), .. } => { let slot = new_slot(reg.reg_class()); slots_input[i] = Some(slot); slots_output[i] = Some(slot); @@ -470,8 +557,8 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for input for (i, operand) in self.operands.iter().enumerate() { match *operand { - InlineAsmOperand::In { reg, .. } - | InlineAsmOperand::InOut { reg, out_place: None, .. } => { + CInlineAsmOperand::In { reg, .. } + | CInlineAsmOperand::InOut { reg, out_place: None, .. } => { slots_input[i] = Some(new_slot(reg.reg_class())); } _ => (), @@ -487,7 +574,7 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for output for (i, operand) in self.operands.iter().enumerate() { match *operand { - InlineAsmOperand::Out { reg, place: Some(_), .. } => { + CInlineAsmOperand::Out { reg, place: Some(_), .. } => { slots_output[i] = Some(new_slot(reg.reg_class())); } _ => (), @@ -549,13 +636,23 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { generated_asm.push_str(s); } InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => { - if self.options.contains(InlineAsmOptions::ATT_SYNTAX) { - generated_asm.push('%'); + match self.operands[*operand_idx] { + CInlineAsmOperand::In { .. } + | CInlineAsmOperand::Out { .. } + | CInlineAsmOperand::InOut { .. } => { + if self.options.contains(InlineAsmOptions::ATT_SYNTAX) { + generated_asm.push('%'); + } + self.registers[*operand_idx] + .unwrap() + .emit(&mut generated_asm, self.arch, *modifier) + .unwrap(); + } + CInlineAsmOperand::Const { ref value } => { + generated_asm.push_str(value); + } + CInlineAsmOperand::Symbol { ref symbol } => generated_asm.push_str(symbol), } - self.registers[*operand_idx] - .unwrap() - .emit(&mut generated_asm, self.arch, *modifier) - .unwrap(); } } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index d561cf139b6..892e7c30e2f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -218,22 +218,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( let intrinsic = fx.tcx.item_name(instance.def_id()); let substs = instance.substs; - let target = if let Some(target) = target { - target - } else { - // Insert non returning intrinsics here - match intrinsic { - sym::abort => { - fx.bcx.ins().trap(TrapCode::User(0)); - } - sym::transmute => { - crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info); - } - _ => unimplemented!("unsupported intrinsic {}", intrinsic), - } - return; - }; - if intrinsic.as_str().starts_with("simd_") { self::simd::codegen_simd_intrinsic_call( fx, @@ -241,11 +225,11 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( substs, args, destination, - target, + target.expect("target for simd intrinsic"), source_info.span, ); } else if codegen_float_intrinsic_call(fx, intrinsic, args, destination) { - let ret_block = fx.get_block(target); + let ret_block = fx.get_block(target.expect("target for float intrinsic")); fx.bcx.ins().jump(ret_block, &[]); } else { codegen_regular_intrinsic_call( @@ -255,7 +239,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( substs, args, destination, - Some(target), + target, source_info, ); } @@ -382,6 +366,10 @@ fn codegen_regular_intrinsic_call<'tcx>( let usize_layout = fx.layout_of(fx.tcx.types.usize); match intrinsic { + sym::abort => { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } sym::likely | sym::unlikely => { intrinsic_args!(fx, args => (a); intrinsic); @@ -579,6 +567,11 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::transmute => { intrinsic_args!(fx, args => (from); intrinsic); + if ret.layout().abi.is_uninhabited() { + crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info); + return; + } + ret.write_cvalue_transmute(fx, from); } sym::write_bytes | sym::volatile_set_memory => { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index fa06d6c3ba7..320eecaee00 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -588,10 +588,13 @@ impl<'tcx> CPlace<'tcx> { return; } CPlaceInner::VarPair(_local, var1, var2) => { - let (ptr, meta) = from.force_stack(fx); - assert!(meta.is_none()); - let (data1, data2) = - CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx); + let (data1, data2) = if from.layout().ty == dst_layout.ty { + CValue(from.0, dst_layout).load_scalar_pair(fx) + } else { + let (ptr, meta) = from.force_stack(fx); + assert!(meta.is_none()); + CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx) + }; let (dst_ty1, dst_ty2) = fx.clif_pair_type(self.layout().ty).unwrap(); transmute_value(fx, var1, data1, dst_ty1); transmute_value(fx, var2, data2, dst_ty2); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 54ac7a46cf2..7a4ec494c8e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -118,7 +118,8 @@ pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attr /// Tell LLVM what instrument function to insert. #[inline] -fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> { + let mut attrs = SmallVec::new(); if cx.sess().opts.unstable_opts.instrument_mcount { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. @@ -127,14 +128,41 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribu // See test/CodeGen/mcount.c in clang. let mcount_name = cx.sess().target.mcount.as_ref(); - Some(llvm::CreateAttrStringValue( + attrs.push(llvm::CreateAttrStringValue( cx.llcx, "instrument-function-entry-inlined", &mcount_name, - )) - } else { - None + )); + } + if let Some(options) = &cx.sess().opts.unstable_opts.instrument_xray { + // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. + // Function prologue and epilogue are instrumented with NOP sleds, + // a runtime library later replaces them with detours into tracing code. + if options.always { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always")); + } + if options.never { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never")); + } + if options.ignore_loops { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops")); + } + // LLVM will not choose the default for us, but rather requires specific + // threshold in absence of "xray-always". Use the same default as Clang. + let threshold = options.instruction_threshold.unwrap_or(200); + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "xray-instruction-threshold", + &threshold.to_string(), + )); + if options.skip_entry { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry")); + } + if options.skip_exit { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit")); + } } + attrs } fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 18e01567ca3..b4a49e1df61 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -54,7 +54,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( trace!( "eval_body_using_ecx: pushing stack frame for global: {}{}", - with_no_trimmed_paths!(ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))), + with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())), cid.promoted.map_or_else(String::new, |p| format!("::promoted[{:?}]", p)) ); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index bdf2978cee2..37967bfdff5 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -624,7 +624,10 @@ fn print_crate_info( println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } FileNames | CrateName => { - let attrs = attrs.as_ref().unwrap(); + let Some(attrs) = attrs.as_ref() else { + // no crate attributes, print out an error and exit + return Compilation::Continue; + }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); if *req == PrintRequest::CrateName { diff --git a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl index fe77cf23e8f..a3b6b5e8138 100644 --- a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl @@ -18,7 +18,7 @@ borrowck_generic_does_not_live_long_enough = `{$kind}` does not live long enough borrowck_move_borrowed = - cannot move out of `{$desc}` beacause it is borrowed + cannot move out of `{$desc}` because it is borrowed borrowck_var_does_not_need_mut = variable does not need to be mutable @@ -87,10 +87,10 @@ borrowck_use_due_to_use_closure = use occurs due to use in closure borrowck_assign_due_to_use_closure = - assign occurs due to use in closure + assignment occurs due to use in closure borrowck_assign_part_due_to_use_closure = - assign to part occurs due to use in closure + assignment to part occurs due to use in closure borrowck_capture_immute = capture is immutable because of use here diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index 5984c201af0..fe553edab42 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -25,6 +25,8 @@ session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C pr session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` +session_instrumentation_not_supported = {$us} instrumentation is not supported for this target + session_sanitizer_not_supported = {$us} sanitizer is not supported for this target session_sanitizers_not_supported = {$us} sanitizers are not supported for this target diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9768526a2f4..5f460b26488 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1796,17 +1796,17 @@ impl EmitterWriter { // telling users to make a change but not clarifying *where*. let loc = sm.lookup_char_pos(parts[0].span.lo()); if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() { - buffer.puts(row_num - 1, 0, "--> ", Style::LineNumber); - buffer.append( - row_num - 1, - &format!( - "{}:{}:{}", - sm.filename_for_diagnostics(&loc.file.name), - sm.doctest_offset_line(&loc.file.name, loc.line), - loc.col.0 + 1, - ), - Style::LineAndColumn, - ); + let arrow = "--> "; + buffer.puts(row_num - 1, 0, arrow, Style::LineNumber); + let filename = sm.filename_for_diagnostics(&loc.file.name); + let offset = sm.doctest_offset_line(&loc.file.name, loc.line); + let message = format!("{}:{}:{}", filename, offset, loc.col.0 + 1); + if row_num == 2 { + let col = usize::max(max_line_num_len + 1, arrow.len()); + buffer.puts(1, col, &message, Style::LineAndColumn); + } else { + buffer.append(row_num - 1, &message, Style::LineAndColumn); + } for _ in 0..max_line_num_len { buffer.prepend(row_num - 1, " ", Style::NoStyle); } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3d5f189e233..f121979be71 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -263,11 +263,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // elision. `resolve_lifetime` should have // reported an error in this case -- but if // not, let's error out. - tcx.sess.delay_span_bug(lifetime.ident.span, "unelided lifetime in signature"); - - // Supply some dummy value. We don't have an - // `re_error`, annoyingly, so use `'static`. - tcx.lifetimes.re_static + tcx.re_error_with_message(lifetime.ident.span, "unelided lifetime in signature") }) } } @@ -481,11 +477,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!(?param, "unelided lifetime in signature"); // This indicates an illegal lifetime in a non-assoc-trait position - tcx.sess.delay_span_bug(self.span, "unelided lifetime in signature"); - - // Supply some dummy value. We don't have an - // `re_error`, annoyingly, so use `'static`. - tcx.lifetimes.re_static + tcx.re_error_with_message(self.span, "unelided lifetime in signature") }) .into(), GenericParamDefKind::Type { has_default, .. } => { @@ -1622,14 +1614,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "the lifetime bound for this object type cannot be deduced \ from context; please supply an explicit bound" ); - if borrowed { + let e = if borrowed { // We will have already emitted an error E0106 complaining about a // missing named lifetime in `&dyn Trait`, so we elide this one. - err.delay_as_bug(); + err.delay_as_bug() } else { - err.emit(); - } - tcx.lifetimes.re_static + err.emit() + }; + tcx.re_error(e) }) } }) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 236e36f28ca..640920638a7 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -786,13 +786,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( } let Some(ty::ReEarlyBound(e)) = map.get(®ion.into()).map(|r| r.expect_region().kind()) else { - tcx - .sess - .delay_span_bug( - return_span, - "expected ReFree to map to ReEarlyBound" - ); - return tcx.lifetimes.re_static; + return tcx.re_error_with_message(return_span, "expected ReFree to map to ReEarlyBound") }; tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: e.def_id, diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 9459c5f54ab..c5c5f63a108 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -170,6 +170,8 @@ fn is_free_region(region: Region<'_>) -> bool { // ignore it. We can't put it on the struct header anyway. ty::ReLateBound(..) => false, + ty::ReError(_) => false, + // These regions don't appear in types from type declarations: ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => { bug!("unexpected region in outlives inference: {:?}", region); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 165782f209a..b0cf0387f87 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -409,6 +409,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // way early-bound regions do, so we skip them here. } + ty::ReError(_) => {} + ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 88fb2653586..e19ef2ff3bf 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut)); } // Now typecheck the blocks. diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 1c70c1b71e7..cc515e6c853 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>( for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // Check the pattern. let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + fcx.check_pat_top(¶m.pat, param_ty, ty_span, None); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2a1265600de..9c7a84ce198 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does the expected pattern type originate from an expression and what is the span? let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) } // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. }; // Type check the pattern. Override if necessary to avoid knock-on errors. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4ce401b52bd..0cf58179ec2 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -951,24 +951,38 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { - // For trait aliases, assume all supertraits are relevant. - let bounds = iter::once(ty::Binder::dummy(trait_ref)); - self.elaborate_bounds(bounds, |this, new_trait_ref, item| { - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); + // For trait aliases, recursively assume all explicitly named traits are relevant + for expansion in traits::expand_trait_aliases( + self.tcx, + iter::once((ty::Binder::dummy(trait_ref), self.span)), + ) { + let bound_trait_ref = expansion.trait_ref(); + for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { + if !self.has_applicable_self(&item) { + self.record_static_candidate(CandidateSource::Trait( + bound_trait_ref.def_id(), + )); + } else { + let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref); - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - import_ids: import_ids.clone(), - kind: TraitCandidate(new_trait_ref), - }, - false, - ); - }); + let (xform_self_ty, xform_ret_ty) = self.xform_self_ty( + &item, + new_trait_ref.self_ty(), + new_trait_ref.substs, + ); + self.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + import_ids: import_ids.clone(), + kind: TraitCandidate(new_trait_ref), + }, + false, + ); + } + } + } } else { debug_assert!(self.tcx.is_trait(trait_def_id)); if self.tcx.trait_is_auto(trait_def_id) { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 46799245222..52236ae56ee 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -46,7 +46,7 @@ struct TopInfo<'tcx> { /// Was the origin of the `span` from a scrutinee expression? /// /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, /// The span giving rise to the `expected` type, if one could be provided. /// /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: @@ -74,7 +74,8 @@ struct TopInfo<'tcx> { impl<'tcx> FnCtxt<'_, 'tcx> { fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { - let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; + let code = + Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() }; self.cause(cause_span, code) } @@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) + let mut diag = + self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?; + if let Some(expr) = ti.origin_expr { + self.suggest_fn_call(&mut diag, expr, expected, |output| { + self.can_eq(self.param_env, output, actual).is_ok() + }); + } + Some(diag) } fn demand_eqtype_pat( @@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option<Span>, - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, ) { let info = TopInfo { expected, origin_expr, span }; self.check_pat(pat, expected, INITIAL_BM, info); @@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("the semantics of slice patterns changed recently; see issue #62254"); } else if self.autoderef(span, expected_ty) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) - && let (Some(span), true) = (ti.span, ti.origin_expr) + && let Some(span) = ti.span + && let Some(_) = ti.origin_expr && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let ty = self.resolve_vars_if_possible(ti.expected); diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7aaa5ce2f42..5d861a78af8 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -31,6 +31,8 @@ impl<'a> DescriptionCtx<'a> { ty::RePlaceholder(_) => return None, + ty::ReError(_) => return None, + // FIXME(#13998) RePlaceholder should probably print like // ReFree rather than dumping Debug output on the user. // diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 87c6dfad5fa..f1b32cd941e 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -203,12 +203,10 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { // rust-lang/rust#57464: `impl Trait` can leak local // scopes (in manner violating typeck). Therefore, use // `delay_span_bug` to allow type error over an ICE. - ty::tls::with(|tcx| { - tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!("unexpected region in query response: `{:?}`", r), - ); - }); + canonicalizer.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("unexpected region in query response: `{:?}`", r), + ); r } } @@ -371,6 +369,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { ty::ReStatic | ty::ReEarlyBound(..) + | ty::ReError(_) | ty::ReFree(_) | ty::RePlaceholder(..) | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index a567b6acdbe..1360044fe75 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -705,6 +705,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { return Ok(r); } + ty::ReError(_) => { + return Ok(r); + } + ty::RePlaceholder(..) | ty::ReVar(..) | ty::ReStatic @@ -861,7 +865,7 @@ impl<'tcx> FallibleTypeFolder<'tcx> for ConstInferUnifier<'_, 'tcx> { match *r { // Never make variables for regions bound within the type itself, // nor for erased regions. - ty::ReLateBound(..) | ty::ReErased => { + ty::ReLateBound(..) | ty::ReErased | ty::ReError(_) => { return Ok(r); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 86f3174b7b2..88a0d6def5e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -134,6 +134,8 @@ pub(super) fn note_and_explain_region<'tcx>( ty::RePlaceholder(_) => return, + ty::ReError(_) => return, + // FIXME(#13998) RePlaceholder should probably print like // ReFree rather than dumping Debug output on the user. // @@ -313,6 +315,9 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ) } } + ty::ReError(_) => { + err.delay_as_bug(); + } _ => { // Ugh. This is a painful case: the hidden region is not one // that we can easily summarize or explain. This can happen @@ -2546,7 +2551,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note_expected_found(&"", sup_expected, &"", sup_found); - err.emit(); + if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug(); + } else { + err.emit(); + } return; } @@ -2562,7 +2571,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); self.note_region_origin(&mut err, &sub_origin); - err.emit(); + if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug(); + } else { + err.emit(); + } } /// Determine whether an error associated with the given span and definition diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index b8c843a8a5a..c092efbb557 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -158,8 +158,12 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte if infcx.probe_ty_var(ty_vid).is_ok() { warn!("resolved ty var in error message"); } - if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = - infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind + + let mut infcx_inner = infcx.inner.borrow_mut(); + let ty_vars = infcx_inner.type_variables(); + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind + && !var_origin.span.from_expansion() { Some(name) } else { @@ -254,7 +258,7 @@ impl<'tcx> InferCtxt<'tcx> { if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind { - if name != kw::SelfUpper { + if name != kw::SelfUpper && !var_origin.span.from_expansion() { return InferenceDiagnosticsData { name: name.to_string(), span: Some(var_origin.span), @@ -780,7 +784,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // The sources are listed in order of preference here. let tcx = self.infcx.tcx; let ctx = CostCtxt { tcx }; - let base_cost = match source.kind { + match source.kind { InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty), InferSourceKind::GenericArg { def_id, generic_args, .. } => { @@ -797,17 +801,17 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => { 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } } - }; - - let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 }; - - base_cost + suggestion_may_apply + } } /// Uses `fn source_cost` to determine whether this inference source is preferable to /// previous sources. We generally prefer earlier sources. #[instrument(level = "debug", skip(self))] fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) { + if new_source.from_expansion() { + return; + } + let cost = self.source_cost(&new_source) + self.attempt; debug!(?cost); self.attempt += 1; @@ -819,6 +823,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // `let x: _ = iter.collect();`, as this is a very common case. *def_id = Some(did); } + if cost < self.infer_source_cost { self.infer_source_cost = cost; self.infer_source = Some(new_source); diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index b18cbd404d4..bdd09a995dc 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -78,7 +78,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sub: Region<'tcx>, sup: Region<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - match origin { + let mut err = match origin { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); let mut err = self.report_and_explain_type_error(trace, terr); @@ -299,7 +299,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err } + }; + if sub.is_error() || sup.is_error() { + err.delay_as_bug(); } + err } pub fn suggest_copy_trait_method_bounds( diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 2355234637c..1c76950cc6c 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -126,6 +126,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::ReFree(_) | ty::ReVar(_) | ty::RePlaceholder(..) + | ty::ReError(_) | ty::ReErased => { // replace all free regions with 'erased self.tcx().lifetimes.re_erased diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index ce8aec8044b..4a2210bdb68 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -17,7 +17,7 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::PlaceholderRegion; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{ReEarlyBound, ReErased, ReFree, ReStatic}; +use rustc_middle::ty::{ReEarlyBound, ReErased, ReError, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; @@ -216,6 +216,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { Ok(self.tcx().lifetimes.re_static) } + ReError(_) => Ok(a_region), + ReEarlyBound(_) | ReFree(_) => { // All empty regions are less than early-bound, free, // and scope regions. @@ -436,7 +438,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Value(a), VarValue::Empty(_)) => { match *a { - ReLateBound(..) | ReErased => { + ReLateBound(..) | ReErased | ReError(_) => { bug!("cannot relate region: {:?}", a); } @@ -465,7 +467,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Empty(a_ui), VarValue::Value(b)) => { match *b { - ReLateBound(..) | ReErased => { + ReLateBound(..) | ReErased | ReError(_) => { bug!("cannot relate region: {:?}", b); } @@ -546,6 +548,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } + (ReError(_), _) => a, + + (_, ReError(_)) => b, + (ReStatic, _) | (_, ReStatic) => { // nothing lives longer than `'static` self.tcx().lifetimes.re_static @@ -1040,7 +1046,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { ty::ReVar(rid) => match self.values[rid] { VarValue::Empty(_) => r, VarValue::Value(r) => r, - VarValue::ErrorValue => tcx.lifetimes.re_static, + VarValue::ErrorValue => tcx.re_error_misc(), }, _ => r, }; diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 94de9bc2d02..bae246418b0 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -207,6 +207,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// In some cases, such as when `erased_ty` represents a `ty::Param`, however, /// the result is precise. + #[instrument(level = "debug", skip(self))] fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0428481b7ff..cb24375c7a3 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -696,9 +696,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex { match *region { - ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => { - ty::UniverseIndex::ROOT - } + ty::ReStatic + | ty::ReErased + | ty::ReFree(..) + | ty::ReEarlyBound(..) + | ty::ReError(_) => ty::UniverseIndex::ROOT, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReVar(vid) => self.var_universe(vid), ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index ee0552d77ce..bc6d7c20997 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -38,7 +38,7 @@ fn track_diagnostic(diagnostic: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnost // Diagnostics are tracked, we can ignore the dependency. let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; - return tls::enter_context(&icx, move |_| (*f)(diagnostic)); + return tls::enter_context(&icx, move || (*f)(diagnostic)); } // In any other case, invoke diagnostics anyway. diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2a373ebc132..33ebbb411ce 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -738,30 +738,16 @@ pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock<ExternProviders> = LazyLock: extern_providers }); -pub struct QueryContext<'tcx> { - gcx: &'tcx GlobalCtxt<'tcx>, -} - -impl<'tcx> QueryContext<'tcx> { - pub fn enter<F, R>(&mut self, f: F) -> R - where - F: FnOnce(TyCtxt<'tcx>) -> R, - { - let icx = ty::tls::ImplicitCtxt::new(self.gcx); - ty::tls::enter_context(&icx, |_| f(icx.tcx)) - } -} - pub fn create_global_ctxt<'tcx>( compiler: &'tcx Compiler, lint_store: Lrc<LintStore>, dep_graph: DepGraph, untracked: Untracked, queries: &'tcx OnceCell<TcxQueries<'tcx>>, - global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>, + gcx_cell: &'tcx OnceCell<GlobalCtxt<'tcx>>, arena: &'tcx WorkerLocal<Arena<'tcx>>, hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, -) -> QueryContext<'tcx> { +) -> &'tcx GlobalCtxt<'tcx> { // We're constructing the HIR here; we don't care what we will // read, since we haven't even constructed the *input* to // incr. comp. yet. @@ -785,8 +771,8 @@ pub fn create_global_ctxt<'tcx>( TcxQueries::new(local_providers, extern_providers, query_result_on_disk_cache) }); - let gcx = sess.time("setup_global_ctxt", || { - global_ctxt.get_or_init(move || { + sess.time("setup_global_ctxt", || { + gcx_cell.get_or_init(move || { TyCtxt::create_global_ctxt( sess, lint_store, @@ -799,9 +785,7 @@ pub fn create_global_ctxt<'tcx>( rustc_query_impl::query_callbacks(arena), ) }) - }); - - QueryContext { gcx } + }) } /// Runs the resolution, type-checking, region checking and other diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 4b0180741c1..6512695873e 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,6 +1,6 @@ use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation}; use crate::interface::{Compiler, Result}; -use crate::passes::{self, BoxedResolver, QueryContext}; +use crate::passes::{self, BoxedResolver}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; @@ -64,7 +64,7 @@ impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> { } } -impl<'a, 'tcx> QueryResult<'a, QueryContext<'tcx>> { +impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> { pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T { (*self.0).get_mut().enter(f) } @@ -78,7 +78,7 @@ impl<T> Default for Query<T> { pub struct Queries<'tcx> { compiler: &'tcx Compiler, - gcx: OnceCell<GlobalCtxt<'tcx>>, + gcx_cell: OnceCell<GlobalCtxt<'tcx>>, queries: OnceCell<TcxQueries<'tcx>>, arena: WorkerLocal<Arena<'tcx>>, @@ -90,7 +90,8 @@ pub struct Queries<'tcx> { register_plugins: Query<(ast::Crate, Lrc<LintStore>)>, expansion: Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>, dep_graph: Query<DepGraph>, - global_ctxt: Query<QueryContext<'tcx>>, + // This just points to what's in `gcx_cell`. + gcx: Query<&'tcx GlobalCtxt<'tcx>>, ongoing_codegen: Query<Box<dyn Any>>, } @@ -98,7 +99,7 @@ impl<'tcx> Queries<'tcx> { pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> { Queries { compiler, - gcx: OnceCell::new(), + gcx_cell: OnceCell::new(), queries: OnceCell::new(), arena: WorkerLocal::new(|_| Arena::default()), hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()), @@ -108,7 +109,7 @@ impl<'tcx> Queries<'tcx> { register_plugins: Default::default(), expansion: Default::default(), dep_graph: Default::default(), - global_ctxt: Default::default(), + gcx: Default::default(), ongoing_codegen: Default::default(), } } @@ -207,8 +208,8 @@ impl<'tcx> Queries<'tcx> { }) } - pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, QueryContext<'tcx>>> { - self.global_ctxt.compute(|| { + pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> { + self.gcx.compute(|| { let crate_name = *self.crate_name()?.borrow(); let (krate, resolver, lint_store) = self.expansion()?.steal(); @@ -218,18 +219,18 @@ impl<'tcx> Queries<'tcx> { ast_lowering: untracked_resolver_for_lowering, } = BoxedResolver::to_resolver_outputs(resolver); - let mut qcx = passes::create_global_ctxt( + let gcx = passes::create_global_ctxt( self.compiler, lint_store, self.dep_graph()?.steal(), untracked, &self.queries, - &self.gcx, + &self.gcx_cell, &self.arena, &self.hir_arena, ); - qcx.enter(|tcx| { + gcx.enter(|tcx| { let feed = tcx.feed_unit_query(); feed.resolver_for_lowering( tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, krate))), @@ -239,7 +240,7 @@ impl<'tcx> Queries<'tcx> { let feed = tcx.feed_local_crate(); feed.crate_name(crate_name); }); - Ok(qcx) + Ok(gcx) }) } @@ -387,7 +388,7 @@ impl Compiler { // NOTE: intentionally does not compute the global context if it hasn't been built yet, // since that likely means there was a parse error. - if let Some(Ok(gcx)) = &mut *queries.global_ctxt.result.borrow_mut() { + if let Some(Ok(gcx)) = &mut *queries.gcx.result.borrow_mut() { let gcx = gcx.get_mut(); // We assume that no queries are run past here. If there are new queries // after this point, they'll show up as "<unknown>" in self-profiling data. diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5165ee424e3..0d3499ca9a0 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -5,6 +5,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::rustc_optgroups; use rustc_session::config::Input; +use rustc_session::config::InstrumentXRay; use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ @@ -755,6 +756,7 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir_threshold, Some(123)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); + tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_only, true); tracked!(llvm_plugins, vec![String::from("plugin_name")]); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 019fdc30dce..de26fd61e4d 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -14,7 +14,7 @@ //! //! ``` //! fn main() { -//! rustc_log::init_rustc_env_logger().unwrap(); +//! rustc_log::init_env_logger("LOG").unwrap(); //! //! let edition = rustc_span::edition::Edition::Edition2021; //! rustc_span::create_session_globals_then(edition, || { @@ -23,9 +23,9 @@ //! } //! ``` //! -//! Now `RUSTC_LOG=debug cargo run` will run your minimal main.rs and show +//! Now `LOG=debug cargo run` will run your minimal main.rs and show //! rustc's debug logging. In a workflow like this, one might also add -//! `std::env::set_var("RUSTC_LOG", "debug")` to the top of main so that `cargo +//! `std::env::set_var("LOG", "debug")` to the top of main so that `cargo //! run` by itself is sufficient to get logs. //! //! The reason rustc_log is a tiny separate crate, as opposed to exposing the @@ -53,12 +53,6 @@ use tracing_subscriber::fmt::{ }; use tracing_subscriber::layer::SubscriberExt; -pub fn init_rustc_env_logger() -> Result<(), Error> { - init_env_logger("RUSTC_LOG") -} - -/// In contrast to `init_rustc_env_logger` this allows you to choose an env var -/// other than `RUSTC_LOG`. pub fn init_env_logger(env: &str) -> Result<(), Error> { let filter = match env::var(env) { Ok(env) => EnvFilter::new(env), diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 2e62bebc852..2e82efba192 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -55,7 +55,7 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { ty::tls::with_context(|icx| { let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; - ty::tls::enter_context(&icx, |_| op()) + ty::tls::enter_context(&icx, op) }) } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 95148de2518..56df1a66f9d 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -34,6 +34,7 @@ #![feature(get_mut_unchecked)] #![feature(if_let_guard)] #![feature(iter_from_generator)] +#![feature(local_key_cell_methods)] #![feature(negative_impls)] #![feature(never_type)] #![feature(extern_types)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9205a8a0ffe..01f18e8158a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -468,6 +468,18 @@ pub struct GlobalCtxt<'tcx> { pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>, } +impl<'tcx> GlobalCtxt<'tcx> { + /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of + /// `f`. + pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R + where + F: FnOnce(TyCtxt<'tcx>) -> R, + { + let icx = tls::ImplicitCtxt::new(self); + tls::enter_context(&icx, || f(icx.tcx)) + } +} + impl<'tcx> TyCtxt<'tcx> { /// Expects a body and returns its codegen attributes. /// @@ -649,6 +661,30 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_ty(Error(reported)) } + /// Constructs a `RegionKind::ReError` lifetime. + #[track_caller] + pub fn re_error(self, reported: ErrorGuaranteed) -> Region<'tcx> { + self.mk_region(ty::ReError(reported)) + } + + /// Constructs a `RegionKind::ReError` lifetime and registers a `delay_span_bug` to ensure it + /// gets used. + #[track_caller] + pub fn re_error_misc(self) -> Region<'tcx> { + self.re_error_with_message( + DUMMY_SP, + "RegionKind::ReError constructed but no error reported", + ) + } + + /// Constructs a `RegionKind::ReError` lifetime and registers a `delay_span_bug` with the given + /// `msg` to ensure it gets used. + #[track_caller] + pub fn re_error_with_message<S: Into<MultiSpan>>(self, span: S, msg: &str) -> Region<'tcx> { + let reported = self.sess.delay_span_bug(span, msg); + self.re_error(reported) + } + /// Like [TyCtxt::ty_error] but for constants, with current `ErrorGuaranteed` #[track_caller] pub fn const_error_with_guaranteed( diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 71b025dc1be..5426ac8d739 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -89,9 +89,8 @@ mod tlv { /// This is used to set the pointer to the new `ImplicitCtxt`. #[inline] pub(super) fn with_tlv<F: FnOnce() -> R, R>(value: *const (), f: F) -> R { - let old = get_tlv(); - let _reset = rustc_data_structures::OnDrop(move || TLV.with(|tlv| tlv.set(old))); - TLV.with(|tlv| tlv.set(value)); + let old = TLV.replace(value); + let _reset = rustc_data_structures::OnDrop(move || TLV.set(old)); f() } } @@ -110,9 +109,9 @@ unsafe fn downcast<'a, 'tcx>(context: *const ()) -> &'a ImplicitCtxt<'a, 'tcx> { #[inline] pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R where - F: FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R, + F: FnOnce() -> R, { - tlv::with_tlv(erase(context), || f(&context)) + tlv::with_tlv(erase(context), f) } /// Allows access to the current `ImplicitCtxt` in a closure if one is available. diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 801ca600445..ea95a38f272 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -100,7 +100,7 @@ impl GenericParamDef { preceding_substs: &[ty::GenericArg<'tcx>], ) -> ty::GenericArg<'tcx> { match &self.kind { - ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), + ty::GenericParamDefKind::Lifetime => tcx.re_error_misc().into(), ty::GenericParamDefKind::Type { .. } => tcx.ty_error().into(), ty::GenericParamDefKind::Const { .. } => { tcx.const_error(tcx.bound_type_of(self.def_id).subst(tcx, preceding_substs)).into() diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index 7ff58f02623..a5ebdbc8792 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -109,6 +109,8 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { // them. ty::ReErased => return r, + ty::ReError(_) => return r, + // The regions that we expect from borrow checking. ty::ReEarlyBound(_) | ty::ReFree(_) => {} @@ -125,20 +127,21 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { Some(u) => panic!("region mapped to unexpected kind: {:?}", u), None if self.do_not_error => self.tcx.lifetimes.re_static, None => { - self.tcx + let e = self + .tcx .sess .struct_span_err(self.span, "non-defining opaque type use in defining scope") .span_label( self.span, format!( "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", + parameter list of the `impl Trait` type alias", r ), ) .emit(); - self.tcx().lifetimes.re_static + self.tcx().re_error(e) } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index bbb4fd999bc..a8b23e64e82 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2114,7 +2114,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { ty::ReVar(_) if identify_regions => true, - ty::ReVar(_) | ty::ReErased => false, + ty::ReVar(_) | ty::ReErased | ty::ReError(_) => false, ty::ReStatic => true, } @@ -2194,6 +2194,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { } ty::ReVar(_) => {} ty::ReErased => {} + ty::ReError(_) => {} ty::ReStatic => { p!("'static"); return Ok(self); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 98d6b683563..b3b2a4fd0e5 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1623,10 +1623,16 @@ impl<'tcx> Region<'tcx> { ty::ReVar(..) => false, ty::RePlaceholder(placeholder) => placeholder.name.is_named(), ty::ReErased => false, + ty::ReError(_) => false, } } #[inline] + pub fn is_error(self) -> bool { + matches!(*self, ty::ReError(_)) + } + + #[inline] pub fn is_static(self) -> bool { matches!(*self, ty::ReStatic) } @@ -1686,6 +1692,7 @@ impl<'tcx> Region<'tcx> { ty::ReErased => { flags = flags | TypeFlags::HAS_RE_ERASED; } + ty::ReError(_) => {} } debug!("type_flags({:?}) = {:?}", self, flags); diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs new file mode 100644 index 00000000000..89f8de23583 --- /dev/null +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -0,0 +1,298 @@ +use crate::rustc_middle::ty::util::IntTypeExt; +use crate::MirPass; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::interpret::AllocId; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, AdtDef, Const, ParamEnv, Ty, TyCtxt}; +use rustc_session::Session; +use rustc_target::abi::{HasDataLayout, Size, TagEncoding, Variants}; + +/// A pass that seeks to optimize unnecessary moves of large enum types, if there is a large +/// enough discrepancy between them. +/// +/// i.e. If there is are two variants: +/// ``` +/// enum Example { +/// Small, +/// Large([u32; 1024]), +/// } +/// ``` +/// Instead of emitting moves of the large variant, +/// Perform a memcpy instead. +/// Based off of [this HackMD](https://hackmd.io/@ft4bxUsFT5CEUBmRKYHr7w/rJM8BBPzD). +/// +/// In summary, what this does is at runtime determine which enum variant is active, +/// and instead of copying all the bytes of the largest possible variant, +/// copy only the bytes for the currently active variant. +pub struct EnumSizeOpt { + pub(crate) discrepancy: u64, +} + +impl<'tcx> MirPass<'tcx> for EnumSizeOpt { + fn is_enabled(&self, sess: &Session) -> bool { + sess.opts.unstable_opts.unsound_mir_opts || sess.mir_opt_level() >= 3 + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + // NOTE: This pass may produce different MIR based on the alignment of the target + // platform, but it will still be valid. + self.optim(tcx, body); + } +} + +impl EnumSizeOpt { + fn candidate<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + alloc_cache: &mut FxHashMap<Ty<'tcx>, AllocId>, + ) -> Option<(AdtDef<'tcx>, usize, AllocId)> { + let adt_def = match ty.kind() { + ty::Adt(adt_def, _substs) if adt_def.is_enum() => adt_def, + _ => return None, + }; + let layout = tcx.layout_of(param_env.and(ty)).ok()?; + let variants = match &layout.variants { + Variants::Single { .. } => return None, + Variants::Multiple { tag_encoding, .. } + if matches!(tag_encoding, TagEncoding::Niche { .. }) => + { + return None; + } + Variants::Multiple { variants, .. } if variants.len() <= 1 => return None, + Variants::Multiple { variants, .. } => variants, + }; + let min = variants.iter().map(|v| v.size).min().unwrap(); + let max = variants.iter().map(|v| v.size).max().unwrap(); + if max.bytes() - min.bytes() < self.discrepancy { + return None; + } + + let num_discrs = adt_def.discriminants(tcx).count(); + if variants.iter_enumerated().any(|(var_idx, _)| { + let discr_for_var = adt_def.discriminant_for_variant(tcx, var_idx).val; + (discr_for_var > usize::MAX as u128) || (discr_for_var as usize >= num_discrs) + }) { + return None; + } + if let Some(alloc_id) = alloc_cache.get(&ty) { + return Some((*adt_def, num_discrs, *alloc_id)); + } + + let data_layout = tcx.data_layout(); + let ptr_sized_int = data_layout.ptr_sized_integer(); + let target_bytes = ptr_sized_int.size().bytes() as usize; + let mut data = vec![0; target_bytes * num_discrs]; + macro_rules! encode_store { + ($curr_idx: expr, $endian: expr, $bytes: expr) => { + let bytes = match $endian { + rustc_target::abi::Endian::Little => $bytes.to_le_bytes(), + rustc_target::abi::Endian::Big => $bytes.to_be_bytes(), + }; + for (i, b) in bytes.into_iter().enumerate() { + data[$curr_idx + i] = b; + } + }; + } + + for (var_idx, layout) in variants.iter_enumerated() { + let curr_idx = + target_bytes * adt_def.discriminant_for_variant(tcx, var_idx).val as usize; + let sz = layout.size; + match ptr_sized_int { + rustc_target::abi::Integer::I32 => { + encode_store!(curr_idx, data_layout.endian, sz.bytes() as u32); + } + rustc_target::abi::Integer::I64 => { + encode_store!(curr_idx, data_layout.endian, sz.bytes()); + } + _ => unreachable!(), + }; + } + let alloc = interpret::Allocation::from_bytes( + data, + tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi, + Mutability::Not, + ); + let alloc = tcx.create_memory_alloc(tcx.intern_const_alloc(alloc)); + Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc))) + } + fn optim<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut alloc_cache = FxHashMap::default(); + let body_did = body.source.def_id(); + let param_env = tcx.param_env(body_did); + + let blocks = body.basic_blocks.as_mut(); + let local_decls = &mut body.local_decls; + + for bb in blocks { + bb.expand_statements(|st| { + if let StatementKind::Assign(box ( + lhs, + Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + )) = &st.kind + { + let ty = lhs.ty(local_decls, tcx).ty; + + let source_info = st.source_info; + let span = source_info.span; + + let (adt_def, num_variants, alloc_id) = + self.candidate(tcx, param_env, ty, &mut alloc_cache)?; + let alloc = tcx.global_alloc(alloc_id).unwrap_memory(); + + let tmp_ty = tcx.mk_ty(ty::Array( + tcx.types.usize, + Const::from_usize(tcx, num_variants as u64), + )); + + let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span)); + let store_live = Statement { + source_info, + kind: StatementKind::StorageLive(size_array_local), + }; + + let place = Place::from(size_array_local); + let constant_vals = Constant { + span, + user_ty: None, + literal: ConstantKind::Val( + interpret::ConstValue::ByRef { alloc, offset: Size::ZERO }, + tmp_ty, + ), + }; + let rval = Rvalue::Use(Operand::Constant(box (constant_vals))); + + let const_assign = + Statement { source_info, kind: StatementKind::Assign(box (place, rval)) }; + + let discr_place = Place::from( + local_decls + .push(LocalDecl::new(adt_def.repr().discr_type().to_ty(tcx), span)), + ); + + let store_discr = Statement { + source_info, + kind: StatementKind::Assign(box (discr_place, Rvalue::Discriminant(*rhs))), + }; + + let discr_cast_place = + Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); + + let cast_discr = Statement { + source_info, + kind: StatementKind::Assign(box ( + discr_cast_place, + Rvalue::Cast( + CastKind::IntToInt, + Operand::Copy(discr_place), + tcx.types.usize, + ), + )), + }; + + let size_place = + Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); + + let store_size = Statement { + source_info, + kind: StatementKind::Assign(box ( + size_place, + Rvalue::Use(Operand::Copy(Place { + local: size_array_local, + projection: tcx.intern_place_elems(&[PlaceElem::Index( + discr_cast_place.local, + )]), + })), + )), + }; + + let dst = + Place::from(local_decls.push(LocalDecl::new(tcx.mk_mut_ptr(ty), span))); + + let dst_ptr = Statement { + source_info, + kind: StatementKind::Assign(box ( + dst, + Rvalue::AddressOf(Mutability::Mut, *lhs), + )), + }; + + let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8); + let dst_cast_place = + Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span))); + + let dst_cast = Statement { + source_info, + kind: StatementKind::Assign(box ( + dst_cast_place, + Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(dst), dst_cast_ty), + )), + }; + + let src = + Place::from(local_decls.push(LocalDecl::new(tcx.mk_imm_ptr(ty), span))); + + let src_ptr = Statement { + source_info, + kind: StatementKind::Assign(box ( + src, + Rvalue::AddressOf(Mutability::Not, *rhs), + )), + }; + + let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8); + let src_cast_place = + Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span))); + + let src_cast = Statement { + source_info, + kind: StatementKind::Assign(box ( + src_cast_place, + Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(src), src_cast_ty), + )), + }; + + let deinit_old = + Statement { source_info, kind: StatementKind::Deinit(box dst) }; + + let copy_bytes = Statement { + source_info, + kind: StatementKind::Intrinsic( + box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src: Operand::Copy(src_cast_place), + dst: Operand::Copy(dst_cast_place), + count: Operand::Copy(size_place), + }), + ), + }; + + let store_dead = Statement { + source_info, + kind: StatementKind::StorageDead(size_array_local), + }; + let iter = [ + store_live, + const_assign, + store_discr, + cast_discr, + store_size, + dst_ptr, + dst_cast, + src_ptr, + src_cast, + deinit_old, + copy_bytes, + store_dead, + ] + .into_iter(); + + st.make_nop(); + Some(iter) + } else { + None + } + }); + } + } +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9070a7368b1..45cd4024c9f 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,6 +1,7 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(drain_filter)] +#![feature(box_syntax)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(min_specialization)] @@ -73,6 +74,7 @@ mod function_item_references; mod generator; mod inline; mod instcombine; +mod large_enums; mod lower_intrinsics; mod lower_slice_len; mod match_branches; @@ -583,6 +585,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &simplify::SimplifyLocals::new("final"), &multiple_return_terminators::MultipleReturnTerminators, &deduplicate_blocks::DeduplicateBlocks, + &large_enums::EnumSizeOpt { discrepancy: 128 }, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, // Dump the end result for testing and debugging purposes. diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4dea03c1ef6..49309db564e 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -124,7 +124,7 @@ impl QueryContext for QueryCtxt<'_> { }; // Use the `ImplicitCtxt` while we execute the query. - tls::enter_context(&new_icx, |_| { + tls::enter_context(&new_icx, || { rustc_data_structures::stack::ensure_sufficient_stack(compute) }) }) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3ca10ac50ba..b28aee40b16 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1725,7 +1725,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { !segment.has_generic_args, elided_lifetime_span, ); - err.note("assuming a `'static` lifetime..."); err.emit(); should_lint = false; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cee0a7f3c20..a9dbb3ca131 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2244,19 +2244,23 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } None => { debug!(?param.ident, ?param.ident.span); - let deletion_span = deletion_span(); - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_LIFETIMES, - param.id, - param.ident.span, - &format!("lifetime parameter `{}` never used", param.ident), - lint::BuiltinLintDiagnostics::SingleUseLifetime { - param_span: param.ident.span, - use_span: None, - deletion_span, - }, - ); + // the give lifetime originates from expanded code so we won't be able to remove it #104432 + let lifetime_only_in_expanded_code = + deletion_span.map(|sp| sp.in_derive_expansion()).unwrap_or(true); + if !lifetime_only_in_expanded_code { + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` never used", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: None, + deletion_span, + }, + ); + } } } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 973d860118e..7d2fdf94baa 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -174,6 +174,25 @@ pub enum InstrumentCoverage { Off, } +/// Settings for `-Z instrument-xray` flag. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub struct InstrumentXRay { + /// `-Z instrument-xray=always`, force instrumentation + pub always: bool, + /// `-Z instrument-xray=never`, disable instrumentation + pub never: bool, + /// `-Z instrument-xray=ignore-loops`, ignore presence of loops, + /// instrument functions based only on instruction count + pub ignore_loops: bool, + /// `-Z instrument-xray=instruction-threshold=N`, explicitly set instruction threshold + /// for instrumentation, or `None` to use compiler's default + pub instruction_threshold: Option<usize>, + /// `-Z instrument-xray=skip-entry`, do not instrument function entry + pub skip_entry: bool, + /// `-Z instrument-xray=skip-exit`, do not instrument function exit + pub skip_exit: bool, +} + #[derive(Clone, PartialEq, Hash, Debug)] pub enum LinkerPluginLto { LinkerPlugin(PathBuf), @@ -2805,9 +2824,9 @@ impl PpMode { pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, - InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, - OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind, - SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, + InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, + OomStrategy, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, + SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, }; use crate::lint; use crate::options::WasiExecModel; @@ -2876,6 +2895,7 @@ pub(crate) mod dep_tracking { CodeModel, TlsModel, InstrumentCoverage, + InstrumentXRay, CrateType, MergeFunctions, PanicStrategy, diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 8e8fba5e236..c851145440b 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -72,6 +72,12 @@ pub struct ProfileSampleUseFileDoesNotExist<'a> { pub struct TargetRequiresUnwindTables; #[derive(Diagnostic)] +#[diag(session_instrumentation_not_supported)] +pub struct InstrumentationNotSupported { + pub us: String, +} + +#[derive(Diagnostic)] #[diag(session_sanitizer_not_supported)] pub struct SanitizerNotSupported { pub us: String, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 61cb81aec3d..03f45d0fc32 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -349,7 +349,7 @@ fn build_options<O: Default>( #[allow(non_upper_case_globals)] mod desc { pub const parse_no_flag: &str = "no value"; - pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `n`, `no`, or `off`"; + pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false`"; pub const parse_opt_bool: &str = parse_bool; pub const parse_string: &str = "a string"; pub const parse_opt_string: &str = parse_string; @@ -380,6 +380,7 @@ mod desc { pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`"; + pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; pub const parse_trait_solver: &str = @@ -432,11 +433,11 @@ mod parse { /// Use this for any boolean option that has a static default. pub(crate) fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { match v { - Some("y") | Some("yes") | Some("on") | None => { + Some("y") | Some("yes") | Some("on") | Some("true") | None => { *slot = true; true } - Some("n") | Some("no") | Some("off") => { + Some("n") | Some("no") | Some("off") | Some("false") => { *slot = false; true } @@ -449,11 +450,11 @@ mod parse { /// other factors, such as other options, or target options.) pub(crate) fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool { match v { - Some("y") | Some("yes") | Some("on") | None => { + Some("y") | Some("yes") | Some("on") | Some("true") | None => { *slot = Some(true); true } - Some("n") | Some("no") | Some("off") => { + Some("n") | Some("no") | Some("off") | Some("false") => { *slot = Some(false); true } @@ -869,6 +870,68 @@ mod parse { true } + pub(crate) fn parse_instrument_xray( + slot: &mut Option<InstrumentXRay>, + v: Option<&str>, + ) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { Some(InstrumentXRay::default()) } else { None }; + return true; + } + } + + let mut options = slot.get_or_insert_default(); + let mut seen_always = false; + let mut seen_never = false; + let mut seen_ignore_loops = false; + let mut seen_instruction_threshold = false; + let mut seen_skip_entry = false; + let mut seen_skip_exit = false; + for option in v.into_iter().map(|v| v.split(',')).flatten() { + match option { + "always" if !seen_always && !seen_never => { + options.always = true; + options.never = false; + seen_always = true; + } + "never" if !seen_never && !seen_always => { + options.never = true; + options.always = false; + seen_never = true; + } + "ignore-loops" if !seen_ignore_loops => { + options.ignore_loops = true; + seen_ignore_loops = true; + } + option + if option.starts_with("instruction-threshold") + && !seen_instruction_threshold => + { + let Some(("instruction-threshold", n)) = option.split_once('=') else { + return false; + }; + match n.parse() { + Ok(n) => options.instruction_threshold = Some(n), + Err(_) => return false, + } + seen_instruction_threshold = true; + } + "skip-entry" if !seen_skip_entry => { + options.skip_entry = true; + seen_skip_entry = true; + } + "skip-exit" if !seen_skip_exit => { + options.skip_exit = true; + seen_skip_exit = true; + } + _ => return false, + } + } + true + } + pub(crate) fn parse_treat_err_as_bug(slot: &mut Option<NonZeroUsize>, v: Option<&str>) -> bool { match v { Some(s) => { @@ -1397,6 +1460,16 @@ options! { `=off` (default)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), + instrument_xray: Option<InstrumentXRay> = (None, parse_instrument_xray, [TRACKED], + "insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas."), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 8a0176f6391..fe6ac80fde6 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1589,6 +1589,10 @@ fn validate_commandline_args_with_session_available(sess: &Session) { { sess.emit_err(errors::SplitDebugInfoUnstablePlatform { debuginfo: sess.split_debuginfo() }); } + + if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { + sess.emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); + } } /// Holds data on the current incremental compilation session, if there is one. diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index c9b4ab0a38d..710f3826403 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -299,6 +299,7 @@ fn encode_region<'tcx>( RegionKind::ReEarlyBound(..) | RegionKind::ReFree(..) | RegionKind::ReStatic + | RegionKind::ReError(_) | RegionKind::ReVar(..) | RegionKind::RePlaceholder(..) => { bug!("encode_region: unexpected `{:?}`", region.kind()); diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index c85f7f62a42..daa946ccd51 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { | SanitizerSet::MEMTAG | SanitizerSet::SHADOWCALLSTACK | SanitizerSet::ADDRESS, + supports_xray: true, ..super::android_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs index 3006044d54a..36d54f1d7cc 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { | SanitizerSet::MEMTAG | SanitizerSet::THREAD | SanitizerSet::HWADDRESS, + supports_xray: true, ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs index 002d0dac2a6..9c299fed6be 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs @@ -3,6 +3,7 @@ use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.max_atomic_width = Some(128); + base.supports_xray = true; Target { llvm_target: "aarch64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a094c2c5452..bc1920e3424 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1718,6 +1718,9 @@ pub struct TargetOptions { /// The ABI of entry function. /// Default value is `Conv::C`, i.e. C call convention pub entry_abi: Conv, + + /// Whether the target supports XRay instrumentation. + pub supports_xray: bool, } /// Add arguments for the given flavor and also for its "twin" flavors @@ -1937,6 +1940,7 @@ impl Default for TargetOptions { supports_stack_protector: true, entry_name: "main".into(), entry_abi: Conv::C, + supports_xray: false, } } } @@ -2592,6 +2596,7 @@ impl Target { key!(supports_stack_protector, bool); key!(entry_name); key!(entry_abi, Conv)?; + key!(supports_xray, bool); if base.is_builtin { // This can cause unfortunate ICEs later down the line. @@ -2845,6 +2850,7 @@ impl ToJson for Target { target_option_val!(supports_stack_protector); target_option_val!(entry_name); target_option_val!(entry_abi); + target_option_val!(supports_xray); if let Some(abi) = self.default_adjusted_cabi { d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index 9c913784855..a3bdb5f5465 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -8,6 +8,7 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; + base.supports_xray = true; Target { llvm_target: "x86_64-linux-android".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index 98988ab3595..b41e5842aad 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -8,6 +8,7 @@ pub fn target() -> Target { base.stack_probes = StackProbeType::X86; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index a91ab365b66..9af1049b870 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 9087dc3df60..bf4cf7d7bec 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index 64ae425d8c0..74c434935ba 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -11,6 +11,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-netbsd".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index 66b8e20226f..8e4d42a0aca 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -6,6 +6,7 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-openbsd".into(), diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3f863038efb..d3eba43b47e 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -24,3 +24,4 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +itertools = "0.10.1" diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 8525b96c0c2..126ec60b3d6 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -3,7 +3,8 @@ use super::infcx_ext::InferCtxtExt; #[cfg(doc)] use super::trait_goals::structural_traits::*; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; +use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use itertools::Itertools; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate_predicates; @@ -399,10 +400,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Alias(_, alias_ty) => alias_ty, }; - for (assumption, _) in self - .tcx() - .bound_explicit_item_bounds(alias_ty.def_id) - .subst_iter_copied(self.tcx(), alias_ty.substs) + for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs) { match G::consider_assumption(self, goal, assumption) { Ok(result) => { @@ -462,4 +460,79 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } + + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn merge_candidates_and_discard_reservation_impls( + &mut self, + mut candidates: Vec<Candidate<'tcx>>, + ) -> QueryResult<'tcx> { + match candidates.len() { + 0 => return Err(NoSolution), + 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result), + _ => {} + } + + if candidates.len() > 1 { + let mut i = 0; + 'outer: while i < candidates.len() { + for j in (0..candidates.len()).filter(|&j| i != j) { + if self.trait_candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + ) { + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); + candidates.swap_remove(i); + continue 'outer; + } + } + + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); + i += 1; + } + + // If there are *STILL* multiple candidates that have *different* response + // results, give up and report ambiguity. + if candidates.len() > 1 && !candidates.iter().map(|cand| cand.result).all_equal() { + let certainty = if candidates.iter().all(|x| { + matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow)) + }) { + Certainty::Maybe(MaybeCause::Overflow) + } else { + Certainty::AMBIGUOUS + }; + return self.make_canonical_response(certainty); + } + } + + // FIXME: What if there are >1 candidates left with the same response, and one is a reservation impl? + Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result) + } + + fn trait_candidate_should_be_dropped_in_favor_of( + &self, + candidate: &Candidate<'tcx>, + other: &Candidate<'tcx>, + ) -> bool { + // FIXME: implement this + match (candidate.source, other.source) { + (CandidateSource::Impl(_), _) + | (CandidateSource::ParamEnv(_), _) + | (CandidateSource::AliasBound, _) + | (CandidateSource::BuiltinImpl, _) => false, + } + } + + fn discard_reservation_impl(&self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> { + if let CandidateSource::Impl(def_id) = candidate.source { + if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) { + debug!("Selected reservation impl"); + // We assemble all candidates inside of a probe so by + // making a new canonical response here our result will + // have no constraints. + candidate.result = self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(); + } + } + + candidate + } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e3ec71d1b4f..4fea49893a6 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::{specialization_graph, translate_substs}; -use super::assembly::{self, Candidate, CandidateSource}; +use super::assembly; use super::infcx_ext::InferCtxtExt; use super::trait_goals::structural_traits; use super::{Certainty, EvalCtxt, Goal, QueryResult}; @@ -34,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // projection cache in the solver. if self.term_is_fully_unconstrained(goal) { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_project_candidates(candidates) + self.merge_candidates_and_discard_reservation_impls(candidates) } else { let predicate = goal.predicate; let unconstrained_rhs = match predicate.term.unpack() { @@ -153,59 +153,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty)) } - - fn merge_project_candidates( - &mut self, - mut candidates: Vec<Candidate<'tcx>>, - ) -> QueryResult<'tcx> { - match candidates.len() { - 0 => return Err(NoSolution), - 1 => return Ok(candidates.pop().unwrap().result), - _ => {} - } - - if candidates.len() > 1 { - let mut i = 0; - 'outer: while i < candidates.len() { - for j in (0..candidates.len()).filter(|&j| i != j) { - if self.project_candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - ) { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - continue 'outer; - } - } - - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - i += 1; - if i > 1 { - debug!("multiple matches, ambig"); - // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. - unimplemented!(); - } - } - } - - Ok(candidates.pop().unwrap().result) - } - - fn project_candidate_should_be_dropped_in_favor_of( - &self, - candidate: &Candidate<'tcx>, - other: &Candidate<'tcx>, - ) -> bool { - // FIXME: implement this - match (candidate.source, other.source) { - (CandidateSource::Impl(_), _) - | (CandidateSource::ParamEnv(_), _) - | (CandidateSource::BuiltinImpl, _) - | (CandidateSource::AliasBound, _) => unimplemented!(), - } - } } impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { @@ -452,7 +399,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { [ty::GenericArg::from(goal.predicate.self_ty())], )); - let is_sized_certainty = ecx.evaluate_goal(goal.with(tcx, sized_predicate))?.1; + let (_, is_sized_certainty) = + ecx.evaluate_goal(goal.with(tcx, sized_predicate))?; return ecx.eq_term_and_make_canonical_response( goal, is_sized_certainty, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 06a72e95d49..6554c739b3f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,7 +2,7 @@ use std::iter; -use super::assembly::{self, Candidate, CandidateSource}; +use super::assembly; use super::infcx_ext::InferCtxtExt; use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; @@ -89,6 +89,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { + // This differs from the current stable behavior and + // fixes #84857. Due to breakage found via crater, we + // currently instead lint patterns which can be used to + // exploit this unsoundness on stable, see #93367 for + // more details. + if let Some(def_id) = ecx.tcx().find_map_relevant_impl( + goal.predicate.def_id(), + goal.predicate.self_ty(), + Some, + ) { + debug!(?def_id, ?goal, "disqualified auto-trait implementation"); + return Err(NoSolution); + } + ecx.probe_and_evaluate_goal_for_constituent_tys( goal, structural_traits::instantiate_constituent_tys_for_auto_trait, @@ -479,73 +493,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_trait_candidates_discard_reservation_impls(candidates) - } - - #[instrument(level = "debug", skip(self), ret)] - pub(super) fn merge_trait_candidates_discard_reservation_impls( - &mut self, - mut candidates: Vec<Candidate<'tcx>>, - ) -> QueryResult<'tcx> { - match candidates.len() { - 0 => return Err(NoSolution), - 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result), - _ => {} - } - - if candidates.len() > 1 { - let mut i = 0; - 'outer: while i < candidates.len() { - for j in (0..candidates.len()).filter(|&j| i != j) { - if self.trait_candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - ) { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - continue 'outer; - } - } - - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - i += 1; - if i > 1 { - debug!("multiple matches, ambig"); - // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. - unimplemented!(); - } - } - } - - Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result) - } - - fn trait_candidate_should_be_dropped_in_favor_of( - &self, - candidate: &Candidate<'tcx>, - other: &Candidate<'tcx>, - ) -> bool { - // FIXME: implement this - match (candidate.source, other.source) { - (CandidateSource::Impl(_), _) - | (CandidateSource::ParamEnv(_), _) - | (CandidateSource::AliasBound, _) - | (CandidateSource::BuiltinImpl, _) => unimplemented!(), - } - } - - fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> { - if let CandidateSource::Impl(def_id) = candidate.source { - if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) { - debug!("Selected reservation impl"); - // FIXME: reduce candidate to ambiguous - // FIXME: replace `var_values` with identity, yeet external constraints. - unimplemented!() - } - } - - candidate + self.merge_candidates_and_discard_reservation_impls(candidates) } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 59aef52910e..048a5471994 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -19,6 +19,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; +use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_hir::{Expr, HirId}; @@ -98,6 +99,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { // obligation fn get_from_await_ty<F>( &self, + tcx: TyCtxt<'tcx>, visitor: AwaitsVisitor, hir: map::Map<'tcx>, ty_matches: F, @@ -134,9 +136,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { .unwrap_or_else(|| { bug!( "node_type: no type for node {}", - ty::tls::with(|tcx| tcx - .hir() - .node_to_string(await_expr.hir_id)) + tcx.hir().node_to_string(await_expr.hir_id) ) }) }, @@ -1350,14 +1350,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Applicability::MaybeIncorrect, ); } else { + // Issue #104961, we need to add parentheses properly for compond expressions + // for example, `x.starts_with("hi".to_string() + "you")` + // should be `x.starts_with(&("hi".to_string() + "you"))` + let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; }; + let body = self.tcx.hir().body(body_id); + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(body.value); + let Some(expr) = expr_finder.result else { return false; }; + let needs_parens = match expr.kind { + // parenthesize if needed (Issue #46756) + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + }; + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "consider{} borrowing here", - if is_mut { " mutably" } else { "" } - ), - format!("&{}", if is_mut { "mut " } else { "" }), + let span = if needs_parens { span } else { span.shrink_to_lo() }; + let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); + let sugg_msg = &format!( + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } + ); + + let suggestions = if !needs_parens { + vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))] + } else { + vec![ + (span.shrink_to_lo(), format!("{}(", sugg_prefix)), + (span.shrink_to_hi(), ")".to_string()), + ] + }; + err.multipart_suggestion_verbose( + sugg_msg, + suggestions, Applicability::MaybeIncorrect, ); } @@ -2351,7 +2378,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut interior_or_upvar_span = None; - let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches); + let from_awaited_ty = generator_data.get_from_await_ty(self.tcx, visitor, hir, ty_matches); debug!(?from_awaited_ty); // The generator interior types share the same binders diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 8f548acfd2e..bafa2981a87 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -646,11 +646,9 @@ fn object_ty_for_trait<'tcx>( debug!(?obligation); let pred = obligation.predicate.to_opt_poly_projection_pred()?; Some(pred.map_bound(|p| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - def_id: p.projection_ty.def_id, - substs: p.projection_ty.substs, - term: p.term, - }) + ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( + tcx, p, + )) })) }) .collect(); diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 9c5db3314c5..65ed3105d10 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -493,6 +493,9 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t ty::ReEarlyBound(_) => { panic!("Should have already been substituted."); } + ty::ReError(_) => { + panic!("Error lifetime should not have already been lowered."); + } ty::ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), br.var.as_usize(), diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index fe633d687d9..2c6c77072e6 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -70,47 +70,61 @@ fn compute_implied_outlives_bounds<'tcx>( let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) .unwrap_or_default(); - // While these predicates should all be implied by other parts of - // the program, they are still relevant as they may constrain - // inference variables, which is necessary to add the correct - // implied bounds in some cases, mostly when dealing with projections. - ocx.register_obligations( - obligations.iter().filter(|o| o.predicate.has_non_region_infer()).cloned(), - ); - - // From the full set of obligations, just filter down to the - // region relationships. - outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| { + for obligation in obligations { + debug!(?obligation); assert!(!obligation.has_escaping_bound_vars()); - match obligation.predicate.kind().no_bound_vars() { - None => None, - Some(pred) => match pred { - ty::PredicateKind::Clause(ty::Clause::Trait(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::Clause::Projection(..)) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - ty::PredicateKind::WellFormed(arg) => { - wf_args.push(arg); - None + + // While these predicates should all be implied by other parts of + // the program, they are still relevant as they may constrain + // inference variables, which is necessary to add the correct + // implied bounds in some cases, mostly when dealing with projections. + // + // Another important point here: we only register `Projection` + // predicates, since otherwise we might register outlives + // predicates containing inference variables, and we don't + // learn anything new from those. + if obligation.predicate.has_non_region_infer() { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(..)) => { + ocx.register_obligation(obligation.clone()); } + _ => {} + } + } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives( - ty::OutlivesPredicate(r_a, r_b), - )) => Some(ty::OutlivesPredicate(r_a.into(), r_b)), + let pred = match obligation.predicate.kind().no_bound_vars() { + None => continue, + Some(pred) => pred, + }; + match pred { + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} + + // We need to search through *all* WellFormed predicates + ty::PredicateKind::WellFormed(arg) => { + wf_args.push(arg); + } + + // We need to register region relationships + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate( + r_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( - ty_a, - r_b, - ))) => Some(ty::OutlivesPredicate(ty_a.into(), r_b)), - }, + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), } - })); + } } // This call to `select_all_or_error` is necessary to constrain inference variables, which we diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 9aa26667e7b..8bea5588ae7 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -4,6 +4,7 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![feature(let_chains)] +#![feature(drain_filter)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 3ede95e8431..61557fdc0ed 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -960,6 +960,9 @@ pub enum RegionKind<I: Interner> { /// Erased region, used by trait selection, in MIR and during codegen. ReErased, + + /// A region that resulted from some other error. Used exclusively for diagnostics. + ReError(I::ErrorGuaranteed), } // This is manually implemented for `RegionKind` because `std::mem::discriminant` @@ -974,6 +977,7 @@ const fn regionkind_discriminant<I: Interner>(value: &RegionKind<I>) -> usize { ReVar(_) => 4, RePlaceholder(_) => 5, ReErased => 6, + ReError(_) => 7, } } @@ -985,6 +989,7 @@ where I::FreeRegion: Copy, I::RegionVid: Copy, I::PlaceholderRegion: Copy, + I::ErrorGuaranteed: Copy, { } @@ -999,6 +1004,7 @@ impl<I: Interner> Clone for RegionKind<I> { ReVar(r) => ReVar(r.clone()), RePlaceholder(r) => RePlaceholder(r.clone()), ReErased => ReErased, + ReError(r) => ReError(r.clone()), } } } @@ -1016,10 +1022,11 @@ impl<I: Interner> PartialEq for RegionKind<I> { (ReVar(a_r), ReVar(b_r)) => a_r == b_r, (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r, (ReErased, ReErased) => true, + (ReError(_), ReError(_)) => true, _ => { debug_assert!( false, - "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" + "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" ); true } @@ -1077,6 +1084,7 @@ impl<I: Interner> hash::Hash for RegionKind<I> { ReVar(r) => r.hash(state), RePlaceholder(r) => r.hash(state), ReErased => (), + ReError(_) => (), } } } @@ -1100,6 +1108,8 @@ impl<I: Interner> fmt::Debug for RegionKind<I> { RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), ReErased => f.write_str("ReErased"), + + ReError(_) => f.write_str("ReError"), } } } @@ -1134,6 +1144,7 @@ where a.encode(e); }), ReErased => e.emit_enum_variant(disc, |_| {}), + ReError(_) => e.emit_enum_variant(disc, |_| {}), } } } @@ -1146,6 +1157,7 @@ where I::FreeRegion: Decodable<D>, I::RegionVid: Decodable<D>, I::PlaceholderRegion: Decodable<D>, + I::ErrorGuaranteed: Decodable<D>, { fn decode(d: &mut D) -> Self { match Decoder::read_usize(d) { @@ -1156,6 +1168,7 @@ where 4 => ReVar(Decodable::decode(d)), 5 => RePlaceholder(Decodable::decode(d)), 6 => ReErased, + 7 => ReError(Decodable::decode(d)), _ => panic!( "{}", format!( @@ -1184,7 +1197,7 @@ where ) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - ReErased | ReStatic => { + ReErased | ReStatic | ReError(_) => { // No variant fields to hash for these ... } ReLateBound(d, r) => { diff --git a/library/core/src/result.rs b/library/core/src/result.rs index f00c40f35d5..7596e9cc005 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -458,7 +458,7 @@ //! [`Result`] of a collection of each contained value of the original //! [`Result`] values, or [`Err`] if any of the elements was [`Err`]. //! -//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA%2C%20E%3E%3E-for-Result%3CV%2C%20E%3E +//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA,+E%3E%3E-for-Result%3CV,+E%3E //! //! ``` //! let v = [Ok(2), Ok(4), Err("err!"), Ok(8)]; @@ -474,8 +474,8 @@ //! to provide the [`product`][Iterator::product] and //! [`sum`][Iterator::sum] methods. //! -//! [impl-Product]: Result#impl-Product%3CResult%3CU%2C%20E%3E%3E-for-Result%3CT%2C%20E%3E -//! [impl-Sum]: Result#impl-Sum%3CResult%3CU%2C%20E%3E%3E-for-Result%3CT%2C%20E%3E +//! [impl-Product]: Result#impl-Product%3CResult%3CU,+E%3E%3E-for-Result%3CT,+E%3E +//! [impl-Sum]: Result#impl-Sum%3CResult%3CU,+E%3E%3E-for-Result%3CT,+E%3E //! //! ``` //! let v = [Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")]; diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index a342f0f5e85..a5ce6d5120d 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -141,12 +141,28 @@ mod imp { // list. let argv = ARGV.load(Ordering::Relaxed); let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - (0..argc) - .map(|i| { - let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect() + let mut args = Vec::with_capacity(argc as usize); + for i in 0..argc { + let ptr = *argv.offset(i) as *const libc::c_char; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. That + // means that `argc` might be bigger than the actual number of non-`NULL` + // pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` argument. + // + // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments + // after the first `NULL` can safely be ignored. + if ptr.is_null() { + break; + } + + let cstr = CStr::from_ptr(ptr); + args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + args } } } diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index f5513e9996d..9919dc7087e 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -21,6 +21,7 @@ mod libc { extern "C" { pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; pub fn chdir(dir: *const c_char) -> c_int; + pub fn __wasilibc_get_environ() -> *mut *mut c_char; } } @@ -161,7 +162,12 @@ impl Iterator for Env { pub fn env() -> Env { unsafe { let _guard = env_read_lock(); - let mut environ = libc::environ; + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_environ(); + let mut result = Vec::new(); if !environ.is_null() { while !(*environ).is_null() { diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 5c863015adb..6dcba117c24 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -229,10 +229,10 @@ impl Config { "--retry", "3", "-Sf", - "-o", ]); - curl.arg(tempfile); curl.arg(url); + let f = File::create(tempfile).unwrap(); + curl.stdout(Stdio::from(f)); if !self.check_run(&mut curl) { if self.build.contains("windows-msvc") { println!("Fallback to PowerShell"); @@ -340,9 +340,12 @@ impl Config { let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - let legacy_rustfmt = self.initial_rustc.with_file_name(exe("rustfmt", host)); - if !legacy_rustfmt.exists() { - t!(self.symlink_file(&rustfmt_path, &legacy_rustfmt)); + #[cfg(not(windows))] + { + let legacy_rustfmt = self.initial_rustc.with_file_name(exe("rustfmt", host)); + if !legacy_rustfmt.exists() { + t!(self.symlink_file(&rustfmt_path, &legacy_rustfmt)); + } } if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 1770c121a0e..c7f120dafea 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -49,10 +49,10 @@ Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) platform security feature. This flag is currently ignored for non-Windows targets. It takes one of the following values: -* `y`, `yes`, `on`, `checks`, or no value: enable Control Flow Guard. +* `y`, `yes`, `on`, `true`, `checks`, or no value: enable Control Flow Guard. * `nochecks`: emit Control Flow Guard metadata without runtime enforcement checks (this should only be used for testing purposes as it does not provide security enforcement). -* `n`, `no`, `off`: do not enable Control Flow Guard (the default). +* `n`, `no`, `off`, `false`: do not enable Control Flow Guard (the default). ## debug-assertions @@ -60,8 +60,8 @@ This flag lets you turn `cfg(debug_assertions)` [conditional compilation](../../reference/conditional-compilation.md#debug_assertions) on or off. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable debug-assertions. -* `n`, `no`, or `off`: disable debug-assertions. +* `y`, `yes`, `on`, `true`, or no value: enable debug-assertions. +* `n`, `no`, `off` or `false`: disable debug-assertions. If not specified, debug assertions are automatically enabled only if the [opt-level](#opt-level) is 0. @@ -82,8 +82,8 @@ Note: The [`-g` flag][option-g-debug] is an alias for `-C debuginfo=2`. This flag controls whether or not the linker includes its default libraries. It takes one of the following values: -* `y`, `yes`, `on`, or no value: include default libraries (the default). -* `n`, `no`, or `off`: exclude default libraries. +* `y`, `yes`, `on`, `true` or no value: include default libraries (the default). +* `n`, `no`, `off` or `false`: exclude default libraries. For example, for gcc flavor linkers, this issues the `-nodefaultlibs` flag to the linker. @@ -93,8 +93,8 @@ the linker. This flag controls whether or not the compiler embeds LLVM bitcode into object files. It takes one of the following values: -* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default). -* `n`, `no`, or `off`: omit bitcode from rlibs. +* `y`, `yes`, `on`, `true` or no value: put bitcode in rlibs (the default). +* `n`, `no`, `off` or `false`: omit bitcode from rlibs. LLVM bitcode is required when rustc is performing link-time optimization (LTO). It is also required on some targets like iOS ones where vendors look for LLVM @@ -135,8 +135,8 @@ flag][option-emit] for more information. This flag forces the use of frame pointers. It takes one of the following values: -* `y`, `yes`, `on`, or no value: force-enable frame pointers. -* `n`, `no`, or `off`: do not force-enable frame pointers. This does +* `y`, `yes`, `on`, `true` or no value: force-enable frame pointers. +* `n`, `no`, `off` or `false`: do not force-enable frame pointers. This does not necessarily mean frame pointers will be removed. The default behaviour, if frame pointers are not force-enabled, depends on the @@ -147,8 +147,8 @@ target. This flag forces the generation of unwind tables. It takes one of the following values: -* `y`, `yes`, `on`, or no value: Unwind tables are forced to be generated. -* `n`, `no`, or `off`: Unwind tables are not forced to be generated. If unwind +* `y`, `yes`, `on`, `true` or no value: Unwind tables are forced to be generated. +* `n`, `no`, `off` or `false`: Unwind tables are not forced to be generated. If unwind tables are required by the target an error will be emitted. The default if not specified depends on the target. @@ -202,8 +202,8 @@ options should be separated by spaces. This flag controls whether the linker will keep dead code. It takes one of the following values: -* `y`, `yes`, `on`, or no value: keep dead code. -* `n`, `no`, or `off`: remove dead code (the default). +* `y`, `yes`, `on`, `true` or no value: 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. @@ -215,8 +215,8 @@ linker will use libraries and objects shipped with Rust instead or those in the It takes one of the following values: * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools. -* `y`, `yes`, `on`: use only libraries/objects shipped with Rust. -* `n`, `no`, or `off`: rely on the user or the linker to provide non-Rust libraries/objects. +* `y`, `yes`, `on`, `true`: use only libraries/objects shipped with Rust. +* `n`, `no`, `off` or `false`: rely on the user or the linker to provide non-Rust libraries/objects. This allows overriding cases when detection fails or user wants to use shipped libraries. @@ -261,8 +261,8 @@ This flag defers LTO optimizations to the linker. See [linker-plugin-LTO](../linker-plugin-lto.md) for more details. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable linker plugin LTO. -* `n`, `no`, or `off`: disable linker plugin LTO (the default). +* `y`, `yes`, `on`, `true` or no value: enable linker plugin LTO. +* `n`, `no`, `off` or `false`: disable linker plugin LTO (the default). * A path to the linker plugin. More specifically this flag will cause the compiler to replace its typical @@ -292,9 +292,9 @@ optimizations](https://llvm.org/docs/LinkTimeOptimization.html) to produce better optimized code, using whole-program analysis, at the cost of longer linking time. It takes one of the following values: -* `y`, `yes`, `on`, `fat`, or no value: perform "fat" LTO which attempts to +* `y`, `yes`, `on`, `true`, `fat`, or no value: perform "fat" LTO which attempts to perform optimizations across all crates within the dependency graph. -* `n`, `no`, `off`: disables LTO. +* `n`, `no`, `off`, `false`: disables LTO. * `thin`: perform ["thin" LTO](http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html). This is similar to "fat", but takes substantially less time to run while @@ -333,8 +333,8 @@ This flag allows you to disable [the red zone](https://en.wikipedia.org/wiki/Red_zone_\(computing\)). It takes one of the following values: -* `y`, `yes`, `on`, or no value: disable the red zone. -* `n`, `no`, or `off`: enable the red zone. +* `y`, `yes`, `on`, `true` or no value: disable the red zone. +* `n`, `no`, `off` or `false`: enable the red zone. The default behaviour, if the flag is not specified, depends on the target. @@ -376,8 +376,8 @@ overflow](../../reference/expressions/operator-expr.md#overflow). When overflow-checks are enabled, a panic will occur on overflow. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: enable overflow checks. -* `n`, `no`, or `off`: disable overflow checks. +* `y`, `yes`, `on`, `true` or no value: enable overflow checks. +* `n`, `no`, `off` or `false`: disable overflow checks. If not specified, overflow checks are enabled if [debug-assertions](#debug-assertions) are enabled, disabled otherwise. @@ -409,8 +409,8 @@ for determining whether or not it is possible to statically or dynamically link with a dependency. For example, `cdylib` crate types may only use static linkage. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: use dynamic linking. -* `n`, `no`, or `off`: use static linking (the default). +* `y`, `yes`, `on`, `true` or no value: use dynamic linking. +* `n`, `no`, `off` or `false`: use static linking (the default). ## profile-generate @@ -487,24 +487,24 @@ The list of passes should be separated by spaces. This flag controls whether [`rpath`](https://en.wikipedia.org/wiki/Rpath) is enabled. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable rpath. -* `n`, `no`, or `off`: disable rpath (the default). +* `y`, `yes`, `on`, `true` or no value: enable rpath. +* `n`, `no`, `off` or `false`: disable rpath (the default). ## save-temps This flag controls whether temporary files generated during compilation are deleted once compilation finishes. It takes one of the following values: -* `y`, `yes`, `on`, or no value: save temporary files. -* `n`, `no`, or `off`: delete temporary files (the default). +* `y`, `yes`, `on`, `true` or no value: save temporary files. +* `n`, `no`, `off` or `false`: delete temporary files (the default). ## soft-float This option controls whether `rustc` generates code that emulates floating point instructions in software. It takes one of the following values: -* `y`, `yes`, `on`, or no value: use soft floats. -* `n`, `no`, or `off`: use hardware floats (the default). +* `y`, `yes`, `on`, `true` or no value: use soft floats. +* `n`, `no`, `off` or `false`: use hardware floats (the default). ## split-debuginfo diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md new file mode 100644 index 00000000000..7fb33cd68b4 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -0,0 +1,39 @@ +# `instrument-xray` + +The tracking issue for this feature is: [#102921](https://github.com/rust-lang/rust/issues/102921). + +------------------------ + +Enable generation of NOP sleds for XRay function tracing instrumentation. +For more information on XRay, +read [LLVM documentation](https://llvm.org/docs/XRay.html), +and/or the [XRay whitepaper](http://research.google.com/pubs/pub45287.html). + +Set the `-Z instrument-xray` compiler flag in order to enable XRay instrumentation. + + - `-Z instrument-xray` – use the default settings + - `-Z instrument-xray=skip-exit` – configure a custom setting + - `-Z instrument-xray=ignore-loops,instruction-threshold=300` – + multiple settings separated by commas + +Supported options: + + - `always` – force instrumentation of all functions + - `never` – do no instrument any functions + - `ignore-loops` – ignore presence of loops, + instrument functions based only on instruction count + - `instruction-threshold=10` – set a different instruction threshold for instrumentation + - `skip-entry` – do no instrument function entry + - `skip-exit` – do no instrument function exit + +The default settings are: + + - instrument both entry & exit from functions + - instrument functions with at least 200 instructions, + or containing a non-trivial loop + +Note that `-Z instrument-xray` only enables generation of NOP sleds +which on their own don't do anything useful. +In order to actually trace the functions, +you will need to link a separate runtime library of your choice, +such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 80493b100bb..5bda3620dd0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -242,6 +242,7 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life ty::ReLateBound(..) | ty::ReFree(..) | ty::ReVar(..) + | ty::ReError(_) | ty::RePlaceholder(..) | ty::ReErased => { debug!("cannot clean region {:?}", region); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index fb7c34118a4..94de93e7a99 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -46,6 +46,7 @@ use crate::html::escape::Escape; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::length_limit::HtmlWithLimit; +use crate::html::render::small_url_encode; use crate::html::toc::TocBuilder; use pulldown_cmark::{ @@ -294,47 +295,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { doctest::make_test(&test, krate, false, &Default::default(), edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; - // These characters don't need to be escaped in a URI. - // See https://url.spec.whatwg.org/#query-percent-encode-set - // and https://url.spec.whatwg.org/#urlencoded-parsing - // and https://url.spec.whatwg.org/#url-code-points - fn dont_escape(c: u8) -> bool { - (b'a' <= c && c <= b'z') - || (b'A' <= c && c <= b'Z') - || (b'0' <= c && c <= b'9') - || c == b'-' - || c == b'_' - || c == b'.' - || c == b',' - || c == b'~' - || c == b'!' - || c == b'\'' - || c == b'(' - || c == b')' - || c == b'*' - || c == b'/' - || c == b';' - || c == b':' - || c == b'?' - // As described in urlencoded-parsing, the - // first `=` is the one that separates key from - // value. Following `=`s are part of the value. - || c == b'=' - } - let mut test_escaped = String::new(); - for b in test.bytes() { - if dont_escape(b) { - test_escaped.push(char::from(b)); - } else if b == b' ' { - // URL queries are decoded with + replaced with SP - test_escaped.push('+'); - } else if b == b'%' { - test_escaped.push('%'); - test_escaped.push('%'); - } else { - write!(test_escaped, "%{:02X}", b).unwrap(); - } - } + let test_escaped = small_url_encode(test); Some(format!( r#"<a class="test-arrow" target="_blank" href="{}?code={}{}&edition={}">Run</a>"#, url, test_escaped, channel, edition, diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index b59645ec2e2..1216a8d71c8 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -18,7 +18,7 @@ use super::search_index::build_index; use super::write_shared::write_shared; use super::{ collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes, - LinkFromSrc, NameDoc, StylePath, + LinkFromSrc, StylePath, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -256,7 +256,7 @@ impl<'tcx> Context<'tcx> { } /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> { + fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<String>> { // BTreeMap instead of HashMap to get a sorted output let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); let mut inserted: FxHashMap<ItemType, FxHashSet<Symbol>> = FxHashMap::default(); @@ -274,10 +274,7 @@ impl<'tcx> Context<'tcx> { if inserted.entry(short).or_default().insert(myname) { let short = short.to_string(); let myname = myname.to_string(); - map.entry(short).or_default().push(( - myname, - Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), - )); + map.entry(short).or_default().push(myname); } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 816a8f4e274..6a9a7a6b1a0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -38,7 +38,7 @@ pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc}; use std::collections::VecDeque; use std::default::Default; -use std::fmt; +use std::fmt::{self, Write}; use std::fs; use std::iter::Peekable; use std::path::PathBuf; @@ -83,9 +83,6 @@ use crate::scrape_examples::{CallData, CallLocation}; use crate::try_none; use crate::DOC_RUST_LANG_ORG_CHANNEL; -/// A pair of name and its optional document. -pub(crate) type NameDoc = (String, Option<String>); - pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } @@ -2020,31 +2017,60 @@ fn get_associated_constants( .collect::<Vec<_>>() } -// The point is to url encode any potential character from a type with genericity. -fn small_url_encode(s: String) -> String { +pub(crate) fn small_url_encode(s: String) -> String { + // These characters don't need to be escaped in a URI. + // See https://url.spec.whatwg.org/#query-percent-encode-set + // and https://url.spec.whatwg.org/#urlencoded-parsing + // and https://url.spec.whatwg.org/#url-code-points + fn dont_escape(c: u8) -> bool { + (b'a' <= c && c <= b'z') + || (b'A' <= c && c <= b'Z') + || (b'0' <= c && c <= b'9') + || c == b'-' + || c == b'_' + || c == b'.' + || c == b',' + || c == b'~' + || c == b'!' + || c == b'\'' + || c == b'(' + || c == b')' + || c == b'*' + || c == b'/' + || c == b';' + || c == b':' + || c == b'?' + // As described in urlencoded-parsing, the + // first `=` is the one that separates key from + // value. Following `=`s are part of the value. + || c == b'=' + } let mut st = String::new(); let mut last_match = 0; - for (idx, c) in s.char_indices() { - let escaped = match c { - '<' => "%3C", - '>' => "%3E", - ' ' => "%20", - '?' => "%3F", - '\'' => "%27", - '&' => "%26", - ',' => "%2C", - ':' => "%3A", - ';' => "%3B", - '[' => "%5B", - ']' => "%5D", - '"' => "%22", - _ => continue, - }; + for (idx, b) in s.bytes().enumerate() { + if dont_escape(b) { + continue; + } - st += &s[last_match..idx]; - st += escaped; - // NOTE: we only expect single byte characters here - which is fine as long as we - // only match single byte characters + if last_match != idx { + // Invariant: `idx` must be the first byte in a character at this point. + st += &s[last_match..idx]; + } + if b == b' ' { + // URL queries are decoded with + replaced with SP. + // While the same is not true for hashes, rustdoc only needs to be + // consistent with itself when encoding them. + st += "+"; + } else if b == b'%' { + st += "%%"; + } else { + write!(st, "%{:02X}", b).unwrap(); + } + // Invariant: if the current byte is not at the start of a multi-byte character, + // we need to get down here so that when the next turn of the loop comes around, + // last_match winds up equalling idx. + // + // In other words, dont_escape must always return `false` in multi-byte character. last_match = idx + 1; } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index c2d24e51484..82a51a8467b 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1109,7 +1109,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); - render_union(w, it, Some(&s.generics), &s.fields, "", cx); + render_union(w, it, Some(&s.generics), &s.fields, cx); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1628,7 +1628,6 @@ fn render_union( it: &clean::Item, g: Option<&clean::Generics>, fields: &[clean::Item], - tab: &str, cx: &Context<'_>, ) { let tcx = cx.tcx(); @@ -1651,7 +1650,7 @@ fn render_union( w.write_str(" "); } - write!(w, "{{\n{}", tab); + write!(w, "{{\n"); let count_fields = fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); let toggle = should_hide_fields(count_fields); @@ -1663,17 +1662,16 @@ fn render_union( if let clean::StructFieldItem(ref ty) = *field.kind { write!( w, - " {}{}: {},\n{}", + " {}{}: {},\n", visibility_print_with_space(field.visibility(tcx), field.item_id, cx), field.name.unwrap(), - ty.print(cx), - tab + ty.print(cx) ); } } if it.has_stripped_entries().unwrap() { - write!(w, " /* private fields */\n{}", tab); + write!(w, " /* private fields */\n"); } if toggle { toggle_close(w); diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index c66f500f423..aa4d65b4bfb 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -455,10 +455,7 @@ function loadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block " + shortty; - for (const item of filtered) { - const name = item[0]; - const desc = item[1]; // can be null - + for (const name of filtered) { let path; if (shortty === "mod") { path = name + "/index.html"; @@ -468,7 +465,6 @@ function loadCss(cssUrl) { const current_page = document.location.href.split("/").pop(); const link = document.createElement("a"); link.href = path; - link.title = desc; if (path === current_page) { link.className = "current"; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 64108c88285..90d6388b70c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -814,9 +814,9 @@ fn main_args(at_args: &[String]) -> MainResult { sess.fatal("Compilation failed, aborting rustdoc"); } - let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess); + let mut gcx = abort_on_err(queries.global_ctxt(), sess); - global_ctxt.enter(|tcx| { + gcx.enter(|tcx| { let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { core::run_global_ctxt( tcx, diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index e2cde09776f..659e8aebcd5 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -4383,6 +4383,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes +[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 2cfb47dd758..70d1268090f 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -42,6 +42,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies +clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index ab44db69483..95f6d2cc45c 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md index 23867df8efe..df4a1f2702e 100644 --- a/src/tools/clippy/book/src/README.md +++ b/src/tools/clippy/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index f79dbb50ff4..32e8e218c40 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -43,6 +43,7 @@ Please use that command to update the file and do not edit it by hand. | [allowed-scripts](#allowed-scripts) | `["Latin"]` | | [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` | | [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` | +| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` | | [max-include-file-size](#max-include-file-size) | `1000000` | | [allow-expect-in-tests](#allow-expect-in-tests) | `false` | | [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` | @@ -167,6 +168,17 @@ The minimum rust version that the project supports * [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) * [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) +* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) +* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) +* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) +* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) +* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) ### cognitive-complexity-threshold @@ -279,7 +291,7 @@ The minimum size (in bytes) to consider a type for passing by reference instead **Default Value:** `256` (`u64`) -* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) ### too-many-lines-threshold @@ -442,6 +454,14 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. * [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +### await-holding-invalid-types + + +**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`) + +* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) + + ### max-include-file-size The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -497,7 +517,7 @@ for the generic parameters for determining interior mutability **Default Value:** `["bytes::Bytes"]` (`Vec<String>`) -* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) +* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) ### allow-mixed-uninlined-format-args @@ -509,7 +529,7 @@ Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar) ### suppress-restriction-lint-in-const -In same +Whether to suppress a restriction lint in constant code. In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code. This configuration will cause restriction lints to trigger even diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 510c7e852af..c3f8a782d27 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "3.2" +clap = "4.1.4" indoc = "1.0" itertools = "0.10.1" opener = "0.5" diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index d3e03669204..e2457e5a8a5 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -2,7 +2,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; @@ -11,22 +11,22 @@ fn main() { match matches.subcommand() { Some(("bless", matches)) => { - bless::bless(matches.contains_id("ignore-timestamp")); + bless::bless(matches.get_flag("ignore-timestamp")); }, Some(("dogfood", matches)) => { dogfood::dogfood( - matches.contains_id("fix"), - matches.contains_id("allow-dirty"), - matches.contains_id("allow-staged"), + matches.get_flag("fix"), + matches.get_flag("allow-dirty"), + matches.get_flag("allow-staged"), ); }, Some(("fmt", matches)) => { - fmt::run(matches.contains_id("check"), matches.contains_id("verbose")); + fmt::run(matches.get_flag("check"), matches.get_flag("verbose")); }, Some(("update_lints", matches)) => { - if matches.contains_id("print-only") { + if matches.get_flag("print-only") { update_lints::print_lints(); - } else if matches.contains_id("check") { + } else if matches.get_flag("check") { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); @@ -38,7 +38,7 @@ fn main() { matches.get_one::<String>("name"), matches.get_one::<String>("category").map(String::as_str), matches.get_one::<String>("type").map(String::as_str), - matches.contains_id("msrv"), + matches.get_flag("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), @@ -46,7 +46,7 @@ fn main() { }, Some(("setup", sub_command)) => match sub_command.subcommand() { Some(("intellij", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::intellij::remove_rustc_src(); } else { setup::intellij::setup_rustc_src( @@ -57,17 +57,17 @@ fn main() { } }, Some(("git-hook", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::git_hook::remove_hook(); } else { - setup::git_hook::install_hook(matches.contains_id("force-override")); + setup::git_hook::install_hook(matches.get_flag("force-override")); } }, Some(("vscode-tasks", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.contains_id("force-override")); + setup::vscode::install_tasks(matches.get_flag("force-override")); } }, _ => {}, @@ -91,7 +91,7 @@ fn main() { Some(("rename_lint", matches)) => { let old_name = matches.get_one::<String>("old_name").unwrap(); let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name); - let uplift = matches.contains_id("uplift"); + let uplift = matches.get_flag("uplift"); update_lints::rename(old_name, new_name, uplift); }, Some(("deprecate", matches)) => { @@ -110,24 +110,37 @@ fn get_clap_config() -> ArgMatches { Command::new("bless").about("bless the test output changes").arg( Arg::new("ignore-timestamp") .long("ignore-timestamp") + .action(ArgAction::SetTrue) .help("Include files updated before clippy was built"), ), Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix").long("fix").help("Apply the suggestions when possible"), + Arg::new("fix") + .long("fix") + .action(ArgAction::SetTrue) + .help("Apply the suggestions when possible"), Arg::new("allow-dirty") .long("allow-dirty") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has changes") .requires("fix"), Arg::new("allow-staged") .long("allow-staged") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has staged changes") .requires("fix"), ]), Command::new("fmt") .about("Run rustfmt on all projects and tests") .args([ - Arg::new("check").long("check").help("Use the rustfmt --check option"), - Arg::new("verbose").short('v').long("verbose").help("Echo commands run"), + Arg::new("check") + .long("check") + .action(ArgAction::SetTrue) + .help("Use the rustfmt --check option"), + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::SetTrue) + .help("Echo commands run"), ]), Command::new("update_lints") .about("Updates lint registration and information from the source code") @@ -140,13 +153,17 @@ fn get_clap_config() -> ArgMatches { * all lints are registered in the lint store", ) .args([ - Arg::new("print-only").long("print-only").help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), + Arg::new("print-only") + .long("print-only") + .action(ArgAction::SetTrue) + .help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + ), Arg::new("check") .long("check") + .action(ArgAction::SetTrue) .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ]), Command::new("new_lint") @@ -156,15 +173,13 @@ fn get_clap_config() -> ArgMatches { .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") - .takes_value(true) - .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) + .value_parser(["early", "late"]) .conflicts_with("type") .required_unless_present("type"), Arg::new("name") .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .takes_value(true) .required(true), Arg::new("category") .short('c') @@ -172,25 +187,23 @@ fn get_clap_config() -> ArgMatches { .help("What category the lint belongs to") .default_value("nursery") .value_parser([ - PossibleValue::new("style"), - PossibleValue::new("correctness"), - PossibleValue::new("suspicious"), - PossibleValue::new("complexity"), - PossibleValue::new("perf"), - PossibleValue::new("pedantic"), - PossibleValue::new("restriction"), - PossibleValue::new("cargo"), - PossibleValue::new("nursery"), - PossibleValue::new("internal"), - PossibleValue::new("internal_warn"), - ]) - .takes_value(true), - Arg::new("type") - .long("type") - .help("What directory the lint belongs in") - .takes_value(true) - .required(false), - Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + "internal_warn", + ]), + Arg::new("type").long("type").help("What directory the lint belongs in"), + Arg::new("msrv") + .long("msrv") + .action(ArgAction::SetTrue) + .help("Add MSRV config code to the lint"), ]), Command::new("setup") .about("Support for setting up your personal development environment") @@ -201,13 +214,12 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the dependencies added with 'cargo dev setup intellij'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the dependencies added with 'cargo dev setup intellij'"), Arg::new("rustc-repo-path") .long("repo-path") .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") - .takes_value(true) .value_name("path") .conflicts_with("remove") .required(true), @@ -217,26 +229,26 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of an existing git pre-commit hook") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of an existing git pre-commit hook"), ]), Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .args([ Arg::new("remove") .long("remove") - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of existing vscode tasks") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of existing vscode tasks"), ]), ]), Command::new("remove") @@ -295,6 +307,7 @@ fn get_clap_config() -> ArgMatches { .help("The new name of the lint"), Arg::new("uplift") .long("uplift") + .action(ArgAction::SetTrue) .help("This lint will be uplifted into rustc"), ]), Command::new("deprecate").about("Deprecates the given lint").args([ @@ -305,8 +318,6 @@ fn get_clap_config() -> ArgMatches { Arg::new("reason") .long("reason") .short('r') - .required(false) - .takes_value(true) .help("The reason for deprecation"), ]), ]) diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 4c40483e3ec..796f1ff1695 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2021" [dependencies] -cargo_metadata = "0.14" +cargo_metadata = "0.15.3" clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index 556fa579000..1d9096ea64d 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -34,14 +35,16 @@ declare_clippy_lint! { declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); -fn is_bool_lit(e: &Expr<'_>) -> bool { - matches!( - e.kind, - ExprKind::Lit(Lit { - node: LitKind::Bool(_), - .. - }) - ) && !e.span.from_expansion() +fn extract_bool_lit(e: &Expr<'_>) -> Option<bool> { + if let ExprKind::Lit(Lit { + node: LitKind::Bool(b), .. + }) = e.kind + && !e.span.from_expansion() + { + Some(b) + } else { + None + } } fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -69,24 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; let macro_name = cx.tcx.item_name(macro_call.def_id); - if !matches!( - macro_name.as_str(), - "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" - ) { - return; - } + let eq_macro = match macro_name.as_str() { + "assert_eq" | "debug_assert_eq" => true, + "assert_ne" | "debug_assert_ne" => false, + _ => return, + }; let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; let a_span = a.span.source_callsite(); let b_span = b.span.source_callsite(); - let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) { - // assert_eq!(true, b) - // ^^^^^^ - (true, false) => (a_span.until(b_span), b), - // assert_eq!(a, true) - // ^^^^^^ - (false, true) => (b_span.with_lo(a_span.hi()), a), + let (lit_span, bool_value, non_lit_expr) = match (extract_bool_lit(a), extract_bool_lit(b)) { + // assert_eq!(true/false, b) + // ^^^^^^^^^^^^ + (Some(bool_value), None) => (a_span.until(b_span), bool_value, b), + // assert_eq!(a, true/false) + // ^^^^^^^^^^^^ + (None, Some(bool_value)) => (b_span.with_lo(a_span.hi()), bool_value, a), // If there are two boolean arguments, we definitely don't understand // what's going on, so better leave things as is... // @@ -121,9 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { // ^^^^^^^^^ let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())]; + + if bool_value ^ eq_macro { + let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return }; + suggestions.push((non_lit_expr.span, (!sugg).to_string())); + } + diag.multipart_suggestion( format!("replace it with `{non_eq_mac}!(..)`"), - vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())], + suggestions, Applicability::MachineApplicable, ); }, diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 36a366fc974..457a25826e7 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -156,6 +156,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, crate::exit::EXIT_INFO, crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, crate::float_literal::EXCESSIVE_PRECISION_INFO, crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 9d089fcad70..aef2db38583 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; +use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs new file mode 100644 index 00000000000..2fdd8a71466 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -0,0 +1,178 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::MultiSpan; +use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; +use rustc_hir::{ + GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{def_id::DefId, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters in generics that are never used anywhere else. + /// + /// ### Why is this bad? + /// Functions cannot infer the value of unused type parameters; therefore, calling them + /// requires using a turbofish, which serves no purpose but to satisfy the compiler. + /// + /// ### Example + /// ```rust + /// // unused type parameters + /// fn unused_ty<T>(x: u8) { + /// // .. + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn no_unused_ty(x: u8) { + /// // .. + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub EXTRA_UNUSED_TYPE_PARAMETERS, + complexity, + "unused type parameters in function definitions" +} +declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); + +/// A visitor struct that walks a given function and gathers generic type parameters, plus any +/// trait bounds those parameters have. +struct TypeWalker<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// Collection of all the type parameters and their spans. + ty_params: FxHashMap<DefId, Span>, + /// Collection of any (inline) trait bounds corresponding to each type parameter. + bounds: FxHashMap<DefId, Span>, + /// The entire `Generics` object of the function, useful for querying purposes. + generics: &'tcx Generics<'tcx>, + /// The value of this will remain `true` if *every* parameter: + /// 1. Is a type parameter, and + /// 2. Goes unused in the function. + /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic + /// parameters are present, this will be set to `false`. + all_params_unused: bool, +} + +impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self { + let mut all_params_unused = true; + let ty_params = generics + .params + .iter() + .filter_map(|param| { + if let GenericParamKind::Type { .. } = param.kind { + Some((param.def_id.into(), param.span)) + } else { + if !param.is_elided_lifetime() { + all_params_unused = false; + } + None + } + }) + .collect(); + Self { + cx, + ty_params, + bounds: FxHashMap::default(), + generics, + all_params_unused, + } + } + + fn emit_lint(&self) { + let (msg, help) = match self.ty_params.len() { + 0 => return, + 1 => ( + "type parameter goes unused in function definition", + "consider removing the parameter", + ), + _ => ( + "type parameters go unused in function definition", + "consider removing the parameters", + ), + }; + + let source_map = self.cx.tcx.sess.source_map(); + let span = if self.all_params_unused { + self.generics.span.into() // Remove the entire list of generics + } else { + MultiSpan::from_spans( + self.ty_params + .iter() + .map(|(def_id, &span)| { + // Extend the span past any trait bounds, and include the comma at the end. + let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi); + let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false); + let comma_span = source_map.span_through_char(comma_range, ','); + span.with_hi(comma_span.hi()) + }) + .collect(), + ) + }; + + span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help); + } +} + +impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + if let Some((def_id, _)) = t.peel_refs().as_generic_param() { + if self.ty_params.remove(&def_id).is_some() { + self.all_params_unused = false; + } + } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls + // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're + // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. + let item = self.nested_visit_map().item(id); + walk_item(self, item); + } else { + walk_ty(self, t); + } + } + + fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { + if let WherePredicate::BoundPredicate(predicate) = predicate { + // Collect spans for bounds that appear in the list of generics (not in a where-clause) + // for use in forming the help message + if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() + && let PredicateOrigin::GenericParam = predicate.origin + { + self.bounds.insert(def_id, predicate.span); + } + // Only walk the right-hand side of where-bounds + for bound in predicate.bounds { + walk_param_bound(self, bound); + } + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} + +impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn(_, generics, _) = item.kind { + let mut walker = TypeWalker::new(cx, generics); + walk_item(&mut walker, item); + walker.emit_lint(); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + // Only lint on inherent methods, not trait methods. + if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + let mut walker = TypeWalker::new(cx, item.generics); + walk_impl_item(&mut walker, item); + walker.emit_lint(); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index d376ad3bfe3..ea26b96ee07 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -125,7 +125,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - style, + pedantic, "using non-inlined variables in `format!` calls" } 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 d6b50537c2e..8b53ee68ebd 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -10,13 +10,7 @@ use std::iter; use super::MISNAMED_GETTERS; -pub fn check_fn( - cx: &LateContext<'_>, - kind: FnKind<'_>, - decl: &FnDecl<'_>, - body: &Body<'_>, - span: Span, -) { +pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, span: Span) { let FnKind::Method(ref ident, sig) = kind else { return; }; 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 a13909a2cdb..f2aa7b597a7 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 @@ -25,7 +25,7 @@ pub(super) fn check_fn<'tcx>( intravisit::FnKind::Closure => return, }; - check_raw_ptr(cx, unsafety, decl, body, def_id) + check_raw_ptr(cx, unsafety, decl, body, def_id); } pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 7557a9ce13f..c924d7361ce 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -66,7 +66,9 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self.has_implicit_self() { - let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); + let ret_ty = cx + .tcx + .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); let ret_ty = cx .tcx .try_normalize_erasing_regions(cx.param_env, ret_ty) diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 80ed2862a41..e13bc47973b 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -135,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if item.ident.name == sym::len; if let ImplItemKind::Fn(sig, _) = &item.kind; if sig.decl.implicit_self.has_implicit_self(); + if sig.decl.inputs.len() == 1; if cx.effective_visibilities.is_exported(item.owner_id.def_id); if matches!(sig.decl.output, FnRetTy::Return(_)); if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); @@ -196,7 +197,15 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool { item.ident.name == name && if let AssocItemKind::Fn { has_self } = item.kind { - has_self && { cx.tcx.fn_sig(item.id.owner_id).skip_binder().inputs().skip_binder().len() == 1 } + has_self && { + cx.tcx + .fn_sig(item.id.owner_id) + .skip_binder() + .inputs() + .skip_binder() + .len() + == 1 + } } else { false } @@ -342,7 +351,11 @@ fn check_for_is_empty<'tcx>( ), Some(is_empty) if !(is_empty.fn_has_self_parameter - && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output)) => + && check_is_empty_sig( + cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), + self_kind, + output, + )) => { ( format!( diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d0683039776..565c5b7af00 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -121,6 +121,7 @@ mod excessive_bools; mod exhaustive_items; mod exit; mod explicit_write; +mod extra_unused_type_parameters; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; @@ -909,6 +910,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); + store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 747a94ba5a6..43a1a65a43a 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,20 +1,21 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; -use rustc_hir::lang_items; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, + Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -33,8 +34,6 @@ declare_clippy_lint! { /// ### Known problems /// - We bail out if the function has a `where` clause where lifetimes /// are mentioned due to potential false positives. - /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the - /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// ### Example /// ```rust @@ -92,7 +91,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, generics, id) = item.kind { - check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -105,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); check_fn_inner( cx, - sig.decl, + sig, Some(id), None, item.generics, @@ -121,29 +120,21 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); } } } -/// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -enum RefLt { - Unnamed, - Static, - Named(LocalDefId), -} - fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, - decl: &'tcx FnDecl<'_>, + sig: &'tcx FnSig<'_>, body: Option<BodyId>, trait_sig: Option<&[Ident]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, generics) { + if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; } @@ -162,7 +153,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref, _) = *bound { @@ -189,12 +180,12 @@ fn check_fn_inner<'tcx>( } } - if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { let lts = elidable_lts .iter() // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. - .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident()) + .filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident()) .map(|ident| ident.to_string()) .collect::<Vec<_>>() .join(", "); @@ -202,21 +193,99 @@ fn check_fn_inner<'tcx>( span_lint_and_then( cx, NEEDLESS_LIFETIMES, - span.with_hi(decl.output.span().hi()), + span.with_hi(sig.decl.output.span().hi()), &format!("the following explicit lifetimes could be elided: {lts}"), |diag| { - if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) { - diag.span_help(span, "replace with `'_` in generic arguments such as here"); + if sig.header.is_async() { + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); } }, ); } if report_extra_lifetimes { - self::report_extra_lifetimes(cx, decl, generics); + self::report_extra_lifetimes(cx, sig.decl, generics); } } +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option<Vec<(Span, String)>> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::<Vec<_>>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::<Option<Vec<_>>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.hir().get_parent(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx + .sess() + .source_map() + .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace()) + .unwrap_or(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) +} + // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool { if_chain! { @@ -236,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: } } +fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option<BodyId>, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> Option<Vec<(LocalDefId, Option<Span>)>> { +) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -300,32 +376,24 @@ fn could_use_elision<'tcx>( // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { - if !allowed_lts.contains(lt) { + if let Some(id) = named_lifetime(lt) + && !allowed_lts.contains(&id) + { return None; } } // check for higher-ranked trait bounds if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() { - let allowed_lts: FxHashSet<_> = allowed_lts - .iter() - .filter_map(|lt| match lt { - RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())), - _ => None, - }) - .collect(); + let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect(); for lt in input_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } for lt in output_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } } @@ -337,15 +405,10 @@ fn could_use_elision<'tcx>( let elidable_lts = named_lifetime_occurrences(&input_lts) .into_iter() .filter_map(|(def_id, occurrences)| { - if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) { - Some(( - def_id, - input_visitor - .lifetime_generic_arg_spans - .get(&def_id) - .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id)) - .copied(), - )) + if occurrences == 1 + && (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id))) + { + Some(def_id) } else { None } @@ -353,31 +416,34 @@ fn could_use_elision<'tcx>( .collect::<Vec<_>>(); if elidable_lts.is_empty() { - None - } else { - Some(elidable_lts) + return None; } + + let usages = itertools::chain(input_lts, output_lts).collect(); + + Some((elidable_lts, usages)) } -fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> { - let mut allowed_lts = FxHashSet::default(); - for par in named_generics.iter() { - if let GenericParamKind::Lifetime { .. } = par.kind { - allowed_lts.insert(RefLt::Named(par.def_id)); - } - } - allowed_lts.insert(RefLt::Unnamed); - allowed_lts.insert(RefLt::Static); - allowed_lts +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> { + named_generics + .iter() + .filter_map(|par| { + if let GenericParamKind::Lifetime { .. } = par.kind { + Some(par.def_id) + } else { + None + } + }) + .collect() } /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve /// relative order. #[must_use] -fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { +fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { let mut occurrences = Vec::new(); for lt in lts { - if let &RefLt::Named(curr_def_id) = lt { + if let Some(curr_def_id) = named_lifetime(lt) { if let Some(pair) = occurrences .iter_mut() .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) @@ -391,12 +457,10 @@ fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { occurrences } -/// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - lts: Vec<RefLt>, - lifetime_generic_arg_spans: FxHashMap<LocalDefId, Span>, - nested_elision_site_lts: Vec<RefLt>, + lts: Vec<Lifetime>, + nested_elision_site_lts: Vec<Lifetime>, unelided_trait_object_lifetime: bool, } @@ -405,32 +469,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - lifetime_generic_arg_spans: FxHashMap::default(), nested_elision_site_lts: Vec::new(), unelided_trait_object_lifetime: false, } } - fn record(&mut self, lifetime: &Option<Lifetime>) { - if let Some(ref lt) = *lifetime { - if lt.is_static() { - self.lts.push(RefLt::Static); - } else if lt.is_anonymous() { - // Fresh lifetimes generated should be ignored. - self.lts.push(RefLt::Unnamed); - } else if let LifetimeName::Param(def_id) = lt.res { - self.lts.push(RefLt::Named(def_id)); - } - } else { - self.lts.push(RefLt::Unnamed); - } - } - - fn all_lts(&self) -> Vec<RefLt> { + fn all_lts(&self) -> Vec<Lifetime> { self.lts .iter() .chain(self.nested_elision_site_lts.iter()) - .cloned() + .copied() .collect::<Vec<_>>() } @@ -442,7 +490,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.record(&Some(*lifetime)); + self.lts.push(*lifetime); } fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) { @@ -467,11 +515,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { walk_item(self, item); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res { - RefLt::Named(def_id) - } else { - RefLt::Unnamed - }), + GenericArg::Lifetime(&l) => Some(l), _ => None, })); }, @@ -491,13 +535,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { _ => walk_ty(self, ty), } } - - fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { - if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { - self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); - } - walk_generic_arg(self, generic_arg); - } } /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to @@ -521,8 +558,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { - return true; + for lt in visitor.all_lts() { + if let Some(id) = named_lifetime(<) + && !allowed_lts.contains(&id) + { + return true; + } } }, WherePredicate::EqPredicate(ref pred) => { diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 8e52cac4323..610a0233eee 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -61,7 +61,8 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Just iterating the collection itself makes the intent - /// more clear and is probably faster. + /// more clear and is probably faster because it eliminates + /// the bounds check that is done when indexing. /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index e6ed4ea7a5d..25a1a5842f7 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -149,7 +149,7 @@ pub(super) fn check<'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator", + "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, <item>)", ident.name)), ( diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 4277455a3a2..ce5d657bcf0 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::macros::root_macro_call; use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -38,57 +37,57 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if !matches!(cond.kind, ExprKind::Let(_)); - if !expr.span.from_expansion(); - let then = peel_blocks_with_stmt(then); - if let Some(macro_call) = root_macro_call(then.span); - if cx.tcx.item_name(macro_call.def_id) == sym::panic; - if !cx.tcx.sess.source_map().is_multiline(cond.span); - if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); + if let ExprKind::If(cond, then, None) = expr.kind + && !matches!(cond.kind, ExprKind::Let(_)) + && !expr.span.from_expansion() + && let then = peel_blocks_with_stmt(then) + && let Some(macro_call) = root_macro_call(then.span) + && cx.tcx.item_name(macro_call.def_id) == sym::panic + && !cx.tcx.sess.source_map().is_multiline(cond.span) + && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) + && let Some(panic_snippet) = panic_snippet.strip_suffix(')') + && let Some((_, format_args_snip)) = panic_snippet.split_once('(') // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just // shuffles the condition around. // Should this have a config value? - if !is_else_clause(cx.tcx, expr); - then { - let mut applicability = Applicability::MachineApplicable; - let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); - let cond = cond.peel_drop_temps(); - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block - span_lint_and_then( - cx, - MANUAL_ASSERT, - expr.span, - "only a `panic!` in `if`-then statement", - |diag| { - // comments can be noisy, do not show them to the user - if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); - } - diag.span_suggestion( - expr.span, - "try instead", - sugg, - applicability); - } - - ); + && !is_else_clause(cx.tcx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; } + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), + }; + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( + cx, + MANUAL_ASSERT, + expr.span, + "only a `panic!` in `if`-then statement", + |diag| { + // comments can be noisy, do not show them to the user + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability + ); + } + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability + ); + } + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 59de8c0384b..3126b590180 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -45,8 +45,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x)); - let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect(); + let is_external = adt_def.did().as_local().is_none(); + let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x)); + let mut missing_variants: Vec<_> = adt_def + .variants() + .iter() + .filter(|x| !(is_external && is_hidden(cx, x))) + .collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -133,7 +138,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -144,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden { + let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index fb94dfa5980..f1e8be7f2b8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1818,6 +1818,7 @@ declare_clippy_lint! { /// - `or_else` to `or` /// - `get_or_insert_with` to `get_or_insert` /// - `ok_or_else` to `ok_or` + /// - `then` to `then_some` (for msrv >= 1.62.0) /// /// ### Why is this bad? /// Using eager evaluation is shorter and simpler in some cases. diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs index fe88fa41fd9..e818f1892e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; use if_chain::if_chain; @@ -17,19 +17,31 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - let input_type = cx.typeck_results().expr_ty(expr); if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind(); if cx.tcx.is_diagnostic_item(sym::Cow, adt.did()); + then { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( + span_lint_and_then( cx, SUSPICIOUS_TO_OWNED, expr.span, &with_forced_trimmed_paths!(format!( "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" )), - "consider using, depending on intent", - format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), - app, + |diag| { + diag.span_suggestion( + expr.span, + "depending on intent, either make the Cow an Owned variant", + format!("{recv_snip}.into_owned()"), + app + ); + diag.span_suggestion( + expr.span, + "or clone the Cow itself", + format!("{recv_snip}.clone()"), + app + ); + } ); return true; } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2814c92e67a..63c575fca30 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -10,6 +10,7 @@ use hir::{ use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -66,7 +67,7 @@ declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]) impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) { return; } let mut unsafe_ops = vec![]; diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 472f52380bb..c5ea09590d3 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -25,11 +25,11 @@ declare_clippy_lint! { /// Using the dedicated functions of the `Option` type is clearer and /// more concise than an `if let` expression. /// - /// ### Known problems - /// This lint uses a deliberately conservative metric for checking - /// if the inside of either body contains breaks or continues which will - /// cause it to not suggest a fix if either block contains a loop with - /// continues or breaks contained within the loop. + /// ### Notes + /// This lint uses a deliberately conservative metric for checking if the + /// inside of either body contains loop control expressions `break` or + /// `continue` (which cannot be used within closures). If these are found, + /// this lint will not be raised. /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 8afe286fbd5..d88409c356e 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -624,7 +624,10 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: return; }; - match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i].peel_refs().kind() { + match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i] + .peel_refs() + .kind() + { ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { set_skip_flag(); }, diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 1fda58fa54d..9e6c6c73d4f 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -1,5 +1,8 @@ +use std::fmt::Display; + use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; @@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[must_use] -fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { - let offset = u32::from(offset); - let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); - let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); - assert!(start <= end); - Span::new(start, end, base.ctxt(), base.parent()) +fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescaped: &str, base: Span, offset: u8) { + let parts: Option<(_, _, &dyn Display)> = match &error { + regex_syntax::Error::Parse(e) => Some((e.span(), e.auxiliary_span(), e.kind())), + regex_syntax::Error::Translate(e) => Some((e.span(), None, e.kind())), + _ => None, + }; + + let convert_span = |regex_span: ®ex_syntax::ast::Span| { + let offset = u32::from(offset); + let start = base.lo() + BytePos(u32::try_from(regex_span.start.offset).expect("offset too large") + offset); + let end = base.lo() + BytePos(u32::try_from(regex_span.end.offset).expect("offset too large") + offset); + + Span::new(start, end, base.ctxt(), base.parent()) + }; + + if let Some((primary, auxiliary, kind)) = parts + && let Some(literal_snippet) = snippet_opt(cx, base) + && let Some(inner) = literal_snippet.get(offset as usize..) + // Only convert to native rustc spans if the parsed regex matches the + // source snippet exactly, to ensure the span offsets are correct + && inner.get(..unescaped.len()) == Some(unescaped) + { + let spans = if let Some(auxiliary) = auxiliary { + vec![convert_span(primary), convert_span(auxiliary)] + } else { + vec![convert_span(primary)] + }; + + span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}")); + } else { + span_lint_and_help( + cx, + INVALID_REGEX, + base, + &error.to_string(), + None, + "consider using a raw string literal: `r\"..\"`", + ); + } } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> { @@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => lint_syntax_error(cx, &e, r, expr.span, offset), } } } else if let Some(r) = const_str(cx, expr) { @@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()), } } } diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 8c39b4fc569..bccf421e8f3 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -78,8 +78,8 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa // We don't want to emit this lint if the `#[must_use]` attribute is already there. if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)); if cx.tcx.visibility(fn_def.to_def_id()).is_public(); - let ret_ty = return_ty(cx, owner_id.into()); - let self_arg = nth_arg(cx, owner_id.into(), 0); + let ret_ty = return_ty(cx, owner_id); + let self_arg = nth_arg(cx, owner_id, 0); // If `Self` has the same type as the returned type, then we want to warn. // // For this check, we don't want to remove the reference on the returned type because if diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 8f1d1490e1f..34a3e5ddf4f 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x); } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_INSIDE_BLOCK, restriction, "add a semicolon inside the block" @@ -59,7 +59,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x) }; /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_OUTSIDE_BLOCK, restriction, "add a semicolon outside the block" diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 585e2075fa9..c1f228d5f90 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -392,9 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let is_exported = cx - .effective_visibilities - .is_exported(field.def_id); + let is_exported = cx.effective_visibilities.is_exported(field.def_id); self.check_ty( cx, diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 92053cec59f..0e526c216be 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { + "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { check_map_error(cx, arg_0, expr); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index f48be27592b..1d78c7cfae0 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -253,7 +253,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN. /// /// The minimum rust version that the project supports (msrv: Option<String> = None), @@ -323,7 +323,7 @@ define_Conf! { /// /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit: Option<u64> = None), - /// Lint: LARGE_TYPE_PASS_BY_MOVE. + /// Lint: LARGE_TYPES_PASSED_BY_VALUE. /// /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. (pass_by_value_size_limit: u64 = 256), @@ -411,7 +411,7 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE + /// Lint: AWAIT_HOLDING_INVALID_TYPE. (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// @@ -437,7 +437,7 @@ define_Conf! { /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY. + /// Lint: MUTABLE_KEY_TYPE. /// /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability @@ -446,7 +446,7 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING + /// Lint: INDEXING_SLICING. /// /// Whether to suppress a restriction lint in constant code. In same /// cases the restructured operation might not be unavoidable, as the diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs index 01efc527a8c..092041aecf2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,5 @@ use clippy_utils::get_attr; +use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -47,6 +48,18 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir { println!("{stmt:#?}"); } } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } } fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 4c3b1b131fd..f718207654f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -215,14 +215,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, }; let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .owner_id + .def_id, ); collector.visit_expr(cx.tcx.hir().body(body_id).value); } diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index d7f466c1976..63dccbf697c 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -770,10 +770,7 @@ impl<'tcx> FormatSpec<'tcx> { /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` pub fn is_default_for_trait(&self) -> bool { - self.width.is_implied() - && self.precision.is_implied() - && self.align == Alignment::AlignUnknown - && self.no_flags + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.no_flags } } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index b8c87aa5e1e..78fb2e0eb7e 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -809,7 +809,10 @@ pub struct DerefClosure { /// /// note: this only works on single line immutable closures with exactly one input parameter. pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option<DerefClosure> { - if let hir::ExprKind::Closure(&Closure { fn_decl, def_id, body, .. }) = closure.kind { + if let hir::ExprKind::Closure(&Closure { + fn_decl, def_id, body, .. + }) = closure.kind + { let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) // a type annotation is present if param `kind` is different from `TyKind::Infer` diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index d18b62d1bf1..00073bcd82a 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -392,12 +392,16 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe) => + .map_or(false, |id| { + self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe + }) => { self.is_unsafe = true; }, ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() { - ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => self.is_unsafe = true, + ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => { + self.is_unsafe = true; + }, ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true, _ => walk_expr(self, e), }, diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index de31c16b819..653121af54d 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" publish = false [dependencies] -cargo_metadata = "0.14" -clap = "3.2" +cargo_metadata = "0.15.3" +clap = "4.1.4" crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 4e7fc565a32..adea8c53df2 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-01-27" +channel = "nightly-2023-02-10" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 2a240cc249b..c1a10ba55ef 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,19 +1,19 @@ error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `sym::Deref` + = help: convert all references to use `sym::deref_method` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | -LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `sym::deref_method` + = help: convert all references to use `sym::Deref` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 1e5f20e8c39..a13af565203 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -149,3 +149,22 @@ macro_rules! almost_complete_range { let _ = '0'..'9'; }; } + +#[macro_export] +macro_rules! unsafe_macro { + () => { + unsafe { + *core::ptr::null::<()>(); + *core::ptr::null::<()>(); + } + }; +} + +#[macro_export] +macro_rules! needless_lifetime { + () => { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; +} diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed index 95f35a61bb2..b8dd92906c8 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed @@ -86,7 +86,7 @@ fn main() { let b = ImplNotTraitWithBool; assert_eq!("a".len(), 1); - assert!("a".is_empty()); + assert!(!"a".is_empty()); assert!("".is_empty()); assert!("".is_empty()); assert_eq!(a!(), b!()); @@ -97,16 +97,16 @@ fn main() { assert_ne!("a".len(), 1); assert!("a".is_empty()); - assert!("".is_empty()); - assert!("".is_empty()); + assert!(!"".is_empty()); + assert!(!"".is_empty()); assert_ne!(a!(), b!()); assert_ne!(a!(), "".is_empty()); assert_ne!("".is_empty(), b!()); assert_ne!(a, true); - assert!(b); + assert!(!b); debug_assert_eq!("a".len(), 1); - debug_assert!("a".is_empty()); + debug_assert!(!"a".is_empty()); debug_assert!("".is_empty()); debug_assert!("".is_empty()); debug_assert_eq!(a!(), b!()); @@ -117,27 +117,27 @@ fn main() { debug_assert_ne!("a".len(), 1); debug_assert!("a".is_empty()); - debug_assert!("".is_empty()); - debug_assert!("".is_empty()); + debug_assert!(!"".is_empty()); + debug_assert!(!"".is_empty()); debug_assert_ne!(a!(), b!()); debug_assert_ne!(a!(), "".is_empty()); debug_assert_ne!("".is_empty(), b!()); debug_assert_ne!(a, true); - debug_assert!(b); + debug_assert!(!b); // assert with error messages assert_eq!("a".len(), 1, "tadam {}", 1); assert_eq!("a".len(), 1, "tadam {}", true); - assert!("a".is_empty(), "tadam {}", 1); - assert!("a".is_empty(), "tadam {}", true); - assert!("a".is_empty(), "tadam {}", true); + assert!(!"a".is_empty(), "tadam {}", 1); + assert!(!"a".is_empty(), "tadam {}", true); + assert!(!"a".is_empty(), "tadam {}", true); assert_eq!(a, true, "tadam {}", false); debug_assert_eq!("a".len(), 1, "tadam {}", 1); debug_assert_eq!("a".len(), 1, "tadam {}", true); - debug_assert!("a".is_empty(), "tadam {}", 1); - debug_assert!("a".is_empty(), "tadam {}", true); - debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert!(!"a".is_empty(), "tadam {}", 1); + debug_assert!(!"a".is_empty(), "tadam {}", true); + debug_assert!(!"a".is_empty(), "tadam {}", true); debug_assert_eq!(a, true, "tadam {}", false); assert!(a!()); @@ -158,4 +158,14 @@ fn main() { }}; } in_macro!(a); + + assert!("".is_empty()); + assert!("".is_empty()); + assert!(!"requires negation".is_empty()); + assert!(!"requires negation".is_empty()); + + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert!(!"requires negation".is_empty()); + debug_assert!(!"requires negation".is_empty()); } diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs index 88e7560b4f9..0a8ad34fda5 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs @@ -158,4 +158,14 @@ fn main() { }}; } in_macro!(a); + + assert_eq!("".is_empty(), true); + assert_ne!("".is_empty(), false); + assert_ne!("requires negation".is_empty(), true); + assert_eq!("requires negation".is_empty(), false); + + debug_assert_eq!("".is_empty(), true); + debug_assert_ne!("".is_empty(), false); + debug_assert_ne!("requires negation".is_empty(), true); + debug_assert_eq!("requires negation".is_empty(), false); } diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr index 3d9f8573e61..89cefc95a9f 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr @@ -8,7 +8,7 @@ LL | assert_eq!("a".is_empty(), false); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false); -LL + assert!("a".is_empty()); +LL + assert!(!"a".is_empty()); | error: used `assert_eq!` with a literal bool @@ -68,7 +68,7 @@ LL | assert_ne!("".is_empty(), true); help: replace it with `assert!(..)` | LL - assert_ne!("".is_empty(), true); -LL + assert!("".is_empty()); +LL + assert!(!"".is_empty()); | error: used `assert_ne!` with a literal bool @@ -80,7 +80,7 @@ LL | assert_ne!(true, "".is_empty()); help: replace it with `assert!(..)` | LL - assert_ne!(true, "".is_empty()); -LL + assert!("".is_empty()); +LL + assert!(!"".is_empty()); | error: used `assert_ne!` with a literal bool @@ -92,7 +92,7 @@ LL | assert_ne!(b, true); help: replace it with `assert!(..)` | LL - assert_ne!(b, true); -LL + assert!(b); +LL + assert!(!b); | error: used `debug_assert_eq!` with a literal bool @@ -104,7 +104,7 @@ LL | debug_assert_eq!("a".is_empty(), false); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false); -LL + debug_assert!("a".is_empty()); +LL + debug_assert!(!"a".is_empty()); | error: used `debug_assert_eq!` with a literal bool @@ -164,7 +164,7 @@ LL | debug_assert_ne!("".is_empty(), true); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!("".is_empty(), true); -LL + debug_assert!("".is_empty()); +LL + debug_assert!(!"".is_empty()); | error: used `debug_assert_ne!` with a literal bool @@ -176,7 +176,7 @@ LL | debug_assert_ne!(true, "".is_empty()); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!(true, "".is_empty()); -LL + debug_assert!("".is_empty()); +LL + debug_assert!(!"".is_empty()); | error: used `debug_assert_ne!` with a literal bool @@ -188,7 +188,7 @@ LL | debug_assert_ne!(b, true); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!(b, true); -LL + debug_assert!(b); +LL + debug_assert!(!b); | error: used `assert_eq!` with a literal bool @@ -200,7 +200,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false, "tadam {}", 1); -LL + assert!("a".is_empty(), "tadam {}", 1); +LL + assert!(!"a".is_empty(), "tadam {}", 1); | error: used `assert_eq!` with a literal bool @@ -212,7 +212,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", true); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false, "tadam {}", true); -LL + assert!("a".is_empty(), "tadam {}", true); +LL + assert!(!"a".is_empty(), "tadam {}", true); | error: used `assert_eq!` with a literal bool @@ -224,7 +224,7 @@ LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); help: replace it with `assert!(..)` | LL - assert_eq!(false, "a".is_empty(), "tadam {}", true); -LL + assert!("a".is_empty(), "tadam {}", true); +LL + assert!(!"a".is_empty(), "tadam {}", true); | error: used `debug_assert_eq!` with a literal bool @@ -236,7 +236,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); -LL + debug_assert!("a".is_empty(), "tadam {}", 1); +LL + debug_assert!(!"a".is_empty(), "tadam {}", 1); | error: used `debug_assert_eq!` with a literal bool @@ -248,7 +248,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true); -LL + debug_assert!("a".is_empty(), "tadam {}", true); +LL + debug_assert!(!"a".is_empty(), "tadam {}", true); | error: used `debug_assert_eq!` with a literal bool @@ -260,7 +260,7 @@ LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); -LL + debug_assert!("a".is_empty(), "tadam {}", true); +LL + debug_assert!(!"a".is_empty(), "tadam {}", true); | error: used `assert_eq!` with a literal bool @@ -299,5 +299,101 @@ LL - renamed!(b, true); LL + debug_assert!(b); | -error: aborting due to 25 previous errors +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:162:5 + | +LL | assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("".is_empty(), true); +LL + assert!("".is_empty()); + | + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:163:5 + | +LL | assert_ne!("".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("".is_empty(), false); +LL + assert!("".is_empty()); + | + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:164:5 + | +LL | assert_ne!("requires negation".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("requires negation".is_empty(), true); +LL + assert!(!"requires negation".is_empty()); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:165:5 + | +LL | assert_eq!("requires negation".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("requires negation".is_empty(), false); +LL + assert!(!"requires negation".is_empty()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:167:5 + | +LL | debug_assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:168:5 + | +LL | debug_assert_ne!("".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("".is_empty(), false); +LL + debug_assert!("".is_empty()); + | + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:169:5 + | +LL | debug_assert_ne!("requires negation".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("requires negation".is_empty(), true); +LL + debug_assert!(!"requires negation".is_empty()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:170:5 + | +LL | debug_assert_eq!("requires negation".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("requires negation".is_empty(), false); +LL + debug_assert!(!"requires negation".is_empty()); + | + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr index 1f26c7f4db6..c5ea0b16d1b 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr @@ -5,6 +5,11 @@ LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { +LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) { + | error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 875d5ab4f21..0b0e0ad2684 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -9,6 +9,11 @@ note: the lint level is defined here | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs new file mode 100644 index 00000000000..5cb80cb6233 --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs @@ -0,0 +1,69 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::extra_unused_type_parameters)] + +fn unused_ty<T>(x: u8) {} + +fn unused_multi<T, U>(x: u8) {} + +fn unused_with_lt<'a, T>(x: &'a u8) {} + +fn used_ty<T>(x: T, y: u8) {} + +fn used_ref<'a, T>(x: &'a T) {} + +fn used_ret<T: Default>(x: u8) -> T { + T::default() +} + +fn unused_bounded<T: Default, U>(x: U) {} + +fn unused_where_clause<T, U>(x: U) +where + T: Default, +{ +} + +fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {} + +fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize { + iter.count() +} + +fn used_ret_opaque<A>() -> impl Iterator<Item = A> { + std::iter::empty() +} + +fn used_vec_box<T>(x: Vec<Box<T>>) {} + +fn used_body<T: Default + ToString>() -> String { + T::default().to_string() +} + +fn used_closure<T: Default + ToString>() -> impl Fn() { + || println!("{}", T::default().to_string()) +} + +struct S; + +impl S { + fn unused_ty_impl<T>(&self) {} +} + +// Don't lint on trait methods +trait Foo { + fn bar<T>(&self); +} + +impl Foo for S { + fn bar<T>(&self) {} +} + +fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A> +where + Iter: Iterator<Item = A>, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr new file mode 100644 index 00000000000..1c8dd53e638 --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr @@ -0,0 +1,59 @@ +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:4:13 + | +LL | fn unused_ty<T>(x: u8) {} + | ^^^ + | + = help: consider removing the parameter + = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings` + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:6:16 + | +LL | fn unused_multi<T, U>(x: u8) {} + | ^^^^^^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:8:23 + | +LL | fn unused_with_lt<'a, T>(x: &'a u8) {} + | ^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:18:19 + | +LL | fn unused_bounded<T: Default, U>(x: U) {} + | ^^^^^^^^^^^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:20:24 + | +LL | fn unused_where_clause<T, U>(x: U) + | ^^ + | + = help: consider removing the parameter + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:26:16 + | +LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {} + | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:49:22 + | +LL | fn unused_ty_impl<T>(&self) {} + | ^^^ + | + = help: consider removing the parameter + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs index 78397c2af34..b5dec6c46bd 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.rs +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -282,4 +282,50 @@ impl AsyncLen { } } +// issue #9520 +pub struct NonStandardLenAndIsEmptySignature; +impl NonStandardLenAndIsEmptySignature { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } + + pub fn is_empty(&self, something: usize) -> bool { + something == 0 + } +} + +// test case for #9520 with generics in the function signature +pub trait TestResource { + type NonStandardSignatureWithGenerics: Copy; + fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result<Option<&[u8]>, String>; +} +pub struct NonStandardSignatureWithGenerics(u32); +impl NonStandardSignatureWithGenerics { + pub fn is_empty<T, U>(self, resource: &T) -> bool + where + T: TestResource<NonStandardSignatureWithGenerics = U>, + U: Copy + From<NonStandardSignatureWithGenerics>, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.is_empty() + } else { + true + } + } + + // test case for #9520 with generics in the function signature + pub fn len<T, U>(self, resource: &T) -> usize + where + T: TestResource<NonStandardSignatureWithGenerics = U>, + U: Copy + From<NonStandardSignatureWithGenerics>, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.len() + } else { + 0_usize + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed index 638320dd6ee..8c7e919bf62 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed @@ -29,9 +29,7 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - if !a.is_empty() { - panic!("qwqwq"); - } + assert!(a.is_empty(), "qwqwq"); if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -46,21 +44,11 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - if b.is_empty() { - panic!("panic1"); - } - if b.is_empty() && a.is_empty() { - panic!("panic2"); - } - if a.is_empty() && !b.is_empty() { - panic!("panic3"); - } - if b.is_empty() || a.is_empty() { - panic!("panic4"); - } - if a.is_empty() || !b.is_empty() { - panic!("panic5"); - } + assert!(!b.is_empty(), "panic1"); + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); if a.is_empty() { let _ = 0; @@ -71,12 +59,11 @@ fn main() { fn issue7730(a: u8) { // Suggestion should preserve comment - if a > 2 { - // comment - /* this is a + // comment +/* this is a multiline comment */ - /// Doc comment - panic!("panic with comment") // comment after `panic!` - } +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); } diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr index 1f2e1e3087b..3555ac29243 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr @@ -9,6 +9,54 @@ LL | | } = note: `-D clippy::manual-assert` implied by `-D warnings` error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:34:5 + | +LL | / if !a.is_empty() { +LL | | panic!("qwqwq"); +LL | | } + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:51:5 + | +LL | / if b.is_empty() { +LL | | panic!("panic1"); +LL | | } + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:54:5 + | +LL | / if b.is_empty() && a.is_empty() { +LL | | panic!("panic2"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:57:5 + | +LL | / if a.is_empty() && !b.is_empty() { +LL | | panic!("panic3"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:60:5 + | +LL | / if b.is_empty() || a.is_empty() { +LL | | panic!("panic4"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:63:5 + | +LL | / if a.is_empty() || !b.is_empty() { +LL | | panic!("panic5"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + +error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { @@ -16,5 +64,22 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: aborting due to 2 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:78:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed index fc252cdd352..9fd3739b69c 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed @@ -123,7 +123,7 @@ fn main() { Enum::A => (), Enum::B => (), Enum::C => (), - _ => (), + Enum::__Private => (), } match Enum::A { Enum::A => (), diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr index 6fa313dc911..105b4c4b41d 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr @@ -49,10 +49,16 @@ LL | _ => (), | ^ help: try this: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:126:13 + | +LL | _ => (), + | ^ help: try this: `Enum::__Private` + +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:153:13 | LL | _ => 2, | ^ help: try this: `Foo::B` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 41263535df6..4511bc99c3c 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,9 +1,13 @@ +// aux-build:macro_rules.rs #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::drop_copy)] #![warn(clippy::multiple_unsafe_ops_per_block)] +#[macro_use] +extern crate macro_rules; + use core::arch::asm; fn raw_ptr() -> *const () { @@ -107,4 +111,9 @@ unsafe fn read_char_good(ptr: *const u8) -> char { unsafe { core::char::from_u32_unchecked(int_value) } } +// no lint +fn issue10259() { + unsafe_macro!(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr index f6b8341795d..303aeb7aee0 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:32:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:36:5 | LL | / unsafe { LL | | STATIC += 1; @@ -8,19 +8,19 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:33:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:37:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:34:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:38:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:41:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:45:5 | LL | / unsafe { LL | | drop(u.u); @@ -29,18 +29,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:42:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:46:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:43:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:47:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:48:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:52:5 | LL | / unsafe { LL | | asm!("nop"); @@ -50,23 +50,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:49:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:53:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:50:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:54:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:51:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:55:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:57:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:61:5 | LL | / unsafe { LL | | drop(u.u); @@ -78,49 +78,49 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:58:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:62:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:59:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:63:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:60:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:64:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:61:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:65:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:62:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:66:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:63:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:67:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:101:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:5 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:14 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:39 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:39 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed new file mode 100644 index 00000000000..d286ef4ba37 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -0,0 +1,538 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::needless_lifetimes)] +#![allow( + unused, + clippy::boxed_local, + clippy::extra_unused_type_parameters, + clippy::needless_pass_by_value, + clippy::unnecessary_wraps, + dyn_drop, + clippy::get_first +)] + +#[macro_use] +extern crate macro_rules; + +fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + +fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out(x: &u8, _y: u8) -> &u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + y +} + +// No error; multiple input refs +async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { + args.get(0).cloned() +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { x } else { f() } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out(&self) -> &u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + x + } + + fn distinct_self_and_in(&self, _x: &u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt(_foo: Foo<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt(_foo: FooAlias<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + unimplemented!() +} + +fn named_input_elided_output(_arg: &str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec<usize>, +} + +impl Test { + fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts(e: &str) -> Cow<'_> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt(x: &u8) {} + } + + impl BadTrait for () { + fn needless_lt(_x: &u8) {} + } +} + +mod issue2944 { + trait Foo {} + struct Bar; + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz(&self) -> impl Foo + '_ { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + +mod issue7296 { + use std::rc::Rc; + use std::sync::Arc; + + struct Foo; + impl Foo { + fn implicit(&self) -> &() { + &() + } + fn implicit_mut(&mut self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc<Self>) -> &'a () { + &() + } + fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() { + &() + } + } + + trait Bar { + fn implicit(&self) -> &(); + fn implicit_provided(&self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc<Self>) -> &'a (); + fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &(); + fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() { + &() + } + } +} + +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo(x: &u8, y: &'_ u8) {} + + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 2efc936752e..409528b291d 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -1,13 +1,20 @@ +// run-rustfix +// aux-build:macro_rules.rs + #![warn(clippy::needless_lifetimes)] #![allow( - dead_code, + unused, clippy::boxed_local, + clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop, clippy::get_first )] +#[macro_use] +extern crate macro_rules; + fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} @@ -495,4 +502,37 @@ mod pr_9743_output_lifetime_checks { } } +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index 5a7cf13c86d..4e3c8f20d8c 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -1,316 +1,559 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:11:1 + --> $DIR/needless_lifetimes.rs:18:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} +LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:13:1 + --> $DIR/needless_lifetimes.rs:20:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} +LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:23:1 + --> $DIR/needless_lifetimes.rs:30:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { +LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:35:1 + --> $DIR/needless_lifetimes.rs:42:1 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:42:1 + --> $DIR/needless_lifetimes.rs:49:1 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { +LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:59:1 + --> $DIR/needless_lifetimes.rs:66:1 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:66:1 + --> $DIR/needless_lifetimes.rs:73:1 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { +LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:75:1 + --> $DIR/needless_lifetimes.rs:82:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:80:1 + --> $DIR/needless_lifetimes.rs:87:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +LL + fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()> + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:92:1 + --> $DIR/needless_lifetimes.rs:99:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:92:37 +help: elide the lifetimes + | +LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} +LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | -LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:116:1 + --> $DIR/needless_lifetimes.rs:123:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:116:32 +help: elide the lifetimes + | +LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +LL + fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^ error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:146:5 + --> $DIR/needless_lifetimes.rs:153:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_out<'s>(&'s self) -> &'s u8 { +LL + fn self_and_out(&self) -> &u8 { + | error: the following explicit lifetimes could be elided: 't - --> $DIR/needless_lifetimes.rs:153:5 + --> $DIR/needless_lifetimes.rs:160:5 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { +LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + | error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:160:5 + --> $DIR/needless_lifetimes.rs:167:5 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { +LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + | error: the following explicit lifetimes could be elided: 's, 't - --> $DIR/needless_lifetimes.rs:164:5 + --> $DIR/needless_lifetimes.rs:171:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} +LL + fn distinct_self_and_in(&self, _x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:183:1 + --> $DIR/needless_lifetimes.rs:190:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:183:33 +help: elide the lifetimes + | +LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { +LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | -LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:201:1 + --> $DIR/needless_lifetimes.rs:208:1 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:201:43 +help: elide the lifetimes + | +LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | -LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:209:1 + --> $DIR/needless_lifetimes.rs:216:1 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { +LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:224:1 + --> $DIR/needless_lifetimes.rs:231:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { +LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:230:1 + --> $DIR/needless_lifetimes.rs:237:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:230:37 +help: elide the lifetimes + | +LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { +LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | -LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:248:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:248:47 +help: elide the lifetimes + | +LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | -LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:256:1 + --> $DIR/needless_lifetimes.rs:263:1 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { +LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:260:1 + --> $DIR/needless_lifetimes.rs:267:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str { +LL + fn named_input_elided_output(_arg: &str) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:268:1 + --> $DIR/needless_lifetimes.rs:275:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { +LL + fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:304:1 + --> $DIR/needless_lifetimes.rs:311:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:304:47 +help: elide the lifetimes + | +LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { +LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | -LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:311:9 + --> $DIR/needless_lifetimes.rs:318:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(x: &'a u8) {} +LL + fn needless_lt(x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:315:9 + --> $DIR/needless_lifetimes.rs:322:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(_x: &'a u8) {} +LL + fn needless_lt(_x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:328:9 + --> $DIR/needless_lifetimes.rs:335:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:360:5 + --> $DIR/needless_lifetimes.rs:367:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { +LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:369:5 + --> $DIR/needless_lifetimes.rs:376:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { +LL + fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:381:5 + --> $DIR/needless_lifetimes.rs:388:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 +LL + fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32 + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:396:5 + --> $DIR/needless_lifetimes.rs:403:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { +LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:409:5 + --> $DIR/needless_lifetimes.rs:416:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { +LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:412:5 + --> $DIR/needless_lifetimes.rs:419:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { +LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:434:9 + --> $DIR/needless_lifetimes.rs:441:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a () { +LL + fn implicit(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:437:9 + --> $DIR/needless_lifetimes.rs:444:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_mut<'a>(&'a mut self) -> &'a () { +LL + fn implicit_mut(&mut self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:448:9 + --> $DIR/needless_lifetimes.rs:455:9 | LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:454:9 + --> $DIR/needless_lifetimes.rs:461:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a (); +LL + fn implicit(&self) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:455:9 + --> $DIR/needless_lifetimes.rs:462:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_provided<'a>(&'a self) -> &'a () { +LL + fn implicit_provided(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:464:9 + --> $DIR/needless_lifetimes.rs:471:9 | LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a (); +LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:465:9 + --> $DIR/needless_lifetimes.rs:472:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:474:5 + --> $DIR/needless_lifetimes.rs:481:5 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn foo<'a>(x: &'a u8, y: &'_ u8) {} +LL + fn foo(x: &u8, y: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:476:5 + --> $DIR/needless_lifetimes.rs:483:5 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:483:5 + --> $DIR/needless_lifetimes.rs:490:5 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:488:5 + --> $DIR/needless_lifetimes.rs:495:5 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { +LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:508:13 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | local_one_input_macro!(); + | ------------------------ in this macro invocation + | + = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | -error: aborting due to 45 previous errors +error: aborting due to 46 previous errors diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr index b31544ec334..cffa19bec3a 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..10 { | ^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter_mut().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs index 65809023f8d..7803418cb04 100644 --- a/src/tools/clippy/tests/ui/new_without_default.rs +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] +#![allow( + dead_code, + clippy::missing_safety_doc, + clippy::extra_unused_lifetimes, + clippy::extra_unused_type_parameters +)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr index 212a69ab94e..583dd327d6a 100644 --- a/src/tools/clippy/tests/ui/new_without_default.stderr +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:7:5 + --> $DIR/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | Foo @@ -17,7 +17,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> $DIR/new_without_default.rs:15:5 + --> $DIR/new_without_default.rs:20:5 | LL | / pub fn new() -> Self { LL | | Bar @@ -34,7 +34,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> $DIR/new_without_default.rs:79:5 + --> $DIR/new_without_default.rs:84:5 | LL | / pub fn new() -> LtKo<'c> { LL | | unimplemented!() @@ -51,7 +51,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> $DIR/new_without_default.rs:172:5 + --> $DIR/new_without_default.rs:177:5 | LL | / pub fn new() -> Self { LL | | NewNotEqualToDerive { foo: 1 } @@ -68,7 +68,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics<T>` - --> $DIR/new_without_default.rs:180:5 + --> $DIR/new_without_default.rs:185:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -85,7 +85,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics<T>` - --> $DIR/new_without_default.rs:187:5 + --> $DIR/new_without_default.rs:192:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -102,7 +102,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo<T>` - --> $DIR/new_without_default.rs:198:9 + --> $DIR/new_without_default.rs:203:9 | LL | / pub fn new() -> Self { LL | | todo!() diff --git a/src/tools/clippy/tests/ui/redundant_field_names.fixed b/src/tools/clippy/tests/ui/redundant_field_names.fixed index ec7f8ae923a..276266a2dd8 100644 --- a/src/tools/clippy/tests/ui/redundant_field_names.fixed +++ b/src/tools/clippy/tests/ui/redundant_field_names.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/src/tools/clippy/tests/ui/redundant_field_names.rs b/src/tools/clippy/tests/ui/redundant_field_names.rs index 73122016cf6..f674141c138 100644 --- a/src/tools/clippy/tests/ui/redundant_field_names.rs +++ b/src/tools/clippy/tests/ui/redundant_field_names.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index f0e1a8128d7..ab8ac97a0e7 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -36,6 +36,10 @@ fn syntax_error() { let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + + let escaped_string_span = Regex::new("\\b\\c"); + + let aux_span = Regex::new("(?ixi)"); } fn trivial_regex() { diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr index 2424644c6f6..c2440f39e0a 100644 --- a/src/tools/clippy/tests/ui/regex.stderr +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -29,7 +29,10 @@ error: regex syntax error: invalid character class range, the start must be <= t LL | let some_unicode = Regex::new("[é-è]"); | ^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:18:33 | LL | let some_regex = Regex::new(OPENING_PAREN); @@ -43,25 +46,37 @@ LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | = help: the regex is unlikely to be useful as it is -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:21:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:22:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:34:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:35:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); @@ -79,8 +94,25 @@ error: regex syntax error: unrecognized escape sequence LL | let raw_string_error = Regex::new(r#"[...//...]"#); | ^^ +error: regex parse error: + /b/c + ^^ + error: unrecognized escape sequence + --> $DIR/regex.rs:40:42 + | +LL | let escaped_string_span = Regex::new("/b/c"); + | ^^^^^^^^ + | + = help: consider using a raw string literal: `r".."` + +error: regex syntax error: duplicate flag + --> $DIR/regex.rs:42:34 + | +LL | let aux_span = Regex::new("(?ixi)"); + | ^ ^ + error: trivial regex - --> $DIR/regex.rs:42:33 + --> $DIR/regex.rs:46:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:44:48 + --> $DIR/regex.rs:48:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:46:42 + --> $DIR/regex.rs:50:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -104,7 +136,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:48:40 + --> $DIR/regex.rs:52:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -112,7 +144,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:50:39 + --> $DIR/regex.rs:54:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -120,7 +152,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:52:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -128,7 +160,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:58:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -136,7 +168,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:57:36 + --> $DIR/regex.rs:61:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -144,7 +176,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:59:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -152,7 +184,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -160,12 +192,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:63:44 + --> $DIR/regex.rs:67:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed index 713cff604a1..dc24d447c60 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs index 467003a1a66..4adde2c4018 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr index dec3f50d6f1..c4ec7aa88a2 100644 --- a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr +++ b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr @@ -2,27 +2,62 @@ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause th --> $DIR/suspicious_to_owned.rs:16:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ | = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned --> $DIR/suspicious_to_owned.rs:26:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, Vec<char>> itself and does not cause the Cow<'_, Vec<char>> contents to become owned --> $DIR/suspicious_to_owned.rs:36:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:46:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type --> $DIR/suspicious_to_owned.rs:60:13 diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index 2eca1f4701c..8b4613b3f6e 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -1,4 +1,5 @@ #![deny(clippy::type_repetition_in_bounds)] +#![allow(clippy::extra_unused_type_parameters)] use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index 70d700c1cc4..a90df03c04f 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:8:5 + --> $DIR/type_repetition_in_bounds.rs:9:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:25:5 + --> $DIR/type_repetition_in_bounds.rs:26:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:85:5 + --> $DIR/type_repetition_in_bounds.rs:86:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:90:5 + --> $DIR/type_repetition_in_bounds.rs:91:5 | LL | T: ?Sized, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index 4b059558173..8d3e094b759 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -63,6 +63,14 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +fn is_ok_err<T: io::Read + io::Write>(s: &mut T) { + s.write(b"ok").is_ok(); + s.write(b"err").is_err(); + let mut buf = [0u8; 0]; + s.read(&mut buf).is_ok(); + s.read(&mut buf).is_err(); +} + async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) { w.write(b"hello world").await.unwrap(); } diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr index 7ba7e09c0f0..0865c5213f6 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.stderr +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -82,13 +82,45 @@ LL | | .expect("error"); error: written amount is not handled --> $DIR/unused_io_amount.rs:67:5 | +LL | s.write(b"ok").is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:68:5 + | +LL | s.write(b"err").is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:70:5 + | +LL | s.read(&mut buf).is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:71:5 + | +LL | s.read(&mut buf).is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:75:5 + | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:72:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:85:9 + --> $DIR/unused_io_amount.rs:93:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:93:9 + --> $DIR/unused_io_amount.rs:101:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:101:5 + --> $DIR/unused_io_amount.rs:109:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:106:5 + --> $DIR/unused_io_amount.rs:114:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed index 23607497841..293bf75a717 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed @@ -96,7 +96,7 @@ fn main() { } match Enum::A { Enum::A => (), - Enum::B | _ => (), + Enum::B | Enum::__Private => (), } } } diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr index efecc9576cc..30d29aa4e77 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr @@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` -error: wildcard matches known variants and will also match future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | _` + | ^ help: try this: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 45fd87bea9b..e11ebca6ea9 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -941,6 +941,7 @@ pub fn make_test_description<R: Read>( let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target); let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target); + let has_xray = util::XRAY_SUPPORTED_TARGETS.contains(&&*config.target); // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find // whether `rust-lld` is present in the compiler under test. @@ -1019,6 +1020,7 @@ pub fn make_test_description<R: Read>( && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack") ); reason!(!config.can_unwind() && config.parse_name_directive(ln, "needs-unwind")); + reason!(!has_xray && config.parse_name_directive(ln, "needs-xray")); reason!( config.target == "wasm32-unknown-unknown" && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index ff7e8df9878..67f49bb6397 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -78,6 +78,19 @@ pub const MEMTAG_SUPPORTED_TARGETS: &[&str] = pub const SHADOWCALLSTACK_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android"]; +pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-linux-android", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "x86_64-linux-android", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64-unknown-netbsd", + "x86_64-unknown-none-linuxkernel", + "x86_64-unknown-openbsd", +]; + pub fn make_new_path(path: &str) -> String { assert!(cfg!(windows)); // Windows just uses PATH as the library search path, so we have to diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 83551a1d820..ef3abb9514f 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -9,7 +9,7 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. -const ROOT_ENTRY_LIMIT: usize = 939; +const ROOT_ENTRY_LIMIT: usize = 940; const ISSUES_ENTRY_LIMIT: usize = 2001; fn check_entries(path: &Path, bad: &mut bool) { diff --git a/tests/codegen/instrument-xray/basic.rs b/tests/codegen/instrument-xray/basic.rs new file mode 100644 index 00000000000..d3e49d53174 --- /dev/null +++ b/tests/codegen/instrument-xray/basic.rs @@ -0,0 +1,9 @@ +// Checks that `-Z instrument-xray` produces expected instrumentation. +// +// needs-xray +// compile-flags: -Z instrument-xray=always + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "function-instrument"="xray-always" +pub fn function() {} diff --git a/tests/codegen/instrument-xray/options-combine.rs b/tests/codegen/instrument-xray/options-combine.rs new file mode 100644 index 00000000000..f7e500b65f6 --- /dev/null +++ b/tests/codegen/instrument-xray/options-combine.rs @@ -0,0 +1,12 @@ +// Checks that `-Z instrument-xray` options can be specified multiple times. +// +// needs-xray +// compile-flags: -Z instrument-xray=skip-exit +// compile-flags: -Z instrument-xray=instruction-threshold=123 +// compile-flags: -Z instrument-xray=instruction-threshold=456 + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "xray-instruction-threshold"="456" "xray-skip-exit" +// CHECK-NOT: attributes #{{.*}} "xray-instruction-threshold"="123" +pub fn function() {} diff --git a/tests/codegen/instrument-xray/options-override.rs b/tests/codegen/instrument-xray/options-override.rs new file mode 100644 index 00000000000..00f81837902 --- /dev/null +++ b/tests/codegen/instrument-xray/options-override.rs @@ -0,0 +1,11 @@ +// Checks that the last `-Z instrument-xray` option wins. +// +// needs-xray +// compile-flags: -Z instrument-xray=always +// compile-flags: -Z instrument-xray=never + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "function-instrument"="xray-never" +// CHECK-NOT: attributes #{{.*}} "function-instrument"="xray-always" +pub fn function() {} diff --git a/tests/codegen/issue-75659.rs b/tests/codegen/issue-75659.rs index 6bcb59affe3..9394868c08d 100644 --- a/tests/codegen/issue-75659.rs +++ b/tests/codegen/issue-75659.rs @@ -1,7 +1,7 @@ // This test checks that the call to memchr/slice_contains is optimized away // when searching in small slices. -// compile-flags: -O -Zinline-mir=no +// compile-flags: -O -Zinline-mir=false // only-x86_64 #![crate_type = "lib"] diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..b139deeee1f --- /dev/null +++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff @@ -0,0 +1,68 @@ +- // MIR for `cand` before EnumSizeOpt ++ // MIR for `cand` after EnumSizeOpt + + fn cand() -> Candidate { + let mut _0: Candidate; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:27 + let mut _1: Candidate; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _8: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _10: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = Candidate::Small(const 1_u8); // scope 0 at $DIR/enum_opt.rs:+1:15: +1:34 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = Candidate::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _4 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..b139deeee1f --- /dev/null +++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff @@ -0,0 +1,68 @@ +- // MIR for `cand` before EnumSizeOpt ++ // MIR for `cand` after EnumSizeOpt + + fn cand() -> Candidate { + let mut _0: Candidate; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:27 + let mut _1: Candidate; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _8: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _10: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = Candidate::Small(const 1_u8); // scope 0 at $DIR/enum_opt.rs:+1:15: +1:34 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = Candidate::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _4 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..a80001149eb --- /dev/null +++ b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.32bit.diff @@ -0,0 +1,28 @@ +- // MIR for `invalid` before EnumSizeOpt ++ // MIR for `invalid` after EnumSizeOpt + + fn invalid() -> InvalidIdxs { + let mut _0: InvalidIdxs; // return place in scope 0 at $DIR/enum_opt.rs:+0:21: +0:32 + let mut _1: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:36 + let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:26: +2:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = InvalidIdxs::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:29 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _3 = [const 0_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _2 = InvalidIdxs::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:36 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..a80001149eb --- /dev/null +++ b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.64bit.diff @@ -0,0 +1,28 @@ +- // MIR for `invalid` before EnumSizeOpt ++ // MIR for `invalid` after EnumSizeOpt + + fn invalid() -> InvalidIdxs { + let mut _0: InvalidIdxs; // return place in scope 0 at $DIR/enum_opt.rs:+0:21: +0:32 + let mut _1: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:36 + let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:26: +2:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = InvalidIdxs::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:29 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _3 = [const 0_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _2 = InvalidIdxs::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:36 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs new file mode 100644 index 00000000000..2768d708049 --- /dev/null +++ b/tests/mir-opt/enum_opt.rs @@ -0,0 +1,86 @@ +// unit-test: EnumSizeOpt +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// compile-flags: -Zunsound-mir-opts + +#![feature(arbitrary_enum_discriminant, repr128)] + +// Tests that an enum with a variant with no data gets correctly transformed. +pub enum NoData { + Large([u8; 8196]), + None, +} + +// Tests that an enum with a variant with data that is a valid candidate gets transformed. +pub enum Candidate { + Small(u8), + Large([u8; 8196]), +} + +// Tests that an enum which has a discriminant much higher than the variant does not get +// tformed. +#[repr(u32)] +pub enum InvalidIdxs { + A = 302, + Large([u64; 1024]), +} + +// Tests that an enum with too high of a discriminant index (not in bounds of usize) does not +// get tformed. +#[repr(u128)] +pub enum NotTrunctable { + A = 0, + B([u8; 1024]) = 1, + C([u8; 4096]) = 0x10000000000000001, +} + +// Tests that an enum with discriminants in random order still gets tformed correctly. +#[repr(u32)] +pub enum RandOrderDiscr { + A = 13, + B([u8; 1024]) = 5, + C = 7, +} + +// EMIT_MIR enum_opt.unin.EnumSizeOpt.diff +pub fn unin() -> NoData { + let mut a = NoData::None; + a = NoData::Large([1; 8196]); + a +} + +// EMIT_MIR enum_opt.cand.EnumSizeOpt.diff +pub fn cand() -> Candidate { + let mut a = Candidate::Small(1); + a = Candidate::Large([1; 8196]); + a +} + +// EMIT_MIR enum_opt.invalid.EnumSizeOpt.diff +pub fn invalid() -> InvalidIdxs { + let mut a = InvalidIdxs::A; + a = InvalidIdxs::Large([0; 1024]); + a +} + +// EMIT_MIR enum_opt.trunc.EnumSizeOpt.diff +pub fn trunc() -> NotTrunctable { + let mut a = NotTrunctable::A; + a = NotTrunctable::B([0; 1024]); + a = NotTrunctable::C([0; 4096]); + a +} + +pub fn rand_order() -> RandOrderDiscr { + let mut a = RandOrderDiscr::A; + a = RandOrderDiscr::B([0; 1024]); + a = RandOrderDiscr::C; + a +} + +pub fn main() { + unin(); + cand(); + invalid(); + trunc(); + rand_order(); +} diff --git a/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..1ef79044d4f --- /dev/null +++ b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.32bit.diff @@ -0,0 +1,37 @@ +- // MIR for `trunc` before EnumSizeOpt ++ // MIR for `trunc` after EnumSizeOpt + + fn trunc() -> NotTrunctable { + let mut _0: NotTrunctable; // return place in scope 0 at $DIR/enum_opt.rs:+0:19: +0:32 + let mut _1: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 + let mut _4: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+3:7: +3:34 + let mut _5: [u8; 4096]; // in scope 0 at $DIR/enum_opt.rs:+3:24: +3:33 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NotTrunctable::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:31 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 0_u8; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = NotTrunctable::B(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageLive(_5); // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _5 = [const 0_u8; 4096]; // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _4 = NotTrunctable::C(move _5); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageDead(_5); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _1 = move _4; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:34 + StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+4:3: +4:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+5:1: +5:2 + return; // scope 0 at $DIR/enum_opt.rs:+5:2: +5:2 + } + } + diff --git a/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..1ef79044d4f --- /dev/null +++ b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.64bit.diff @@ -0,0 +1,37 @@ +- // MIR for `trunc` before EnumSizeOpt ++ // MIR for `trunc` after EnumSizeOpt + + fn trunc() -> NotTrunctable { + let mut _0: NotTrunctable; // return place in scope 0 at $DIR/enum_opt.rs:+0:19: +0:32 + let mut _1: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 + let mut _4: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+3:7: +3:34 + let mut _5: [u8; 4096]; // in scope 0 at $DIR/enum_opt.rs:+3:24: +3:33 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NotTrunctable::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:31 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 0_u8; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = NotTrunctable::B(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageLive(_5); // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _5 = [const 0_u8; 4096]; // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _4 = NotTrunctable::C(move _5); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageDead(_5); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _1 = move _4; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:34 + StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+4:3: +4:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+5:1: +5:2 + return; // scope 0 at $DIR/enum_opt.rs:+5:2: +5:2 + } + } + diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..ad9f12cf959 --- /dev/null +++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff @@ -0,0 +1,68 @@ +- // MIR for `unin` before EnumSizeOpt ++ // MIR for `unin` after EnumSizeOpt + + fn unin() -> NoData { + let mut _0: NoData; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:24 + let mut _1: NoData; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NoData; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:31 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:21: +2:30 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _8: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _10: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NoData::None; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:27 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _2 = NoData::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _4 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..ad9f12cf959 --- /dev/null +++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff @@ -0,0 +1,68 @@ +- // MIR for `unin` before EnumSizeOpt ++ // MIR for `unin` after EnumSizeOpt + + fn unin() -> NoData { + let mut _0: NoData; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:24 + let mut _1: NoData; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NoData; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:31 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:21: +2:30 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _8: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _10: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NoData::None; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:27 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _2 = NoData::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _4 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/run-make/no-input-file/Makefile b/tests/run-make/no-input-file/Makefile new file mode 100644 index 00000000000..2f02159229d --- /dev/null +++ b/tests/run-make/no-input-file/Makefile @@ -0,0 +1,4 @@ +include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --print crate-name 2>&1 | diff - no-input-file.stderr diff --git a/tests/run-make/no-input-file/no-input-file.stderr b/tests/run-make/no-input-file/no-input-file.stderr new file mode 100644 index 00000000000..b843eb524f3 --- /dev/null +++ b/tests/run-make/no-input-file/no-input-file.stderr @@ -0,0 +1,2 @@ +error: no input filename given + diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 706db892cb3..6c1684201a2 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -70,6 +70,15 @@ `=except-unused-functions` `=off` (default) -Z instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) + -Z instrument-xray=val -- insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas. -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) -Z layout-seed=val -- seed layout randomization -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) diff --git a/tests/rustdoc/const-generics/const-generics-docs.rs b/tests/rustdoc/const-generics/const-generics-docs.rs index cbda095424b..828486a41d4 100644 --- a/tests/rustdoc/const-generics/const-generics-docs.rs +++ b/tests/rustdoc/const-generics/const-generics-docs.rs @@ -21,8 +21,8 @@ pub use extern_crate::WTrait; // 'pub trait Trait<const N: usize>' // @has - '//*[@id="impl-Trait%3C1%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<1> for u8' // @has - '//*[@id="impl-Trait%3C2%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<2> for u8' -// @has - '//*[@id="impl-Trait%3C{1%20+%202}%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<{1 + 2}> for u8' -// @has - '//*[@id="impl-Trait%3CN%3E-for-%5Bu8%3B%20N%5D"]//h3[@class="code-header"]' \ +// @has - '//*[@id="impl-Trait%3C%7B1+%2B+2%7D%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<{1 + 2}> for u8' +// @has - '//*[@id="impl-Trait%3CN%3E-for-%5Bu8;+N%5D"]//h3[@class="code-header"]' \ // 'impl<const N: usize> Trait<N> for [u8; N]' pub trait Trait<const N: usize> {} impl Trait<1> for u8 {} @@ -47,7 +47,7 @@ impl<const M: usize> Foo<M> where u8: Trait<M> { } } -// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8%2C%20M%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Bar<u8, M>' +// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8,+M%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Bar<u8, M>' impl<const M: usize> Bar<u8, M> { // @has - '//*[@id="method.hey"]' \ // 'pub fn hey<const N: usize>(&self) -> Foo<N>where u8: Trait<N>' diff --git a/tests/rustdoc/const-generics/const-impl.rs b/tests/rustdoc/const-generics/const-impl.rs index 91866b7d890..152b643bf4b 100644 --- a/tests/rustdoc/const-generics/const-impl.rs +++ b/tests/rustdoc/const-generics/const-impl.rs @@ -9,20 +9,20 @@ pub enum Order { } // @has foo/struct.VSet.html '//pre[@class="rust item-decl"]' 'pub struct VSet<T, const ORDER: Order>' -// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>' -// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>' +// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT,+ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>' +// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT,+ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>' pub struct VSet<T, const ORDER: Order> { inner: Vec<T>, } -// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT%2C%20{%20Order%3A%3ASorted%20}%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Sorted }>' +// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT,+%7B+Order::Sorted+%7D%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Sorted }>' impl<T> VSet<T, { Order::Sorted }> { pub fn new() -> Self { Self { inner: Vec::new() } } } -// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT%2C%20{%20Order%3A%3AUnsorted%20}%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Unsorted }>' +// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT,+%7B+Order::Unsorted+%7D%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Unsorted }>' impl<T> VSet<T, { Order::Unsorted }> { pub fn new() -> Self { Self { inner: Vec::new() } @@ -31,7 +31,7 @@ impl<T> VSet<T, { Order::Unsorted }> { pub struct Escape<const S: &'static str>; -// @has foo/struct.Escape.html '//*[@id="impl-Escape%3Cr#%22%3Cscript%3Ealert(%22Escape%22)%3B%3C/script%3E%22#%3E"]/h3[@class="code-header"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>' +// @has foo/struct.Escape.html '//*[@id="impl-Escape%3Cr%23%22%3Cscript%3Ealert(%22Escape%22);%3C/script%3E%22%23%3E"]/h3[@class="code-header"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>' impl Escape<r#"<script>alert("Escape");</script>"#> { pub fn f() {} } diff --git a/tests/rustdoc/double-quote-escape.rs b/tests/rustdoc/double-quote-escape.rs index 350c897417d..4f4436377a0 100644 --- a/tests/rustdoc/double-quote-escape.rs +++ b/tests/rustdoc/double-quote-escape.rs @@ -7,5 +7,5 @@ pub trait Foo<T> { pub struct Bar; // @has foo/struct.Bar.html -// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe%20extern%20%22C%22%20fn()%3E-for-Bar"]' 'Foo<unsafe extern "C" fn()>' +// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe+extern+%22C%22+fn()%3E-for-Bar"]' 'Foo<unsafe extern "C" fn()>' impl Foo<unsafe extern "C" fn()> for Bar {} diff --git a/tests/rustdoc/markdown-summaries.rs b/tests/rustdoc/markdown-summaries.rs deleted file mode 100644 index 31e7072b5ce..00000000000 --- a/tests/rustdoc/markdown-summaries.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![crate_type = "lib"] -#![crate_name = "summaries"] - -//! This *summary* has a [link] and `code`. -//! -//! This is the second paragraph. -//! -//! [link]: https://example.com - -// @hasraw search-index.js 'This <em>summary</em> has a link and <code>code</code>.' -// @!hasraw - 'second paragraph' - -/// This `code` will be rendered in a code tag. -/// -/// This text should not be rendered. -pub struct Sidebar; - -// @hasraw search-index.js 'This <code>code</code> will be rendered in a code tag.' -// @hasraw summaries/sidebar-items.js 'This `code` will be rendered in a code tag.' -// @!hasraw - 'text should not be rendered' - -/// ```text -/// this block should not be rendered -/// ``` -pub struct Sidebar2; - -// @!hasraw summaries/sidebar-items.js 'block should not be rendered' diff --git a/tests/rustdoc/primitive-tuple-variadic.rs b/tests/rustdoc/primitive-tuple-variadic.rs index db7cfd60c71..846028bbb19 100644 --- a/tests/rustdoc/primitive-tuple-variadic.rs +++ b/tests/rustdoc/primitive-tuple-variadic.rs @@ -6,13 +6,13 @@ pub trait Foo {} // @has foo/trait.Foo.html -// @has - '//section[@id="impl-Foo-for-(T%2C)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)' +// @has - '//section[@id="impl-Foo-for-(T,)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)' #[doc(fake_variadic)] impl<T> Foo for (T,) {} pub trait Bar {} // @has foo/trait.Bar.html -// @has - '//section[@id="impl-Bar-for-(U%2C)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)' +// @has - '//section[@id="impl-Bar-for-(U,)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)' #[doc(fake_variadic)] impl<U: Foo> Bar for (U,) {} diff --git a/tests/rustdoc/sidebar-links-to-foreign-impl.rs b/tests/rustdoc/sidebar-links-to-foreign-impl.rs index 11e94694802..caa17dfbb1c 100644 --- a/tests/rustdoc/sidebar-links-to-foreign-impl.rs +++ b/tests/rustdoc/sidebar-links-to-foreign-impl.rs @@ -7,8 +7,8 @@ // @has - '//h2[@id="foreign-impls"]' 'Implementations on Foreign Types' // @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-u32"]' 'u32' // @has - '//*[@id="impl-Foo-for-u32"]//h3[@class="code-header"]' 'impl Foo for u32' -// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-%26%27a%20str"]' "&'a str" -// @has - '//*[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header"]' "impl<'a> Foo for &'a str" +// @has - "//*[@class=\"sidebar-elems\"]//section//a[@href=\"#impl-Foo-for-%26'a+str\"]" "&'a str" +// @has - "//*[@id=\"impl-Foo-for-%26'a+str\"]//h3[@class=\"code-header\"]" "impl<'a> Foo for &'a str" pub trait Foo {} impl Foo for u32 {} diff --git a/tests/rustdoc/where-clause-order.rs b/tests/rustdoc/where-clause-order.rs index b8502e10a48..b10f8f6856e 100644 --- a/tests/rustdoc/where-clause-order.rs +++ b/tests/rustdoc/where-clause-order.rs @@ -7,7 +7,7 @@ where } // @has 'foo/trait.SomeTrait.html' -// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, " +// @has - "//*[@id='impl-SomeTrait%3C(A,+B,+C,+D,+E)%3E-for-(A,+B,+C,+D,+E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, " impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, diff --git a/tests/ui/associated-type-bounds/elision.stderr b/tests/ui/associated-type-bounds/elision.stderr index b64a4dab206..cc10bbcc0b5 100644 --- a/tests/ui/associated-type-bounds/elision.stderr +++ b/tests/ui/associated-type-bounds/elision.stderr @@ -16,10 +16,10 @@ error[E0308]: mismatched types LL | fn f(x: &mut dyn Iterator<Item: Iterator<Item = &'_ ()>>) -> Option<&'_ ()> { x.next() } | ----------------------------- -------------- ^^^^^^^^ expected `Option<&()>`, found `Option<impl Iterator<Item = &'_ ()>>` | | | - | | expected `Option<&'static ()>` because of return type + | | expected `Option<&()>` because of return type | this type parameter | - = note: expected enum `Option<&'static ()>` + = note: expected enum `Option<&()>` found enum `Option<impl Iterator<Item = &'_ ()>>` error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/async-fn-path-elision.stderr b/tests/ui/async-await/async-fn-path-elision.stderr index 5e0c8c29989..224198653dc 100644 --- a/tests/ui/async-await/async-fn-path-elision.stderr +++ b/tests/ui/async-await/async-fn-path-elision.stderr @@ -4,7 +4,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | async fn error(lt: HasLifetime) { | ^^^^^^^^^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | async fn error(lt: HasLifetime<'_>) { diff --git a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr index 4bba42c7782..656bc29466f 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr +++ b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr @@ -28,7 +28,7 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here LL | fn bar<const N: &u8>() {} | ^ explicit lifetime name needed here -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:9:19 | LL | struct A<const N: &u8>; @@ -37,7 +37,7 @@ LL | struct A<const N: &u8>; = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:14:15 | LL | impl<const N: &u8> A<N> { @@ -46,7 +46,7 @@ LL | impl<const N: &u8> A<N> { = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:22:15 | LL | impl<const N: &u8> B for A<N> {} @@ -55,7 +55,7 @@ LL | impl<const N: &u8> B for A<N> {} = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:26:17 | LL | fn bar<const N: &u8>() {} @@ -64,7 +64,7 @@ LL | fn bar<const N: &u8>() {} = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:17:21 | LL | fn foo<const M: &u8>(&self) {} diff --git a/tests/ui/const-generics/const-param-elided-lifetime.rs b/tests/ui/const-generics/const-param-elided-lifetime.rs index 487b82dbf4a..45611d6bf5f 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.rs +++ b/tests/ui/const-generics/const-param-elided-lifetime.rs @@ -8,23 +8,23 @@ struct A<const N: &u8>; //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden trait B {} impl<const N: &u8> A<N> { //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden fn foo<const M: &u8>(&self) {} //~^ ERROR `&` without an explicit lifetime name cannot be used here - //[min]~^^ ERROR `&'static u8` is forbidden + //[min]~^^ ERROR `&u8` is forbidden } impl<const N: &u8> B for A<N> {} //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden fn bar<const N: &u8>() {} //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden fn main() {} diff --git a/tests/ui/const-generics/issues/issue-56445-1.min.stderr b/tests/ui/const-generics/issues/issue-56445-1.min.stderr index 43a5df117fd..9f880134162 100644 --- a/tests/ui/const-generics/issues/issue-56445-1.min.stderr +++ b/tests/ui/const-generics/issues/issue-56445-1.min.stderr @@ -6,7 +6,7 @@ LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); | = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052> -error: `&'static str` is forbidden as the type of a const generic parameter +error: `&str` is forbidden as the type of a const generic parameter --> $DIR/issue-56445-1.rs:9:25 | LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); diff --git a/tests/ui/const-generics/issues/issue-56445-1.rs b/tests/ui/const-generics/issues/issue-56445-1.rs index 13eb2ea9f69..0741c3796ad 100644 --- a/tests/ui/const-generics/issues/issue-56445-1.rs +++ b/tests/ui/const-generics/issues/issue-56445-1.rs @@ -8,6 +8,6 @@ use std::marker::PhantomData; struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); //~^ ERROR: use of non-static lifetime `'a` in const generic -//[min]~| ERROR: `&'static str` is forbidden as the type of a const generic parameter +//[min]~| ERROR: `&str` is forbidden as the type of a const generic parameter impl Bug<'_, ""> {} diff --git a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs index 9ea9fc71b55..54b483f53d4 100644 --- a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs +++ b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs @@ -8,7 +8,6 @@ fn should_error<T>() where T : Into<&u32> {} trait X<'a, K: 'a> { fn foo<'b, L: X<&'b Nested<K>>>(); //~^ ERROR missing lifetime specifier [E0106] - //~| ERROR the type `&'b Nested<K>` does not fulfill the required lifetime } fn bar<'b, L: X<&'b Nested<i32>>>(){} diff --git a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr index 9d859fddf56..faf4c9eb872 100644 --- a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr +++ b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr @@ -29,7 +29,7 @@ LL | fn foo<'b, L: X<'lifetime, &'b Nested<K>>>(); | ++++++++++ error[E0106]: missing lifetime specifier - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:14:16 + --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:16 | LL | fn bar<'b, L: X<&'b Nested<i32>>>(){} | ^ expected named lifetime parameter @@ -39,19 +39,7 @@ help: consider using the `'b` lifetime LL | fn bar<'b, L: X<'b, &'b Nested<i32>>>(){} | +++ -error[E0477]: the type `&'b Nested<K>` does not fulfill the required lifetime - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:19 - | -LL | fn foo<'b, L: X<&'b Nested<K>>>(); - | ^^^^^^^^^^^^^^^^ - | -note: type must satisfy the static lifetime as required by this binding - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:8:16 - | -LL | trait X<'a, K: 'a> { - | ^^ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0106, E0477, E0637. +Some errors have detailed explanations: E0106, E0637. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-header-lifetime-elision/path-elided.stderr b/tests/ui/impl-header-lifetime-elision/path-elided.stderr index 0b7d3f1e851..18e4c618dba 100644 --- a/tests/ui/impl-header-lifetime-elision/path-elided.stderr +++ b/tests/ui/impl-header-lifetime-elision/path-elided.stderr @@ -4,7 +4,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl MyTrait for Foo { | ^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl MyTrait for Foo<'_> { diff --git a/tests/ui/impl-header-lifetime-elision/trait-elided.stderr b/tests/ui/impl-header-lifetime-elision/trait-elided.stderr index 412bba6be71..74631a03786 100644 --- a/tests/ui/impl-header-lifetime-elision/trait-elided.stderr +++ b/tests/ui/impl-header-lifetime-elision/trait-elided.stderr @@ -4,7 +4,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl MyTrait for u32 {} | ^^^^^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl MyTrait<'_> for u32 {} diff --git a/tests/ui/impl-trait/in-trait/signature-mismatch.stderr b/tests/ui/impl-trait/in-trait/signature-mismatch.stderr index e105660173b..c4fcaabe446 100644 --- a/tests/ui/impl-trait/in-trait/signature-mismatch.stderr +++ b/tests/ui/impl-trait/in-trait/signature-mismatch.stderr @@ -2,12 +2,12 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/signature-mismatch.rs:15:5 | LL | fn async_fn(&self, buff: &[u8]) -> impl Future<Output = Vec<u8>>; - | ----------------------------------------------------------------- expected `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + 'static` + | ----------------------------------------------------------------- expected `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3` ... LL | fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2` | - = note: expected signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + 'static` + = note: expected signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3` found signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/tests/ui/inference/issue-107090.rs b/tests/ui/inference/issue-107090.rs index 9426445656f..a22e12c6d88 100644 --- a/tests/ui/inference/issue-107090.rs +++ b/tests/ui/inference/issue-107090.rs @@ -2,9 +2,7 @@ use std::marker::PhantomData; struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) where Foo<'short, 'out, T>: Convert<'a, 'b>; - //~^ ERROR mismatched types - //~^^ ERROR mismatched types - //~^^^ ERROR use of undeclared lifetime name + //~^ ERROR use of undeclared lifetime name //~| ERROR use of undeclared lifetime name `'out` trait Convert<'a, 'b>: Sized { @@ -13,19 +11,15 @@ trait Convert<'a, 'b>: Sized { impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { //~^ ERROR use of undeclared lifetime name //~^^ ERROR use of undeclared lifetime name `'out` - //~| ERROR cannot infer an appropriate lifetime for lifetime parameter fn cast(&'long self) -> &'short Foo<'short, 'out, T> { //~^ ERROR use of undeclared lifetime name - //~| ERROR cannot infer an appropriate lifetime for lifetime parameter self } } fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { //~^ ERROR use of undeclared lifetime name - //~^^ ERROR incompatible lifetime on type - //~| ERROR `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement - sadness.cast() + sadness.cast() //~ ERROR mismatched types } fn main() {} diff --git a/tests/ui/inference/issue-107090.stderr b/tests/ui/inference/issue-107090.stderr index 33cb39014ac..6233b629ad6 100644 --- a/tests/ui/inference/issue-107090.stderr +++ b/tests/ui/inference/issue-107090.stderr @@ -30,7 +30,7 @@ LL | struct Foo<'out, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) | +++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/issue-107090.rs:13:47 + --> $DIR/issue-107090.rs:11:47 | LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { | - ^^ undeclared lifetime @@ -38,13 +38,13 @@ LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> | help: consider introducing lifetime `'b` here: `'b,` error[E0261]: use of undeclared lifetime name `'out` - --> $DIR/issue-107090.rs:13:67 + --> $DIR/issue-107090.rs:11:67 | LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { | - help: consider introducing lifetime `'out` here: `'out,` ^^^^ undeclared lifetime error[E0261]: use of undeclared lifetime name `'out` - --> $DIR/issue-107090.rs:17:49 + --> $DIR/issue-107090.rs:14:49 | LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { | ^^^^ undeclared lifetime @@ -59,7 +59,7 @@ LL | impl<'out, 'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'ou | +++++ error[E0261]: use of undeclared lifetime name `'short` - --> $DIR/issue-107090.rs:24:68 + --> $DIR/issue-107090.rs:20:68 | LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { | - ^^^^^^ undeclared lifetime @@ -67,107 +67,18 @@ LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, | help: consider introducing lifetime `'short` here: `'short,` error[E0308]: mismatched types - --> $DIR/issue-107090.rs:4:27 - | -LL | Foo<'short, 'out, T>: Convert<'a, 'b>; - | ^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected trait `Convert<'static, 'static>` - found trait `Convert<'a, 'b>` -note: the lifetime `'a` as defined here... - --> $DIR/issue-107090.rs:2:12 - | -LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) - | ^^ - = note: ...does not necessarily outlive the static lifetime - -error[E0308]: mismatched types - --> $DIR/issue-107090.rs:4:27 - | -LL | Foo<'short, 'out, T>: Convert<'a, 'b>; - | ^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected trait `Convert<'static, 'static>` - found trait `Convert<'a, 'b>` -note: the lifetime `'b` as defined here... - --> $DIR/issue-107090.rs:2:16 - | -LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) - | ^^ - = note: ...does not necessarily outlive the static lifetime - -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements - --> $DIR/issue-107090.rs:13:55 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^^^^^^^^^^^^^^^ - | -note: first, the lifetime cannot outlive the lifetime `'short` as defined here... - --> $DIR/issue-107090.rs:13:21 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^ - = note: ...but the lifetime must also be valid for the static lifetime... -note: ...so that the types are compatible - --> $DIR/issue-107090.rs:13:55 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^^^^^^^^^^^^^^^ - = note: expected `Convert<'short, 'static>` - found `Convert<'_, 'static>` - -error: incompatible lifetime on type - --> $DIR/issue-107090.rs:24:29 - | -LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | ^^^^^^^^^^^^^^^^^^ - | -note: because this has an unmet lifetime requirement - --> $DIR/issue-107090.rs:4:27 - | -LL | Foo<'short, 'out, T>: Convert<'a, 'b>; - | ^^^^^^^^^^^^^^^ introduces a `'static` lifetime requirement -note: the lifetime `'out` as defined here... - --> $DIR/issue-107090.rs:24:17 - | -LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | ^^^^ -note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` - --> $DIR/issue-107090.rs:13:1 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0759]: `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement - --> $DIR/issue-107090.rs:24:29 + --> $DIR/issue-107090.rs:22:5 | LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | ^^^^^^^^^^^^^^^^^^ - | | - | this data with lifetime `'in_`... - | ...is used and required to live as long as `'static` here - -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements - --> $DIR/issue-107090.rs:17:13 - | -LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { - | ^^^^^^^^^^^ + | - this type parameter ------- expected `&'out T` because of return type +LL | +LL | sadness.cast() + | ^^^^^^^^^^^^^^ expected `&T`, found `&Foo<'_, '_, T>` | -note: first, the lifetime cannot outlive the lifetime `'short` as defined here... - --> $DIR/issue-107090.rs:13:21 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^ - = note: ...but the lifetime must also be valid for the static lifetime... -note: ...so that the types are compatible - --> $DIR/issue-107090.rs:17:13 - | -LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { - | ^^^^^^^^^^^ - = note: expected `Convert<'short, 'static>` - found `Convert<'_, 'static>` + = note: expected reference `&'out T` + found reference `&Foo<'_, '_, T>` -error: aborting due to 12 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0261, E0308, E0495, E0759. +Some errors have detailed explanations: E0261, E0308. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs new file mode 100644 index 00000000000..7f6758f47f8 --- /dev/null +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs @@ -0,0 +1,19 @@ +// ignore-tidy-linelength + +// Regression test for #107745. +// Previously need_type_info::update_infer_source will consider expressions originating from +// macro expressions as candiate "previous sources". This unfortunately can mean that +// for macros expansions such as `format!()` internal implementation details can leak, such as: +// +// ``` +// error[E0282]: type annotations needed +// --> src/main.rs:2:22 +// | +//2 | println!("{:?}", []); +// | ^^ cannot infer type of the type parameter `T` declared on the associated function `new_debug` +// ``` + +fn main() { + println!("{:?}", []); + //~^ ERROR type annotations needed +} diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr new file mode 100644 index 00000000000..464655bbcf4 --- /dev/null +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/issue-107745-avoid-expr-from-macro-expansion.rs:17:22 + | +LL | println!("{:?}", []); + | ^^ cannot infer type + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/instrument-xray/flags-always-never-1.rs b/tests/ui/instrument-xray/flags-always-never-1.rs new file mode 100644 index 00000000000..4dd43439eb7 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-1.rs @@ -0,0 +1,7 @@ +// Checks that `-Z instrument-xray` does not allow `always` and `never` simultaneously. +// +// needs-xray +// compile-flags: -Z instrument-xray=always,never +// error-pattern: incorrect value `always,never` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-always-never-1.stderr b/tests/ui/instrument-xray/flags-always-never-1.stderr new file mode 100644 index 00000000000..e211c6f6025 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-1.stderr @@ -0,0 +1,2 @@ +error: incorrect value `always,never` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected + diff --git a/tests/ui/instrument-xray/flags-always-never-2.rs b/tests/ui/instrument-xray/flags-always-never-2.rs new file mode 100644 index 00000000000..7310aa0a0d2 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-2.rs @@ -0,0 +1,9 @@ +// Checks that `-Z instrument-xray` allows `always` and `never` sequentially. +// (The last specified setting wins, like `-Z instrument-xray=no` as well.) +// +// needs-xray +// compile-flags: -Z instrument-xray=always +// compile-flags: -Z instrument-xray=never +// check-pass + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-basic.rs b/tests/ui/instrument-xray/flags-basic.rs new file mode 100644 index 00000000000..b97f0dd8a07 --- /dev/null +++ b/tests/ui/instrument-xray/flags-basic.rs @@ -0,0 +1,9 @@ +// Verifies basic `-Z instrument-xray` flags. +// +// needs-xray +// compile-flags: -Z instrument-xray +// compile-flags: -Z instrument-xray=skip-exit +// compile-flags: -Z instrument-xray=ignore-loops,instruction-threshold=300 +// check-pass + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-always.rs b/tests/ui/instrument-xray/flags-dupe-always.rs new file mode 100644 index 00000000000..407f3e2aa5d --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-always.rs @@ -0,0 +1,7 @@ +// Checks that `-Z instrument-xray` does not allow duplicates. +// +// needs-xray +// compile-flags: -Z instrument-xray=always,always +// error-pattern: incorrect value `always,always` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-always.stderr b/tests/ui/instrument-xray/flags-dupe-always.stderr new file mode 100644 index 00000000000..d1ac113fa43 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-always.stderr @@ -0,0 +1,2 @@ +error: incorrect value `always,always` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected + diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs new file mode 100644 index 00000000000..75b210a6547 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs @@ -0,0 +1,7 @@ +// Checks that `-Z instrument-xray` does not allow duplicates. +// +// needs-xray +// compile-flags: -Z instrument-xray=ignore-loops,ignore-loops +// error-pattern: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr new file mode 100644 index 00000000000..52f6b33075b --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr @@ -0,0 +1,2 @@ +error: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected + diff --git a/tests/ui/instrument-xray/target-not-supported.rs b/tests/ui/instrument-xray/target-not-supported.rs new file mode 100644 index 00000000000..e6bdd23e8fc --- /dev/null +++ b/tests/ui/instrument-xray/target-not-supported.rs @@ -0,0 +1,9 @@ +// Verifies that `-Z instrument-xray` cannot be used with unsupported targets, +// +// needs-llvm-components: x86 +// compile-flags: -Z instrument-xray --target x86_64-apple-darwin +// error-pattern: error: XRay instrumentation is not supported for this target + +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/tests/ui/instrument-xray/target-not-supported.stderr b/tests/ui/instrument-xray/target-not-supported.stderr new file mode 100644 index 00000000000..6e3b0c8a380 --- /dev/null +++ b/tests/ui/instrument-xray/target-not-supported.stderr @@ -0,0 +1,4 @@ +error: XRay instrumentation is not supported for this target + +error: aborting due to previous error + diff --git a/tests/ui/issues/issue-10412.stderr b/tests/ui/issues/issue-10412.stderr index 46b9fd541ad..26666782d2a 100644 --- a/tests/ui/issues/issue-10412.stderr +++ b/tests/ui/issues/issue-10412.stderr @@ -46,7 +46,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl<'self> Serializable<str> for &'self str { | ^^^^^^^^^^^^^^^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl<'self> Serializable<'_, str> for &'self str { diff --git a/tests/ui/issues/issue-16966.stderr b/tests/ui/issues/issue-16966.stderr index 60f5190dbd0..8c92505b5eb 100644 --- a/tests/ui/issues/issue-16966.stderr +++ b/tests/ui/issues/issue-16966.stderr @@ -1,10 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/issue-16966.rs:2:5 + --> $DIR/issue-16966.rs:2:12 | LL | panic!(std::default::Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `M` declared on the function `begin_panic` - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type error: aborting due to previous error diff --git a/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs b/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs new file mode 100644 index 00000000000..5d5429ec895 --- /dev/null +++ b/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs @@ -0,0 +1,12 @@ +// check-pass + +#![deny(unused_lifetimes)] +trait Trait2 { + type As; +} + +// we should not warn about an unused lifetime about code generated from this proc macro here +#[derive(Clone)] +struct ShimMethod4<T: Trait2 + 'static>(pub &'static dyn for<'s> Fn(&'s mut T::As)); + +pub fn main() {} diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index 4dfacb93801..30afcecf827 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -40,9 +40,9 @@ error[E0308]: mismatched types LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() } | ---- ^^^^^^^^^^^ expected `&str`, found `Option<&str>` | | - | expected `&'static str` because of return type + | expected `&str` because of return type | - = note: expected reference `&'static str` + = note: expected reference `&str` found enum `Option<&str>` error[E0061]: this function takes 1 argument but 0 arguments were supplied diff --git a/tests/ui/lifetimes/issue-69314.fixed b/tests/ui/lifetimes/issue-69314.fixed new file mode 100644 index 00000000000..41116d4ea61 --- /dev/null +++ b/tests/ui/lifetimes/issue-69314.fixed @@ -0,0 +1,22 @@ +// run-rustfix +// edition:2021 +#![allow(dead_code, unused_mut, unused_variables)] +struct A {} +struct Msg<'a> { + s: &'a [i32], +} +impl A { + async fn g(buf: &[i32]) -> Msg<'_> { + Msg { s: &buf[0..1] } + } + async fn f() { + let mut buf = [0; 512]; + let m2 = &buf[..]; //~ ERROR `buf` does not live long enough + let m = Self::g(m2).await; + Self::f2(m).await; + } + async fn f2(m: Msg<'_>) {} + //~^ ERROR implicit elided lifetime not allowed here +} + +fn main() {} diff --git a/tests/ui/lifetimes/issue-69314.rs b/tests/ui/lifetimes/issue-69314.rs new file mode 100644 index 00000000000..17445341eb6 --- /dev/null +++ b/tests/ui/lifetimes/issue-69314.rs @@ -0,0 +1,22 @@ +// run-rustfix +// edition:2021 +#![allow(dead_code, unused_mut, unused_variables)] +struct A {} +struct Msg<'a> { + s: &'a [i32], +} +impl A { + async fn g(buf: &[i32]) -> Msg<'_> { + Msg { s: &buf[0..1] } + } + async fn f() { + let mut buf = [0; 512]; + let m2 = &buf[..]; //~ ERROR `buf` does not live long enough + let m = Self::g(m2).await; + Self::f2(m).await; + } + async fn f2(m: Msg) {} + //~^ ERROR implicit elided lifetime not allowed here +} + +fn main() {} diff --git a/tests/ui/lifetimes/issue-69314.stderr b/tests/ui/lifetimes/issue-69314.stderr new file mode 100644 index 00000000000..7ae6789285b --- /dev/null +++ b/tests/ui/lifetimes/issue-69314.stderr @@ -0,0 +1,26 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/issue-69314.rs:18:20 + | +LL | async fn f2(m: Msg) {} + | ^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | async fn f2(m: Msg<'_>) {} + | ++++ + +error[E0597]: `buf` does not live long enough + --> $DIR/issue-69314.rs:14:19 + | +LL | let m2 = &buf[..]; + | ^^^ borrowed value does not live long enough +LL | let m = Self::g(m2).await; + | ----------- argument requires that `buf` is borrowed for `'static` +LL | Self::f2(m).await; +LL | } + | - `buf` dropped here while still borrowed + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0597, E0726. +For more information about an error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/unusual-rib-combinations.rs b/tests/ui/lifetimes/unusual-rib-combinations.rs index b4c86aab863..1c122f42e59 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.rs +++ b/tests/ui/lifetimes/unusual-rib-combinations.rs @@ -23,6 +23,6 @@ fn c<T = u8()>() {} // Elided lifetime in path in ConstGeneric fn d<const C: S>() {} //~^ ERROR missing lifetime specifier -//~| ERROR `S<'static>` is forbidden as the type of a const generic parameter +//~| ERROR `S<'_>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr index 6d7b4250698..68f4fce0178 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.stderr +++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr @@ -46,7 +46,7 @@ LL | fn a() -> [u8; foo::()] { = note: expected type `usize` found fn item `fn() {foo}` -error: `S<'static>` is forbidden as the type of a const generic parameter +error: `S<'_>` is forbidden as the type of a const generic parameter --> $DIR/unusual-rib-combinations.rs:24:15 | LL | fn d<const C: S>() {} diff --git a/tests/ui/lint/reasons-forbidden.rs b/tests/ui/lint/reasons-forbidden.rs index 9c2edec4d52..947099fdd13 100644 --- a/tests/ui/lint/reasons-forbidden.rs +++ b/tests/ui/lint/reasons-forbidden.rs @@ -8,7 +8,7 @@ // // The test is much cleaner if we deduplicate, though. -// compile-flags: -Z deduplicate-diagnostics=yes +// compile-flags: -Z deduplicate-diagnostics=true #![forbid( unsafe_code, diff --git a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr index 9ddea162944..b5231823099 100644 --- a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr +++ b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -16,9 +16,9 @@ LL | fn next(&mut self) -> Option<IteratorChunk<T, S>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'1, T, S>>` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL | - = note: expected `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'static, T, S>>` + = note: expected `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'2, T, S>>` | - = note: expected signature `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'static, T, S>>` + = note: expected signature `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'2, T, S>>` found signature `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'1, T, S>>` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/tests/ui/modules/issue-107649.rs b/tests/ui/modules/issue-107649.rs new file mode 100644 index 00000000000..71b84cd30d6 --- /dev/null +++ b/tests/ui/modules/issue-107649.rs @@ -0,0 +1,106 @@ +// compile-flags: -Z ui-testing=no +#[path = "auxiliary/dummy_lib.rs"] +mod lib; + +/// The function needs to be long enough to +/// ensure `max_line_num_len` to be large enough +/// for no-ui-testing +fn main() { + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + dbg!(lib::Dummy); //~ Error: `Dummy` doesn't implement `Debug` +} diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr new file mode 100644 index 00000000000..1cea71f2829 --- /dev/null +++ b/tests/ui/modules/issue-107649.stderr @@ -0,0 +1,18 @@ +error[E0277]: `Dummy` doesn't implement `Debug` + --> $DIR/issue-107649.rs:105:5 + | +105 | dbg!(lib::Dummy); + | ^^^^^^^^^^^^^^^^ `Dummy` cannot be formatted using `{:?}` + | + = help: the trait `Debug` is not implemented for `Dummy` + = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy` + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `Dummy` with `#[derive(Debug)]` + --> $DIR/auxiliary/dummy_lib.rs:2:1 + | +2 | #[derive(Debug)] + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/nll/issue-52057.rs b/tests/ui/nll/issue-52057.rs index 98f49fe8f55..5991c1104c8 100644 --- a/tests/ui/nll/issue-52057.rs +++ b/tests/ui/nll/issue-52057.rs @@ -1,6 +1,6 @@ // Regression test for #52057. There is an implied bound -// that `I: 'a` where `'a` is the lifetime of `self` in `parse_first`; -// but to observe that, one must normalize first. +// that `I: 'x` where `'x` is the lifetime of the reference `&mut Self::Input` +// in `parse_first`; but to observe that, one must normalize first. // // run-pass diff --git a/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs b/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs index 79d78da3328..0e487a700b8 100644 --- a/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs +++ b/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags: -C debug_assertions=yes +// compile-flags: -C debug_assertions=true // needs-unwind // ignore-emscripten dies with an LLVM error diff --git a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs index da95c1bfa27..a56cd17773d 100644 --- a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs +++ b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs @@ -17,7 +17,7 @@ fn test2<T1, T2>(arg1 : T1, arg2 : T2) { fn test3<'a>(arg : &'a u32) { let v : Vec<'a = vec![]; //~^ ERROR: expected one of - //~| ERROR: type annotations needed for `Vec<T>` + //~| ERROR: type annotations needed for `Vec<_>` } fn main() {} diff --git a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr index bad241634cb..b2448774ae9 100644 --- a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr +++ b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr @@ -39,26 +39,26 @@ help: you might have meant to end the type parameters here LL | let v : Vec<'a> = vec![]; | + -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:7:7 | LL | let v : Vec<(u32,_) = vec![]; | ^ | -help: consider giving `v` an explicit type, where the type for type parameter `T` is specified +help: consider giving `v` an explicit type, where the placeholders `_` are specified | -LL | let v: Vec<T> : Vec<(u32,_) = vec![]; +LL | let v: Vec<_> : Vec<(u32,_) = vec![]; | ++++++++ -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:18:7 | LL | let v : Vec<'a = vec![]; | ^ | -help: consider giving `v` an explicit type, where the type for type parameter `T` is specified +help: consider giving `v` an explicit type, where the placeholders `_` are specified | -LL | let v: Vec<T> : Vec<'a = vec![]; +LL | let v: Vec<_> : Vec<'a = vec![]; | ++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/rfc-2091-track-caller/call-chain.rs b/tests/ui/rfc-2091-track-caller/call-chain.rs index 28b3f76c9d5..a8814ce2852 100644 --- a/tests/ui/rfc-2091-track-caller/call-chain.rs +++ b/tests/ui/rfc-2091-track-caller/call-chain.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[default] compile-flags: -Zinline-mir=no +//[default] compile-flags: -Zinline-mir=false //[mir-opt] compile-flags: -Zmir-opt-level=4 use std::panic::Location; diff --git a/tests/ui/suggestions/issue-104961.fixed b/tests/ui/suggestions/issue-104961.fixed new file mode 100644 index 00000000000..520d638b174 --- /dev/null +++ b/tests/ui/suggestions/issue-104961.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +fn foo(x: &str) -> bool { + x.starts_with(&("hi".to_string() + " you")) + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn foo2(x: &str) -> bool { + x.starts_with(&"hi".to_string()) + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn main() { + foo("hi you"); + foo2("hi"); +} diff --git a/tests/ui/suggestions/issue-104961.rs b/tests/ui/suggestions/issue-104961.rs new file mode 100644 index 00000000000..aeb787abb6f --- /dev/null +++ b/tests/ui/suggestions/issue-104961.rs @@ -0,0 +1,16 @@ +// run-rustfix + +fn foo(x: &str) -> bool { + x.starts_with("hi".to_string() + " you") + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn foo2(x: &str) -> bool { + x.starts_with("hi".to_string()) + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn main() { + foo("hi you"); + foo2("hi"); +} diff --git a/tests/ui/suggestions/issue-104961.stderr b/tests/ui/suggestions/issue-104961.stderr new file mode 100644 index 00000000000..8cec6a3f827 --- /dev/null +++ b/tests/ui/suggestions/issue-104961.stderr @@ -0,0 +1,37 @@ +error[E0277]: expected a `FnMut<(char,)>` closure, found `String` + --> $DIR/issue-104961.rs:4:19 + | +LL | x.starts_with("hi".to_string() + " you") + | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | | + | required by a bound introduced by this call + | + = note: the trait bound `String: Pattern<'_>` is not satisfied + = note: required for `String` to implement `Pattern<'_>` +note: required by a bound in `core::str::<impl str>::starts_with` + --> $SRC_DIR/core/src/str/mod.rs:LL:COL +help: consider borrowing here + | +LL | x.starts_with(&("hi".to_string() + " you")) + | ++ + + +error[E0277]: expected a `FnMut<(char,)>` closure, found `String` + --> $DIR/issue-104961.rs:9:19 + | +LL | x.starts_with("hi".to_string()) + | ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | | + | required by a bound introduced by this call + | + = note: the trait bound `String: Pattern<'_>` is not satisfied + = note: required for `String` to implement `Pattern<'_>` +note: required by a bound in `core::str::<impl str>::starts_with` + --> $SRC_DIR/core/src/str/mod.rs:LL:COL +help: consider borrowing here + | +LL | x.starts_with(&"hi".to_string()) + | + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs new file mode 100644 index 00000000000..657dd9c22c2 --- /dev/null +++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs @@ -0,0 +1,16 @@ +enum E { + One(i32, i32), +} + +fn main() { + let var = E::One; + if let E::One(var1, var2) = var { + //~^ ERROR mismatched types + //~| HELP use parentheses to construct this tuple variant + println!("{var1} {var2}"); + } + + let Some(x) = Some; + //~^ ERROR mismatched types + //~| HELP use parentheses to construct this tuple variant +} diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr new file mode 100644 index 00000000000..7338312bab6 --- /dev/null +++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/suggest-call-on-pat-mismatch.rs:7:12 + | +LL | if let E::One(var1, var2) = var { + | ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}` + | | + | expected enum constructor, found `E` + | + = note: expected enum constructor `fn(i32, i32) -> E {E::One}` + found enum `E` +help: use parentheses to construct this tuple variant + | +LL | if let E::One(var1, var2) = var(/* i32 */, /* i32 */) { + | ++++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/suggest-call-on-pat-mismatch.rs:13:9 + | +LL | let Some(x) = Some; + | ^^^^^^^ ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}` + | | + | expected enum constructor, found `Option<_>` + | + = note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}` + found enum `Option<_>` +help: use parentheses to construct this tuple variant + | +LL | let Some(x) = Some(/* value */); + | +++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs b/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs new file mode 100644 index 00000000000..9b41a8096c4 --- /dev/null +++ b/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs @@ -0,0 +1,21 @@ +// Regression test for #107747: methods from trait alias supertraits were brought into scope +// +// check-pass + +#![feature(trait_alias)] + +use std::fmt; + +trait Foo: fmt::Debug {} +trait Bar = Foo; + +#[derive(Debug)] +struct Qux(bool); + +impl fmt::Display for Qux { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/elaborate-item-bounds.rs b/tests/ui/traits/new-solver/elaborate-item-bounds.rs new file mode 100644 index 00000000000..076aefcf8fc --- /dev/null +++ b/tests/ui/traits/new-solver/elaborate-item-bounds.rs @@ -0,0 +1,12 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo { + type Bar: Bar; +} + +trait Bar: Baz {} + +trait Baz {} + +fn main() {} diff --git a/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs new file mode 100644 index 00000000000..bdf999ec5dd --- /dev/null +++ b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs @@ -0,0 +1,25 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl<T> Foo for T { + type Assoc = i32; +} + +impl<T> Bar for T where T: Foo<Assoc = i32> {} + +fn require_bar<T: Bar>() {} + +fn foo<T: Foo>() { + // Unlike the classic solver, `<T as Foo>::Assoc = _` will still project + // down to `i32` even though there's a param-env candidate here, since we + // don't assemble any param-env projection candidates for `T: Foo` alone. + require_bar::<T>(); +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/provisional-result-done.rs b/tests/ui/traits/new-solver/provisional-result-done.rs index a3d97927bad..589d34dd7ab 100644 --- a/tests/ui/traits/new-solver/provisional-result-done.rs +++ b/tests/ui/traits/new-solver/provisional-result-done.rs @@ -1,9 +1,5 @@ -// known-bug: unknown // compile-flags: -Ztrait-solver=next -// failure-status: 101 -// normalize-stderr-test "note: .*\n\n" -> "" -// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" -// rustc-env:RUST_BACKTRACE=0 +// check-pass // This tests checks that we update results in the provisional cache when // we pop a goal from the stack. diff --git a/tests/ui/traits/new-solver/provisional-result-done.stderr b/tests/ui/traits/new-solver/provisional-result-done.stderr deleted file mode 100644 index ffc92b81f08..00000000000 --- a/tests/ui/traits/new-solver/provisional-result-done.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [check_well_formed] checking that `<impl at $DIR/provisional-result-done.rs:20:1: 20:31>` is well-formed -#1 [check_mod_type_wf] checking that types are well-formed in top-level module -end of query stack diff --git a/tests/ui/traits/new-solver/temporary-ambiguity.rs b/tests/ui/traits/new-solver/temporary-ambiguity.rs new file mode 100644 index 00000000000..18ee0545700 --- /dev/null +++ b/tests/ui/traits/new-solver/temporary-ambiguity.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +// Checks that we don't explode when we assemble >1 candidate for a goal. + +struct Wrapper<T>(T); + +trait Foo {} + +impl Foo for Wrapper<i32> {} + +impl Foo for Wrapper<()> {} + +fn needs_foo(_: impl Foo) {} + +fn main() { + let mut x = Default::default(); + let w = Wrapper(x); + needs_foo(w); + x = 1; + drop(x); +} diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs new file mode 100644 index 00000000000..cde2059ca9b --- /dev/null +++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs @@ -0,0 +1,30 @@ +// compile-flags: -Ztrait-solver=next + +// When we're solving `<T as Foo>::Assoc = i32`, we actually first solve +// `<T as Foo>::Assoc = _#1t`, then unify `_#1t` with `i32`. That goal +// with the inference variable is ambiguous when there are >1 param-env +// candidates. + +// We don't unify the RHS of a projection goal eagerly when solving, both +// for caching reasons and partly to make sure that we don't make the new +// trait solver smarter than it should be. + +// This is (as far as I can tell) a forwards-compatible decision, but if you +// make this test go from fail to pass, be sure you understand the implications! + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl<T> Bar for T where T: Foo<Assoc = i32> {} + +fn needs_bar<T: Bar>() {} + +fn foo<T: Foo<Assoc = i32> + Foo<Assoc = u32>>() { + needs_bar::<T>(); + //~^ ERROR type annotations needed: cannot satisfy `T: Bar` +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr new file mode 100644 index 00000000000..fa5e780ee5e --- /dev/null +++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr @@ -0,0 +1,16 @@ +error[E0283]: type annotations needed: cannot satisfy `T: Bar` + --> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:5 + | +LL | needs_bar::<T>(); + | ^^^^^^^^^^^^^^ + | + = note: cannot satisfy `T: Bar` +note: required by a bound in `needs_bar` + --> $DIR/two-projection-param-candidates-are-ambiguous.rs:23:17 + | +LL | fn needs_bar<T: Bar>() {} + | ^^^ required by this bound in `needs_bar` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs b/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs new file mode 100644 index 00000000000..bcfc747ebb1 --- /dev/null +++ b/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs @@ -0,0 +1,8 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +struct Foo(*mut ()); + +unsafe impl Sync for Foo {} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs index d3e169a70d3..cdd8f6f1976 100644 --- a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs +++ b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs @@ -4,7 +4,7 @@ fn main() { let y = 42; let x = wrong_generic(&y); let z: i32 = x; - //~^ ERROR expected generic type parameter, found `&'static i32 + //~^ ERROR expected generic type parameter, found `&i32` } type WrongGeneric<T> = impl 'static; diff --git a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr index 19115fd2866..fa79e51e9f7 100644 --- a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr +++ b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr @@ -4,7 +4,7 @@ error: at least one trait must be specified LL | type WrongGeneric<T> = impl 'static; | ^^^^^^^^^^^^ -error[E0792]: expected generic type parameter, found `&'static i32` +error[E0792]: expected generic type parameter, found `&i32` --> $DIR/generic_type_does_not_live_long_enough.rs:6:18 | LL | let z: i32 = x; diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr index b63d2a3b61c..09c4b2053b2 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr @@ -1,12 +1,12 @@ -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/cannot_infer_local_or_vec.rs:2:9 | LL | let x = vec![]; | ^ | -help: consider giving `x` an explicit type, where the type for type parameter `T` is specified +help: consider giving `x` an explicit type, where the placeholders `_` are specified | -LL | let x: Vec<T> = vec![]; +LL | let x: Vec<_> = vec![]; | ++++++++ error: aborting due to previous error diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr index e544b369515..1fa253052e6 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr @@ -1,12 +1,12 @@ -error[E0282]: type annotations needed for `(Vec<T>,)` +error[E0282]: type annotations needed for `(Vec<_>,)` --> $DIR/cannot_infer_local_or_vec_in_tuples.rs:2:9 | LL | let (x, ) = (vec![], ); | ^^^^^ ---------- type must be known at this point | -help: consider giving this pattern a type, where the type for type parameter `T` is specified +help: consider giving this pattern a type, where the placeholders `_` are specified | -LL | let (x, ): (Vec<T>,) = (vec![], ); +LL | let (x, ): (Vec<_>,) = (vec![], ); | +++++++++++ error: aborting due to previous error diff --git a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs index 4fcf8f403bb..0be5127dcc4 100644 --- a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs +++ b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs @@ -13,5 +13,5 @@ pub struct Ref<'a>(&'a u8); impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here extern "C" { - pub fn repro(_: Wrapper<Ref>); //~ ERROR: incompatible lifetime on type + pub fn repro(_: Wrapper<Ref>); } diff --git a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index 94f6dc26624..b10856571a6 100644 --- a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -4,34 +4,11 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl Trait for Ref {} | ^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl Trait for Ref<'_> {} | ++++ -error: incompatible lifetime on type - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 - | -LL | pub fn repro(_: Wrapper<Ref>); - | ^^^^^^^^^^^^ - | -note: because this has an unmet lifetime requirement - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:8:23 - | -LL | pub struct Wrapper<T: Trait>(T); - | ^^^^^ introduces a `'static` lifetime requirement -note: the anonymous lifetime as defined here... - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:29 - | -LL | pub fn repro(_: Wrapper<Ref>); - | ^^^ -note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:1 - | -LL | impl Trait for Ref {} - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0726`. |
