diff options
240 files changed, 4090 insertions, 2273 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c207e7ea11..9bde9c4fbff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,6 +93,9 @@ jobs: - name: show the current environment run: src/ci/scripts/dump-environment.sh if: success() && !env.SKIP_JOB + - name: install awscli + run: src/ci/scripts/install-awscli.sh + if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh if: success() && !env.SKIP_JOB @@ -345,8 +348,8 @@ jobs: os: macos-13 - name: dist-aarch64-apple env: - SCRIPT: "./x.py dist bootstrap --include-default-paths --stage 2" - RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --host=aarch64-apple-darwin --target=aarch64-apple-darwin --enable-full-tools --enable-sanitizers --enable-profiler --disable-docs --set rust.jemalloc --set llvm.ninja=false" + SCRIPT: "./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin" + RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin" RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 SELECT_XCODE: /Applications/Xcode_13.4.1.app USE_XCODE_CLANG: 1 @@ -356,8 +359,7 @@ jobs: NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 - JEMALLOC_SYS_WITH_LG_PAGE: 14 - os: macos-13 + os: macos-13-xlarge - name: x86_64-msvc env: RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler" @@ -458,6 +460,9 @@ jobs: - name: show the current environment run: src/ci/scripts/dump-environment.sh if: success() && !env.SKIP_JOB + - name: install awscli + run: src/ci/scripts/install-awscli.sh + if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh if: success() && !env.SKIP_JOB @@ -578,6 +583,9 @@ jobs: - name: show the current environment run: src/ci/scripts/dump-environment.sh if: success() && !env.SKIP_JOB + - name: install awscli + run: src/ci/scripts/install-awscli.sh + if: success() && !env.SKIP_JOB - name: install sccache run: src/ci/scripts/install-sccache.sh if: success() && !env.SKIP_JOB diff --git a/Cargo.lock b/Cargo.lock index 232c9c9441f..6c96f415f44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,7 @@ dependencies = [ "tester", "tokio", "toml 0.7.5", - "ui_test 0.20.0", + "ui_test", "walkdir", ] @@ -614,6 +614,7 @@ dependencies = [ "if_chain", "itertools", "rustc-semver", + "serde", ] [[package]] @@ -2511,7 +2512,7 @@ dependencies = [ "rustc_version", "serde", "smallvec", - "ui_test 0.21.2", + "ui_test", ] [[package]] @@ -3696,7 +3697,6 @@ version = "0.0.0" dependencies = [ "arrayvec", "bitflags 1.3.2", - "cfg-if", "elsa", "ena", "indexmap 2.0.0", @@ -4528,7 +4528,6 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ - "cfg-if", "indexmap 2.0.0", "md-5", "rustc_arena", @@ -5742,33 +5741,6 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "ui_test" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd8fb9b15c8332cf51bfc2dc4830063b2446a9c9d732421b56f2478024a3971" -dependencies = [ - "annotate-snippets", - "anyhow", - "bstr", - "cargo-platform", - "cargo_metadata 0.15.4", - "color-eyre", - "colored", - "comma", - "crossbeam-channel", - "indicatif", - "lazy_static", - "levenshtein", - "prettydiff", - "regex", - "rustc_version", - "rustfix", - "serde", - "serde_json", - "tempfile", -] - -[[package]] -name = "ui_test" version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf4bf7c184b8dfc7a4d3b90df789b1eb992ee42811cd115f32a7a1eb781058d" diff --git a/README.md b/README.md index f0c45f341d8..a88ee4b8bf0 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,20 @@ standard library, and documentation. If you wish to _contribute_ to the compiler, you should read [CONTRIBUTING.md](CONTRIBUTING.md) instead. +<details> +<summary>Table of content</summary> + +- [Quick Start](#quick-start) +- [Installing from Source](#installing-from-source) +- [Building Documentation](#building-documentation) +- [Notes](#notes) +- [Getting Help](#getting-help) +- [Contributing](#contributing) +- [License](#license) +- [Trademark](#trademark) + +</details> + ## Quick Start Read ["Installation"] from [The Book]. diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 00816c0f253..ecf7930ff6b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2263,6 +2263,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { current: usize, found: usize, prop_expr: Option<&'tcx hir::Expr<'tcx>>, + call: Option<&'tcx hir::Expr<'tcx>>, } impl<'tcx> Visitor<'tcx> for NestedStatementVisitor<'tcx> { @@ -2272,6 +2273,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.current -= 1; } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind { + if self.span == rcvr.span.source_callsite() { + self.call = Some(expr); + } + } if self.span == expr.span.source_callsite() { self.found = self.current; if self.prop_expr.is_none() { @@ -2295,6 +2301,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { current: 0, found: 0, prop_expr: None, + call: None, }; visitor.visit_stmt(stmt); @@ -2316,6 +2323,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { && let Some(p) = sm.span_to_margin(stmt.span) && let Ok(s) = sm.span_to_snippet(proper_span) { + if let Some(call) = visitor.call + && let hir::ExprKind::MethodCall(path, _, [], _) = call.kind + && path.ident.name == sym::iter + && let Some(ty) = expr_ty + { + err.span_suggestion_verbose( + path.ident.span, + format!( + "consider consuming the `{ty}` when turning it into an \ + `Iterator`", + ), + "into_iter".to_string(), + Applicability::MaybeIncorrect, + ); + } if !is_format_arguments_item { let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); err.multipart_suggestion_verbose( diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml index 8b4efd4e394..aa1a2bad2cf 100644 --- a/compiler/rustc_codegen_cranelift/.cirrus.yml +++ b/compiler/rustc_codegen_cranelift/.cirrus.yml @@ -3,7 +3,7 @@ task: freebsd_instance: image: freebsd-13-2-release-amd64 setup_rust_script: - - pkg install -y git bash + - pkg install -y git bash binutils - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh --default-toolchain none -y --profile=minimal target_cache: diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 652d6eca3f6..47d9a3b93f7 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -50,10 +50,12 @@ jobs: - os: ubuntu-latest env: TARGET_TRIPLE: aarch64-unknown-linux-gnu - # s390x requires QEMU 6.1 or greater, we could build it from source, but ubuntu 22.04 comes with 6.2 by default - os: ubuntu-latest env: TARGET_TRIPLE: s390x-unknown-linux-gnu + - os: ubuntu-latest + env: + TARGET_TRIPLE: riscv64gc-unknown-linux-gnu - os: windows-latest env: TARGET_TRIPLE: x86_64-pc-windows-msvc @@ -92,6 +94,12 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-s390x-linux-gnu qemu-user + - name: Install riscv64gc toolchain and qemu + if: matrix.env.TARGET_TRIPLE == 'riscv64gc-unknown-linux-gnu' + run: | + sudo apt-get update + sudo apt-get install -y gcc-riscv64-linux-gnu qemu-user + - name: Prepare dependencies run: ./y.sh prepare diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 7c324421be9..8079913cb0f 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -45,18 +45,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b9d1a9e776c27ad55d7792a380785d1fe8c2d7b099eed8dbd8f4af2b598192" +checksum = "8e5e1df0da8488dd03b34afc134ba84b754d61862cc465932a9e5d07952f661e" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5528483314c2dd5da438576cd8a9d0b3cedad66fb8a4727f90cd319a81950038" +checksum = "77a17ca4e699a0aaf49a0c88f6311a864f321048aa63f6b787cab20eb5f93f10" dependencies = [ "bumpalo", "cranelift-bforest", @@ -75,39 +75,39 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f46a8318163f7682e35b8730ba93c1b586a2da8ce12a0ed545efc1218550f70" +checksum = "022f2793cdade1d37a1f755ac42938a3f832f533eac6cafc8b26b209544c3c06" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d1239cfd50eecfaed468d46943f8650e32969591868ad50111613704da6c70" +checksum = "a4d72dbb83c2ad788dec4ad0843070973cb48c35a3ca19b1e7437ac40834fd9c" [[package]] name = "cranelift-control" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc530560c8f16cc1d4dd7ea000c56f519c60d1a914977abe849ce555c35a61d" +checksum = "ae07cf26dcc90d546826d747ac63b6c40c916f34b03e92a6ae0422c28d771b8a" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f333fa641a9ad2bff0b107767dcb972c18c2bfab7969805a1d7e42449ccb0408" +checksum = "c2fe6b7e49820893691aea497f36257e9d6f52061d8c4758d61d802d5f101a3d" [[package]] name = "cranelift-frontend" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abf6563015a80f03f8bc4df307d0a81363f4eb73108df3a34f6e66fb6d5307" +checksum = "44f497576ca3674581581601b6a55ccc1b43447217648c880e5bce70db3cf659" dependencies = [ "cranelift-codegen", "log", @@ -117,15 +117,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb29d0edc8a5c029ed0f7ca77501f272738e3c410020b4a00f42ffe8ad2a8aa" +checksum = "b96aa02eac00fffee13b0cd37d17874ccdb3d5458983041accd825ef78ce6454" [[package]] name = "cranelift-jit" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16e8c5e212b1e63658aada17553497e7a259acab61f044d1f185527efa609fb" +checksum = "b1d6e0e308c873eefc185745a6b21daec2a10f7554c9fb67e334c2d7d756d979" dependencies = [ "anyhow", "cranelift-codegen", @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b5fd273e1a959e920c7a9d790b1646d31acc8782bb549bad5ab85dd2fc9aa7" +checksum = "c1aa8ebb06eced4e478c3f94f1d65d4e7c93493f4640057912b27a3e34b84841" dependencies = [ "anyhow", "cranelift-codegen", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006056a7fa920870bad06bf8e1b3033d70cbb7ee625b035efa9d90882a931868" +checksum = "2870170ca44054b202c737626607b87be6e35655084bd94a6ff807a5812ba7df" dependencies = [ "cranelift-codegen", "libc", @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.100.0" +version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8be1b0e7720f30fec31be0c0b0b23caef2a73fa751190c6a251c1362e8f8c9" +checksum = "20647761742d17dabac8205da958910ede78599550e06418a16711a3ee2fc897" dependencies = [ "anyhow", "cranelift-codegen", @@ -374,9 +374,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "13.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ff5f3707a5e3797deeeeac6ac26b2e1dd32dbc06693c0ab52e8ac4d18ec706" +checksum = "a3a5dda53ad6993f9b0a2d65fb49e0348a7232a27a8794064122870d6ee19eb2" dependencies = [ "cfg-if", "libc", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 28a37b7995b..5ad5e0e8140 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.100", features = ["unwind", "all-arch"] } -cranelift-frontend = { version = "0.100" } -cranelift-module = { version = "0.100" } -cranelift-native = { version = "0.100" } -cranelift-jit = { version = "0.100", optional = true } -cranelift-object = { version = "0.100" } +cranelift-codegen = { version = "0.101", features = ["unwind", "all-arch"] } +cranelift-frontend = { version = "0.101" } +cranelift-module = { version = "0.101" } +cranelift-native = { version = "0.101" } +cranelift-jit = { version = "0.101", optional = true } +cranelift-object = { version = "0.101" } target-lexicon = "0.12.0" gimli = { version = "0.28", default-features = false, features = ["write"]} object = { version = "0.32", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 31a4b209826..1ed896c6bf0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -1,3 +1,4 @@ +use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; @@ -259,6 +260,14 @@ fn build_clif_sysroot_for_triple( // inlining. rustflags.push("-Zinline-mir".to_owned()); } + if let Some(prefix) = env::var_os("CG_CLIF_STDLIB_REMAP_PATH_PREFIX") { + rustflags.push("--remap-path-prefix".to_owned()); + rustflags.push(format!( + "{}={}", + STDLIB_SRC.to_path(dirs).to_str().unwrap(), + prefix.to_str().unwrap() + )); + } compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); maybe_incremental(&mut build_cmd); diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index 16e7a4bafae..c68968b4fde 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -143,6 +143,7 @@ impl GitRepo { RelPath::PATCHES.to_path(dirs).join(format!("{}-lock.toml", self.patch_name)); let target_lockfile = download_dir.join("Cargo.lock"); if source_lockfile.exists() { + assert!(!target_lockfile.exists()); fs::copy(source_lockfile, target_lockfile).unwrap(); } else { assert!(target_lockfile.exists()); diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 95ff6b75422..1e24d1b113f 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -104,8 +104,8 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ pub(crate) static RAND_REPO: GitRepo = GitRepo::github( "rust-random", "rand", - "f3dd0b885c4597b9617ca79987a0dd899ab29fcb", - "3f869e4fcd602b66", + "9a02c819cc1e4ec6959ae25eafbb5cf6acb68234", + "4934f0afb1d1c2ca", "rand", ); @@ -125,7 +125,7 @@ pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github( "rust-lang", "portable-simd", "4825b2a64d765317066948867e8714674419359b", - "8b188cc41f5af835", + "9e67d07c00f5fb0b", "portable-simd", ); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index 9f24c043a50..149f1618f5c 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -42,6 +42,16 @@ impl Compiler { "/usr/s390x-linux-gnu".to_owned(), ]; } + "riscv64gc-unknown-linux-gnu" => { + // We are cross-compiling for riscv64. Use the correct linker and run tests in qemu. + self.rustflags.push("-Clinker=riscv64-linux-gnu-gcc".to_owned()); + self.rustdocflags.push("-Clinker=riscv64-linux-gnu-gcc".to_owned()); + self.runner = vec![ + "qemu-riscv64".to_owned(), + "-L".to_owned(), + "/usr/riscv64-linux-gnu".to_owned(), + ]; + } "x86_64-pc-windows-gnu" => { // We are cross-compiling for Windows. Run tests in wine. self.runner = vec!["wine".to_owned()]; diff --git a/compiler/rustc_codegen_cranelift/patches/0002-rand-Disable-failing-test.patch b/compiler/rustc_codegen_cranelift/patches/0002-rand-Disable-failing-test.patch deleted file mode 100644 index ae13ab3b0ca..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/0002-rand-Disable-failing-test.patch +++ /dev/null @@ -1,24 +0,0 @@ -From a8fb97120d71252538b6b026695df40d02696bdb Mon Sep 17 00:00:00 2001 -From: bjorn3 <bjorn3@users.noreply.github.com> -Date: Sat, 15 Aug 2020 20:04:38 +0200 -Subject: [PATCH] [rand] Disable failing test - ---- - src/distributions/uniform.rs | 1 + - 1 file changed, 1 insertion(+), 0 deletions(-) - -diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs -index 480b859..c80bb6f 100644 ---- a/src/distributions/uniform.rs -+++ b/src/distributions/uniform.rs -@@ -1314,6 +1314,7 @@ mod tests { - not(target_arch = "wasm32"), - not(target_arch = "asmjs") - ))] -+ #[ignore] // Requires unwinding - fn test_float_assertions() { - use super::SampleUniform; - use std::panic::catch_unwind; --- -2.20.1 - diff --git a/compiler/rustc_codegen_cranelift/patches/0003-rand-Disable-rand-tests-on-mingw.patch b/compiler/rustc_codegen_cranelift/patches/0003-rand-Disable-rand-tests-on-mingw.patch deleted file mode 100644 index eb452c5cd37..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/0003-rand-Disable-rand-tests-on-mingw.patch +++ /dev/null @@ -1,47 +0,0 @@ -From eec874c889b8d24e5ad50faded24288150f057b1 Mon Sep 17 00:00:00 2001 -From: Afonso Bordado <afonsobordado@az8.co> -Date: Tue, 27 Sep 2022 08:13:58 +0100 -Subject: [PATCH] Disable rand tests on mingw - ---- - rand_distr/src/pareto.rs | 2 ++ - rand_distr/tests/value_stability.rs | 4 ++++ - 2 files changed, 6 insertions(+) - -diff --git a/rand_distr/src/pareto.rs b/rand_distr/src/pareto.rs -index 217899e..9cedeb7 100644 ---- a/rand_distr/src/pareto.rs -+++ b/rand_distr/src/pareto.rs -@@ -107,6 +107,8 @@ mod tests { - } - - #[test] -+ // This is broken on x86_64-pc-windows-gnu presumably due to a broken powf implementation -+ #[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)] - fn value_stability() { - fn test_samples<F: Float + Debug + Display + LowerExp, D: Distribution<F>>( - distr: D, thresh: F, expected: &[F], -diff --git a/rand_distr/tests/value_stability.rs b/rand_distr/tests/value_stability.rs -index 192ba74..0101ace 100644 ---- a/rand_distr/tests/value_stability.rs -+++ b/rand_distr/tests/value_stability.rs -@@ -72,6 +72,8 @@ fn unit_disc_stability() { - } - - #[test] -+// This is broken on x86_64-pc-windows-gnu -+#[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)] - fn pareto_stability() { - test_samples(213, Pareto::new(1.0, 1.0).unwrap(), &[ - 1.0423688f32, 2.1235929, 4.132709, 1.4679428, -@@ -143,6 +145,8 @@ fn inverse_gaussian_stability() { - } - - #[test] -+// This is broken on x86_64-pc-windows-gnu -+#[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)] - fn gamma_stability() { - // Gamma has 3 cases: shape == 1, shape < 1, shape > 1 - test_samples(223, Gamma::new(1.0, 5.0).unwrap(), &[ --- -2.25.1 diff --git a/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml b/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml deleted file mode 100644 index 5c9dc7b361f..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml +++ /dev/null @@ -1,304 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "core_simd" -version = "0.1.0" -dependencies = [ - "proptest", - "std_float", - "test_helpers", - "wasm-bindgen", - "wasm-bindgen-test", -] - -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e6c80c1139113c28ee4670dc50cc42915228b51f56a9e407f0ec60f966646f" -dependencies = [ - "bitflags", - "byteorder", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_xorshift" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" -dependencies = [ - "rand_core", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "std_float" -version = "0.1.0" -dependencies = [ - "core_simd", -] - -[[package]] -name = "syn" -version = "2.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "test_helpers" -version = "0.1.0" -dependencies = [ - "proptest", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml index de89c4f5eff..f10b4d6b9d4 100644 --- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml +++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" dependencies = [ "rustc-std-workspace-core", ] @@ -361,7 +361,6 @@ version = "0.0.0" dependencies = [ "addr2line", "alloc", - "cc", "cfg-if", "compiler_builtins", "core", diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 86ef127badd..3735ac1c17b 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-10-09" +channel = "nightly-2023-10-21" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 60ac6bc9951..3e48fb006de 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash set -e -./y.sh build +# Compiletest expects all standard library paths to start with /rustc/FAKE_PREFIX. +# CG_CLIF_STDLIB_REMAP_PATH_PREFIX will cause cg_clif's build system to pass +# --remap-path-prefix to handle this. +CG_CLIF_STDLIB_REMAP_PATH_PREFIX=/rustc/FAKE_PREFIX ./y.sh build echo "[SETUP] Rust fork" git clone https://github.com/rust-lang/rust.git || true @@ -13,23 +16,6 @@ git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')" git -c user.name=Dummy -c user.email=dummy@example.com -c commit.gpgSign=false \ am ../patches/*-stdlib-*.patch -git apply - <<EOF -diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml -index d95b5b7f17f..00b6f0e3635 100644 ---- a/library/alloc/Cargo.toml -+++ b/library/alloc/Cargo.toml -@@ -8,7 +8,7 @@ edition = "2018" - - [dependencies] - core = { path = "../core" } --compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] } -+compiler_builtins = { version = "0.1.66", features = ['rustc-dep-of-std', 'no-asm'] } - - [dev-dependencies] - rand = { version = "0.8.5", default-features = false, features = ["alloc"] } - rand_xorshift = "0.3.0" -EOF - cat > config.toml <<EOF change-id = 115898 @@ -49,11 +35,6 @@ verbose-tests = false EOF popd -# FIXME remove once inline asm is fully supported -export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc" - -export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd build/stdlib; pwd)" - # Allow the testsuite to use llvm tools host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") export LLVM_BIN_DIR="$(rustc --print sysroot)/lib/rustlib/$host_triple/bin" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index c83efa51e9e..b485f2571cc 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -27,8 +27,6 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR # missing features # ================ -rm -r tests/run-make/comment-section # cg_clif doesn't yet write the .comment section - # requires stack unwinding # FIXME add needs-unwind to these tests rm -r tests/run-make/libtest-junit @@ -44,10 +42,8 @@ rm tests/ui/proc-macro/allowed-signatures.rs rm tests/ui/proc-macro/no-mangle-in-proc-macro-issue-111888.rs # vendor intrinsics -rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected +rm tests/ui/sse2.rs # CodegenBackend::target_features not yet implemented rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant" -rm tests/ui/simd/intrinsic/generic-bswap-byte.rs # simd_bswap not yet implemented -rm tests/ui/simd/intrinsic/generic-arithmetic-pass.rs # many missing simd intrinsics # exotic linkages rm tests/ui/issues/issue-33992.rs # unsupported linkages @@ -115,21 +111,6 @@ rm tests/ui/consts/issue-33537.rs # same rm tests/ui/layout/valid_range_oob.rs # different ICE message rm tests/ui/const-generics/generic_const_exprs/issue-80742.rs # gives error instead of ICE with cg_clif -rm tests/ui/consts/issue-miri-1910.rs # different error message -rm tests/ui/consts/offset_ub.rs # same -rm tests/ui/consts/const-eval/ub-slice-get-unchecked.rs # same -rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # same -rm tests/ui/lint/lint-const-item-mutation.rs # same -rm tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs # same -rm tests/ui/suggestions/derive-trait-for-method-call.rs # same -rm tests/ui/typeck/issue-46112.rs # same -rm tests/ui/consts/const_cmp_type_id.rs # same -rm tests/ui/consts/issue-73976-monomorphic.rs # same -rm tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs # same -rm tests/ui/consts/const-eval/nonnull_as_ref_ub.rs # same -rm tests/ui/consts/issue-94675.rs # same -rm tests/ui/associated-types/issue-85103-layout-debug.rs # same - # rustdoc-clif passes extra args, suppressing the help message when no args are passed rm -r tests/run-make/issue-88756-default-output diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 84bfe15c13f..e6edc452cfb 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -20,7 +20,7 @@ use crate::prelude::*; pub(crate) fn producer() -> String { format!( - "cg_clif (rustc {}, cranelift {})", + "rustc version {} with cranelift {}", rustc_interface::util::rustc_version_str().unwrap_or("unknown version"), cranelift_codegen::VERSION, ) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 49f51f9f956..d3a7decc543 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -394,28 +394,31 @@ pub(crate) fn run_aot( let modules = tcx.sess.time("codegen mono items", || { cgus.iter() .enumerate() - .map(|(i, cgu)| match cgu_reuse[i] { - CguReuse::No => { - let dep_node = cgu.codegen_dep_node(tcx); - tcx.dep_graph - .with_task( - dep_node, - tcx, - ( - backend_config.clone(), - global_asm_config.clone(), - cgu.name(), - concurrency_limiter.acquire(tcx.sess.diagnostic()), - ), - module_codegen, - Some(rustc_middle::dep_graph::hash_result), - ) - .0 - } - CguReuse::PreLto => unreachable!("LTO not yet supported"), - CguReuse::PostLto => { - concurrency_limiter.job_already_done(); - OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu)) + .map(|(i, cgu)| { + let cgu_reuse = + if backend_config.disable_incr_cache { CguReuse::No } else { cgu_reuse[i] }; + match cgu_reuse { + CguReuse::No => { + let dep_node = cgu.codegen_dep_node(tcx); + tcx.dep_graph + .with_task( + dep_node, + tcx, + ( + backend_config.clone(), + global_asm_config.clone(), + cgu.name(), + concurrency_limiter.acquire(tcx.sess.diagnostic()), + ), + module_codegen, + Some(rustc_middle::dep_graph::hash_result), + ) + .0 + } + CguReuse::PreLto | CguReuse::PostLto => { + concurrency_limiter.job_already_done(); + OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu)) + } } }) .collect::<Vec<_>>() diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index ebd9b728d90..6692d1b85ea 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -81,6 +81,10 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, } } +pub(crate) fn asm_supported(tcx: TyCtxt<'_>) -> bool { + cfg!(feature = "inline_asm") && !tcx.sess.target.is_like_windows +} + #[derive(Debug)] pub(crate) struct GlobalAsmConfig { asm_enabled: bool, @@ -90,10 +94,8 @@ pub(crate) struct GlobalAsmConfig { impl GlobalAsmConfig { pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { - let asm_enabled = cfg!(feature = "inline_asm") && !tcx.sess.target.is_like_windows; - GlobalAsmConfig { - asm_enabled, + asm_enabled: asm_supported(tcx), assembler: crate::toolchain::get_toolchain_binary(tcx.sess, "as"), output_filenames: tcx.output_filenames(()).clone(), } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index dd2127d554d..ed077234254 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -8,6 +8,7 @@ use rustc_span::sym; use rustc_target::asm::*; use target_lexicon::BinaryFormat; +use crate::global_asm::asm_supported; use crate::prelude::*; enum CInlineAsmOperand<'tcx> { @@ -44,9 +45,13 @@ pub(crate) fn codegen_inline_asm<'tcx>( ) { // FIXME add .eh_frame unwind info directives - if !template.is_empty() - && (cfg!(not(feature = "inline_asm")) || fx.tcx.sess.target.is_like_windows) - { + if !asm_supported(fx.tcx) { + if template.is_empty() { + let destination_block = fx.get_block(destination.unwrap()); + fx.bcx.ins().jump(destination_block, &[]); + return; + } + // Used by panic_abort if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { fx.bcx.ins().trap(TrapCode::User(1)); @@ -144,6 +149,16 @@ pub(crate) fn codegen_inline_asm<'tcx>( return; } + // Used by core::hint::spin_loop() + if template[0] + == InlineAsmTemplatePiece::String(".insn i 0x0F, 0, x0, x0, 0x010".to_string()) + && template.len() == 1 + { + let destination_block = fx.get_block(destination.unwrap()); + fx.bcx.ins().jump(destination_block, &[]); + return; + } + // Used by measureme if template[0] == InlineAsmTemplatePiece::String("xor %eax, %eax".to_string()) && template[1] == InlineAsmTemplatePiece::String("\n".to_string()) @@ -223,6 +238,16 @@ pub(crate) fn codegen_inline_asm<'tcx>( fx.bcx.ins().jump(destination_block, &[]); return; } + + if cfg!(not(feature = "inline_asm")) { + fx.tcx.sess.span_err( + span, + "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift", + ); + } else { + fx.tcx.sess.span_err(span, "asm! and global_asm! are not yet supported on Windows"); + } + return; } let operands = operands @@ -745,6 +770,13 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // x19 is reserved by LLVM for the "base pointer", so rustc doesn't allow using it generated_asm.push_str(" mov x19, x0\n"); } + InlineAsmArch::RiscV64 => { + generated_asm.push_str(" addi sp, sp, -16\n"); + generated_asm.push_str(" sd ra, 8(sp)\n"); + generated_asm.push_str(" sd s1, 0(sp)\n"); // s1 is callee saved + // s1/x9 is reserved by LLVM for the "base pointer", so rustc doesn't allow using it + generated_asm.push_str(" mv s1, a0\n"); + } _ => unimplemented!("prologue for {:?}", arch), } } @@ -761,6 +793,12 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { generated_asm.push_str(" ldp fp, lr, [sp], #32\n"); generated_asm.push_str(" ret\n"); } + InlineAsmArch::RiscV64 => { + generated_asm.push_str(" ld s1, 0(sp)\n"); + generated_asm.push_str(" ld ra, 8(sp)\n"); + generated_asm.push_str(" addi sp, sp, 16\n"); + generated_asm.push_str(" ret\n"); + } _ => unimplemented!("epilogue for {:?}", arch), } } @@ -771,7 +809,10 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { generated_asm.push_str(" ud2\n"); } InlineAsmArch::AArch64 => { - generated_asm.push_str(" brk #0x1"); + generated_asm.push_str(" brk #0x1\n"); + } + InlineAsmArch::RiscV64 => { + generated_asm.push_str(" ebreak\n"); } _ => unimplemented!("epilogue_noreturn for {:?}", arch), } @@ -794,6 +835,11 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(); writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap(); } + InlineAsmArch::RiscV64 => { + generated_asm.push_str(" sd "); + reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap(); + writeln!(generated_asm, ", 0x{:x}(s1)", offset.bytes()).unwrap(); + } _ => unimplemented!("save_register for {:?}", arch), } } @@ -815,6 +861,11 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(); writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap(); } + InlineAsmArch::RiscV64 => { + generated_asm.push_str(" ld "); + reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap(); + writeln!(generated_asm, ", 0x{:x}(s1)", offset.bytes()).unwrap(); + } _ => unimplemented!("restore_register for {:?}", arch), } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8992de5a923..148193b5a97 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -1,6 +1,6 @@ -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![cfg_attr(all(doc, not(bootstrap)), allow(internal_features))] +#![cfg_attr(all(doc, not(bootstrap)), feature(rustdoc_internals))] +#![cfg_attr(all(doc, not(bootstrap)), doc(rust_logo))] #![feature(rustc_private)] // Note: please avoid adding other feature gates where possible #![warn(rust_2018_idioms)] @@ -189,7 +189,7 @@ impl CodegenBackend for CraneliftCodegenBackend { } fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> { - vec![] + vec![] // FIXME necessary for #[cfg(target_feature] } fn print_version(&self) { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index f75add90aa2..2f825b801ac 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -4,14 +4,16 @@ use crate::coverageinfo::ffi::CounterMappingRegion; use crate::coverageinfo::map_data::FunctionCoverage; use crate::llvm; -use rustc_codegen_ssa::traits::ConstMethods; +use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::bug; +use rustc_middle::mir; use rustc_middle::mir::coverage::CodeRegion; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefIdSet; use rustc_span::Symbol; /// Generates and exports the Coverage Map. @@ -94,8 +96,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Generate the LLVM IR representation of the coverage map and store it in a well-known global let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val); + let mut unused_function_names = Vec::new(); + let covfun_section_name = coverageinfo::covfun_section_name(cx); for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data { + if !is_used { + unused_function_names.push(mangled_function_name); + } + save_function_record( cx, &covfun_section_name, @@ -107,6 +115,25 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { ); } + // For unused functions, we need to take their mangled names and store them + // in a specially-named global array. LLVM's `InstrProfiling` pass will + // detect this global and include those names in its `__llvm_prf_names` + // section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.) + if !unused_function_names.is_empty() { + assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); + + let name_globals = unused_function_names + .into_iter() + .map(|mangled_function_name| cx.const_str(mangled_function_name).0) + .collect::<Vec<_>>(); + let initializer = cx.const_array(cx.type_ptr(), &name_globals); + + let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names"); + llvm::set_global_constant(array, true); + llvm::set_linkage(array, llvm::Linkage::InternalLinkage); + llvm::set_initializer(array, initializer); + } + // Save the coverage data value to LLVM IR coverageinfo::save_cov_data_to_mod(cx, cov_data_val); } @@ -287,13 +314,12 @@ fn save_function_record( /// `-Clink-dead-code` will not generate code for unused generic functions.) /// /// We can find the unused functions (including generic functions) by the set difference of all MIR -/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query -/// `codegened_and_inlined_items`). +/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`codegenned_and_inlined_items`). /// -/// These unused functions are then codegen'd in one of the CGUs which is marked as the -/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating -/// code regions for the same function more than once which can lead to linker errors regarding -/// duplicate symbols. +/// These unused functions don't need to be codegenned, but we do need to add them to the function +/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them. +/// We also end up adding their symbol names to a special global array that LLVM will include in +/// its embedded coverage data. fn add_unused_functions(cx: &CodegenCx<'_, '_>) { assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); @@ -324,19 +350,80 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) { }) .collect(); - let codegenned_def_ids = tcx.codegened_and_inlined_items(()); + let codegenned_def_ids = codegenned_and_inlined_items(tcx); - for non_codegenned_def_id in - eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id)) - { + // For each `DefId` that should have coverage instrumentation but wasn't + // codegenned, add it to the function coverage map as an unused function. + for def_id in eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id)) { // Skip any function that didn't have coverage data added to it by the // coverage instrumentor. - let body = tcx.instance_mir(ty::InstanceDef::Item(non_codegenned_def_id)); + let body = tcx.instance_mir(ty::InstanceDef::Item(def_id)); let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { continue; }; - debug!("generating unused fn: {:?}", non_codegenned_def_id); - cx.define_unused_fn(non_codegenned_def_id, function_coverage_info); + debug!("generating unused fn: {def_id:?}"); + let instance = declare_unused_fn(tcx, def_id); + add_unused_function_coverage(cx, instance, function_coverage_info); + } +} + +/// All items participating in code generation together with (instrumented) +/// items inlined into them. +fn codegenned_and_inlined_items(tcx: TyCtxt<'_>) -> DefIdSet { + let (items, cgus) = tcx.collect_and_partition_mono_items(()); + let mut visited = DefIdSet::default(); + let mut result = items.clone(); + + for cgu in cgus { + for item in cgu.items().keys() { + if let mir::mono::MonoItem::Fn(ref instance) = item { + let did = instance.def_id(); + if !visited.insert(did) { + continue; + } + let body = tcx.instance_mir(instance.def); + for block in body.basic_blocks.iter() { + for statement in &block.statements { + let mir::StatementKind::Coverage(_) = statement.kind else { continue }; + let scope = statement.source_info.scope; + if let Some(inlined) = scope.inlined_instance(&body.source_scopes) { + result.insert(inlined.def_id()); + } + } + } + } + } + } + + result +} + +fn declare_unused_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::Instance<'tcx> { + ty::Instance::new( + def_id, + ty::GenericArgs::for_item(tcx, def_id, |param, _| { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else { + tcx.mk_param_from_def(param) + } + }), + ) +} + +fn add_unused_function_coverage<'tcx>( + cx: &CodegenCx<'_, 'tcx>, + instance: ty::Instance<'tcx>, + function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo, +) { + // An unused function's mappings will automatically be rewritten to map to + // zero, because none of its counters/expressions are marked as seen. + let function_coverage = FunctionCoverage::unused(instance, function_coverage_info); + + if let Some(coverage_context) = cx.coverage_context() { + coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); + } else { + bug!("Could not get the `coverage_context`"); } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 204a73b788a..cf7c7e6be60 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -1,6 +1,5 @@ use crate::llvm; -use crate::abi::Abi; use crate::builder::Builder; use crate::common::CodegenCx; use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion}; @@ -12,17 +11,12 @@ use rustc_codegen_ssa::traits::{ StaticMethods, }; use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_llvm::RustString; use rustc_middle::bug; -use rustc_middle::mir::coverage::{CounterId, CoverageKind, FunctionCoverageInfo}; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::Coverage; -use rustc_middle::ty; -use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; -use rustc_middle::ty::GenericArgs; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; -use rustc_middle::ty::Ty; use std::cell::RefCell; @@ -30,8 +24,6 @@ pub(crate) mod ffi; pub(crate) mod map_data; pub mod mapgen; -const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START; - const VAR_ALIGN_BYTES: usize = 8; /// A context object for maintaining all state needed by the coverageinfo module. @@ -76,25 +68,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { bug!("Could not get the `coverage_context`"); } } - - /// Functions with MIR-based coverage are normally codegenned _only_ if - /// called. LLVM coverage tools typically expect every function to be - /// defined (even if unused), with at least one call to LLVM intrinsic - /// `instrprof.increment`. - /// - /// Codegen a small function that will never be called, with one counter - /// that will never be incremented. - /// - /// For used/called functions, the coverageinfo was already added to the - /// `function_coverage_map` (keyed by function `Instance`) during codegen. - /// But in this case, since the unused function was _not_ previously - /// codegenned, collect the function coverage info from MIR and add an - /// "unused" entry to the function coverage map. - fn define_unused_fn(&self, def_id: DefId, function_coverage_info: &'tcx FunctionCoverageInfo) { - let instance = declare_unused_fn(self, def_id); - codegen_unused_fn_and_counter(self, instance); - add_unused_function_coverage(self, instance, function_coverage_info); - } } impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { @@ -159,76 +132,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { } } -fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<'tcx> { - let tcx = cx.tcx; - - let instance = Instance::new( - def_id, - GenericArgs::for_item(tcx, def_id, |param, _| { - if let ty::GenericParamDefKind::Lifetime = param.kind { - tcx.lifetimes.re_erased.into() - } else { - tcx.mk_param_from_def(param) - } - }), - ); - - let llfn = cx.declare_fn( - tcx.symbol_name(instance).name, - cx.fn_abi_of_fn_ptr( - ty::Binder::dummy(tcx.mk_fn_sig( - [Ty::new_unit(tcx)], - Ty::new_unit(tcx), - false, - hir::Unsafety::Unsafe, - Abi::Rust, - )), - ty::List::empty(), - ), - None, - ); - - llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage); - llvm::set_visibility(llfn, llvm::Visibility::Default); - - assert!(cx.instances.borrow_mut().insert(instance, llfn).is_none()); - - instance -} - -fn codegen_unused_fn_and_counter<'tcx>(cx: &CodegenCx<'_, 'tcx>, instance: Instance<'tcx>) { - let llfn = cx.get_fn(instance); - let llbb = Builder::append_block(cx, llfn, "unused_function"); - let mut bx = Builder::build(cx, llbb); - let fn_name = bx.get_pgo_func_name_var(instance); - let hash = bx.const_u64(0); - let num_counters = bx.const_u32(1); - let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID)); - debug!( - "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, - index={:?}) for unused function: {:?}", - fn_name, hash, num_counters, index, instance - ); - bx.instrprof_increment(fn_name, hash, num_counters, index); - bx.ret_void(); -} - -fn add_unused_function_coverage<'tcx>( - cx: &CodegenCx<'_, 'tcx>, - instance: Instance<'tcx>, - function_coverage_info: &'tcx FunctionCoverageInfo, -) { - // An unused function's mappings will automatically be rewritten to map to - // zero, because none of its counters/expressions are marked as seen. - let function_coverage = FunctionCoverage::unused(instance, function_coverage_info); - - if let Some(coverage_context) = cx.coverage_context() { - coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); - } else { - bug!("Could not get the `coverage_context`"); - } -} - /// Calls llvm::createPGOFuncNameVar() with the given function instance's /// mangled function name. The LLVM API returns an llvm::GlobalVariable /// containing the function name, with the specific variable name and linkage diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f77bd53e76c..dce4e199e17 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] arrayvec = { version = "0.7", default-features = false } bitflags = "1.2.1" -cfg-if = "1.0" ena = "0.14.2" indexmap = { version = "2.0.0" } jobserver_crate = { version = "0.1.13", package = "jobserver" } diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index efdb44248d1..008565e4c7b 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -4,17 +4,20 @@ //! green/native threading. This is just a bare-bones enough solution for //! librustdoc, it is not production quality at all. -cfg_if! { - if #[cfg(target_os = "linux")] { +cfg_match! { + cfg(target_os = "linux") => { mod linux; use linux as imp; - } else if #[cfg(unix)] { + } + cfg(unix) => { mod unix; use unix as imp; - } else if #[cfg(windows)] { + } + cfg(windows) => { mod windows; use self::windows as imp; - } else { + } + _ => { mod unsupported; use unsupported as imp; } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 9511f1700e1..5d7f385c6e4 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -6,43 +6,44 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::default_hash_types)] +#![allow(rustc::potential_query_instability)] #![cfg_attr(not(bootstrap), doc(rust_logo))] #![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![deny(rustc::diagnostic_outside_of_impl)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(unsafe_op_in_unsafe_fn)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(allocator_api)] #![feature(array_windows)] #![feature(auto_traits)] #![feature(cell_leak)] +#![feature(cfg_match)] #![feature(core_intrinsics)] #![feature(extend_one)] #![feature(hash_raw_entry)] #![feature(hasher_prefixfree_extras)] +#![feature(lazy_cell)] +#![feature(lint_reasons)] +#![feature(macro_metavar_expr)] #![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] +#![feature(negative_impls)] #![feature(never_type)] -#![feature(type_alias_impl_trait)] -#![feature(lazy_cell)] +#![feature(ptr_alignment_type)] #![feature(rustc_attrs)] -#![feature(negative_impls)] +#![feature(strict_provenance)] #![feature(test)] #![feature(thread_id_value)] -#![feature(allocator_api)] -#![feature(lint_reasons)] +#![feature(type_alias_impl_trait)] #![feature(unwrap_infallible)] -#![feature(strict_provenance)] -#![feature(ptr_alignment_type)] -#![feature(macro_metavar_expr)] -#![allow(rustc::default_hash_types)] -#![allow(rustc::potential_query_instability)] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] -#![allow(internal_features)] -#![deny(unsafe_op_in_unsafe_fn)] +// tidy-alphabetical-end #[macro_use] extern crate tracing; #[macro_use] -extern crate cfg_if; -#[macro_use] extern crate rustc_macros; use std::fmt; diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index b067f9d4502..a8c442377fb 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -1,11 +1,12 @@ -cfg_if!( - if #[cfg(not(parallel_compiler))] { +cfg_match! { + cfg(not(parallel_compiler)) => { pub auto trait DynSend {} pub auto trait DynSync {} impl<T> DynSend for T {} impl<T> DynSync for T {} - } else { + } + _ => { #[rustc_on_unimplemented( message = "`{Self}` doesn't implement `DynSend`. \ Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`" @@ -48,13 +49,10 @@ cfg_if!( [std::io::StdoutLock<'_>] [std::io::StderrLock<'_>] ); - cfg_if!( - // Consistent with `std` - // `os_imp::Env` is `!Send` in these platforms - if #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] { - impl !DynSend for std::env::VarsOs {} - } - ); + + #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] + // Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms + impl !DynSend for std::env::VarsOs {} macro_rules! already_send { ($([$ty: ty])*) => { @@ -123,13 +121,10 @@ cfg_if!( [std::sync::mpsc::Receiver<T> where T] [std::sync::mpsc::Sender<T> where T] ); - cfg_if!( - // Consistent with `std` - // `os_imp::Env` is `!Sync` in these platforms - if #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] { - impl !DynSync for std::env::VarsOs {} - } - ); + + #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] + // Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms + impl !DynSync for std::env::VarsOs {} macro_rules! already_sync { ($([$ty: ty])*) => { @@ -183,7 +178,7 @@ cfg_if!( [thin_vec::ThinVec<T> where T: DynSync] ); } -); +} pub fn assert_dyn_sync<T: ?Sized + DynSync>() {} pub fn assert_dyn_send<T: ?Sized + DynSend>() {} diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index e688feb5fe1..ef7375a7320 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -859,8 +859,8 @@ fn get_thread_id() -> u32 { } // Memory reporting -cfg_if! { - if #[cfg(windows)] { +cfg_match! { + cfg(windows) => { pub fn get_resident_set_size() -> Option<usize> { use std::mem; @@ -885,7 +885,8 @@ cfg_if! { Some(pmc.WorkingSetSize) } - } else if #[cfg(target_os = "macos")] { + } + cfg(target_os = "macos") => { pub fn get_resident_set_size() -> Option<usize> { use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO}; use std::mem; @@ -903,7 +904,8 @@ cfg_if! { } } } - } else if #[cfg(unix)] { + } + cfg(unix) => { pub fn get_resident_set_size() -> Option<usize> { let field = 1; let contents = fs::read("/proc/self/statm").ok()?; @@ -912,7 +914,8 @@ cfg_if! { let npages = s.parse::<usize>().ok()?; Some(npages * 4096) } - } else { + } + _ => { pub fn get_resident_set_size() -> Option<usize> { None } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index cca043ba0d1..62fff604f81 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -109,8 +109,8 @@ mod mode { pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; -cfg_if! { - if #[cfg(not(parallel_compiler))] { +cfg_match! { + cfg(not(parallel_compiler)) => { use std::ops::Add; use std::cell::Cell; @@ -251,7 +251,8 @@ cfg_if! { MTLock(self.0.clone()) } } - } else { + } + _ => { pub use std::marker::Send as Send; pub use std::marker::Sync as Sync; diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 6c7e68246ea..8b93829623d 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -86,6 +86,7 @@ expand_module_circular = expand_module_file_not_found = file not found for module `{$name}` .help = to create the module `{$name}`, create file "{$default_path}" or "{$secondary_path}" + .note = if there is a `mod {$name}` elsewhere in the crate already, import it with `use crate::...` instead expand_module_in_block = cannot declare a non-inline module inside a block unless it has a path attribute diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index e3a0ae3570e..d86632c47fc 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -350,6 +350,7 @@ pub(crate) struct ModuleInBlockName { #[derive(Diagnostic)] #[diag(expand_module_file_not_found, code = "E0583")] #[help] +#[note] pub(crate) struct ModuleFileNotFound { #[primary_span] pub span: Span, 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 a1470cc69c3..74e2b157dd0 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1557,38 +1557,24 @@ fn compare_number_of_generics<'tcx>( DiagnosticId::Error("E0049".into()), ); - let mut suffix = None; - + let msg = + format!("expected {trait_count} {kind} parameter{}", pluralize!(trait_count),); if let Some(spans) = trait_spans { let mut spans = spans.iter(); if let Some(span) = spans.next() { - err.span_label( - *span, - format!( - "expected {} {} parameter{}", - trait_count, - kind, - pluralize!(trait_count), - ), - ); + err.span_label(*span, msg); } for span in spans { err.span_label(*span, ""); } } else { - suffix = Some(format!(", expected {trait_count}")); + err.span_label(tcx.def_span(trait_.def_id), msg); } if let Some(span) = span { err.span_label( span, - format!( - "found {} {} parameter{}{}", - impl_count, - kind, - pluralize!(impl_count), - suffix.unwrap_or_default(), - ), + format!("found {} {} parameter{}", impl_count, kind, pluralize!(impl_count),), ); } diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index aef880acbe1..84e986488a6 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -121,6 +121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prior_arm_ty, prior_arm_span, scrut_span: scrut.span, + scrut_hir_id: scrut.hir_id, source: match_src, prior_arms: other_arms.clone(), opt_suggest_box_span, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index fdeaf7f6850..97ab3d0b751 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -776,6 +776,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ref prior_arms, opt_suggest_box_span, scrut_span, + scrut_hir_id, .. }) => match source { hir::MatchSource::TryDesugar(scrut_hir_id) => { @@ -843,6 +844,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { err.subdiagnostic(subdiag); } + if let Some(hir::Node::Expr(m)) = self.tcx.hir().find_parent(scrut_hir_id) + && let Some(hir::Node::Stmt(stmt)) = self.tcx.hir().find_parent(m.hir_id) + && let hir::StmtKind::Expr(_) = stmt.kind + { + err.span_suggestion_verbose( + stmt.span.shrink_to_hi(), + "consider using a semicolon here, but this will discard any values \ + in the match arms", + ";", + Applicability::MaybeIncorrect, + ); + } if let Some(ret_sp) = opt_suggest_box_span { // Get return type span and point to it. self.suggest_boxing_for_return_impl_trait( diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index c8d42937f7b..918e484b9f4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3953,8 +3953,13 @@ declare_lint! { } declare_lint! { - /// The `non_exhaustive_omitted_patterns` lint detects when a wildcard (`_` or `..`) in a - /// pattern for a `#[non_exhaustive]` struct or enum is reachable. + /// The `non_exhaustive_omitted_patterns` lint aims to help consumers of a `#[non_exhaustive]` + /// struct or enum who want to match all of its fields/variants explicitly. + /// + /// The `#[non_exhaustive]` annotation forces matches to use wildcards, so exhaustiveness + /// checking cannot be used to ensure that all fields/variants are matched explicitly. To remedy + /// this, this allow-by-default lint warns the user when a match mentions some but not all of + /// the fields/variants of a `#[non_exhaustive]` struct or enum. /// /// ### Example /// @@ -3968,9 +3973,9 @@ declare_lint! { /// /// // in crate B /// #![feature(non_exhaustive_omitted_patterns_lint)] + /// #[warn(non_exhaustive_omitted_patterns)] /// match Bar::A { /// Bar::A => {}, - /// #[warn(non_exhaustive_omitted_patterns)] /// _ => {}, /// } /// ``` @@ -3978,29 +3983,32 @@ declare_lint! { /// This will produce: /// /// ```text - /// warning: reachable patterns not covered of non exhaustive enum + /// warning: some variants are not matched explicitly /// --> $DIR/reachable-patterns.rs:70:9 /// | - /// LL | _ => {} - /// | ^ pattern `B` not covered + /// LL | match Bar::A { + /// | ^ pattern `Bar::B` not covered /// | /// note: the lint level is defined here /// --> $DIR/reachable-patterns.rs:69:16 /// | /// LL | #[warn(non_exhaustive_omitted_patterns)] /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// = help: ensure that all possible cases are being handled by adding the suggested match arms + /// = help: ensure that all variants are matched explicitly by adding the suggested match arms /// = note: the matched value is of type `Bar` and the `non_exhaustive_omitted_patterns` attribute was found /// ``` /// + /// Warning: setting this to `deny` will make upstream non-breaking changes (adding fields or + /// variants to a `#[non_exhaustive]` struct or enum) break your crate. This goes against + /// expected semver behavior. + /// /// ### Explanation /// - /// Structs and enums tagged with `#[non_exhaustive]` force the user to add a - /// (potentially redundant) wildcard when pattern-matching, to allow for future - /// addition of fields or variants. The `non_exhaustive_omitted_patterns` lint - /// detects when such a wildcard happens to actually catch some fields/variants. - /// In other words, when the match without the wildcard would not be exhaustive. - /// This lets the user be informed if new fields/variants were added. + /// Structs and enums tagged with `#[non_exhaustive]` force the user to add a (potentially + /// redundant) wildcard when pattern-matching, to allow for future addition of fields or + /// variants. The `non_exhaustive_omitted_patterns` lint detects when such a wildcard happens to + /// actually catch some fields/variants. In other words, when the match without the wildcard + /// would not be exhaustive. This lets the user be informed if new fields/variants were added. pub NON_EXHAUSTIVE_OMITTED_PATTERNS, Allow, "detect when patterns of types marked `non_exhaustive` are missed", diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b5f873c9257..cd5206a837f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1882,12 +1882,6 @@ rustc_queries! { desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } } - /// All items participating in code generation together with items inlined into them. - query codegened_and_inlined_items(_: ()) -> &'tcx DefIdSet { - eval_always - desc { "collecting codegened and inlined items" } - } - query codegen_unit(sym: Symbol) -> &'tcx CodegenUnit<'tcx> { desc { "getting codegen unit `{sym}`" } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 9397e98343e..9e2ff3820af 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -541,6 +541,7 @@ pub struct MatchExpressionArmCause<'tcx> { pub prior_arm_ty: Ty<'tcx>, pub prior_arm_span: Span, pub scrut_span: Span, + pub scrut_hir_id: hir::HirId, pub source: hir::MatchSource, pub prior_arms: Vec<Span>, pub opt_suggest_box_span: Option<Span>, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 4f98932a88d..c09dd186418 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,6 +1,6 @@ use crate::{ fluent_generated as fluent, - thir::pattern::{deconstruct_pat::DeconstructedPat, MatchCheckCtxt}, + thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt}, }; use rustc_errors::{ error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -810,7 +810,7 @@ impl<'tcx> Uncovered<'tcx> { pub fn new<'p>( span: Span, cx: &MatchCheckCtxt<'p, 'tcx>, - witnesses: Vec<DeconstructedPat<'p, 'tcx>>, + witnesses: Vec<WitnessPat<'tcx>>, ) -> Self { let witness_1 = witnesses.get(0).unwrap().to_pat(cx); Self { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 9d38087fdeb..9156af3425a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,4 +1,4 @@ -use super::deconstruct_pat::{Constructor, DeconstructedPat}; +use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat}; use super::usefulness::{ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, }; @@ -279,7 +279,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let scrut = &self.thir[scrut]; let scrut_ty = scrut.ty; - let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty); + let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty, scrut.span); match source { // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }` @@ -473,7 +473,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let pattern = self.lower_pattern(&mut cx, pat); let pattern_ty = pattern.ty(); let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false }; - let report = compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty); + let report = + compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty, pattern.span()); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -662,7 +663,7 @@ fn is_let_irrefutable<'p, 'tcx>( pat: &'p DeconstructedPat<'p, 'tcx>, ) -> bool { let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty()); + let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty(), pat.span()); // Report if the pattern is unreachable, which can only occur when the type is uninhabited. // This also reports unreachable sub-patterns though, so we can't just replace it with an @@ -701,8 +702,8 @@ fn report_arm_reachability<'p, 'tcx>( } } -fn collect_non_exhaustive_tys<'p, 'tcx>( - pat: &DeconstructedPat<'p, 'tcx>, +fn collect_non_exhaustive_tys<'tcx>( + pat: &WitnessPat<'tcx>, non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>, ) { if matches!(pat.ctor(), Constructor::NonExhaustive) { @@ -718,7 +719,7 @@ fn non_exhaustive_match<'p, 'tcx>( thir: &Thir<'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - witnesses: Vec<DeconstructedPat<'p, 'tcx>>, + witnesses: Vec<WitnessPat<'tcx>>, arms: &[ArmId], expr_span: Span, ) -> ErrorGuaranteed { @@ -896,10 +897,10 @@ fn non_exhaustive_match<'p, 'tcx>( pub(crate) fn joined_uncovered_patterns<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - witnesses: &[DeconstructedPat<'p, 'tcx>], + witnesses: &[WitnessPat<'tcx>], ) -> String { const LIMIT: usize = 3; - let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string(); + let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_pat(cx).to_string(); match witnesses { [] => bug!(), [witness] => format!("`{}`", witness.to_pat(cx)), @@ -916,7 +917,7 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>( } pub(crate) fn pattern_not_covered_label( - witnesses: &[DeconstructedPat<'_, '_>], + witnesses: &[WitnessPat<'_>], joined_patterns: &str, ) -> String { format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns) @@ -927,7 +928,7 @@ fn adt_defined_here<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, err: &mut Diagnostic, ty: Ty<'tcx>, - witnesses: &[DeconstructedPat<'p, 'tcx>], + witnesses: &[WitnessPat<'tcx>], ) { let ty = ty.peel_refs(); if let ty::Adt(def, _) = ty.kind() { @@ -958,7 +959,7 @@ fn adt_defined_here<'p, 'tcx>( fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( cx: &MatchCheckCtxt<'p, 'tcx>, def: AdtDef<'tcx>, - patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>, + patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>, ) -> Vec<Span> { use Constructor::*; let mut covered = vec![]; diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index bbc0aeb66cf..86018340a69 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -629,18 +629,11 @@ pub(super) enum Constructor<'tcx> { /// `#[doc(hidden)]` ones. Hidden, /// Fake extra constructor for constructors that are not seen in the matrix, as explained in the - /// code for [`Constructor::split`]. The carried `bool` is used for the - /// `non_exhaustive_omitted_patterns` lint. - Missing { - nonexhaustive_enum_missing_visible_variants: bool, - }, + /// code for [`Constructor::split`]. + Missing, } impl<'tcx> Constructor<'tcx> { - pub(super) fn is_wildcard(&self) -> bool { - matches!(self, Wildcard) - } - pub(super) fn is_non_exhaustive(&self) -> bool { matches!(self, NonExhaustive) } @@ -778,14 +771,8 @@ impl<'tcx> Constructor<'tcx> { let all_missing = split_set.present.is_empty(); let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); - let ctor = if all_missing && !report_when_all_missing { - Wildcard - } else { - Missing { - nonexhaustive_enum_missing_visible_variants: split_set - .nonexhaustive_enum_missing_visible_variants, - } - }; + let ctor = + if all_missing && !report_when_all_missing { Wildcard } else { Missing }; smallvec![ctor] } else { split_set.present @@ -905,11 +892,9 @@ pub(super) enum ConstructorSet { /// either fully included in or disjoint from each constructor in the column. This avoids /// non-trivial intersections like between `0..10` and `5..15`. #[derive(Debug)] -struct SplitConstructorSet<'tcx> { - present: SmallVec<[Constructor<'tcx>; 1]>, - missing: Vec<Constructor<'tcx>>, - /// For the `non_exhaustive_omitted_patterns` lint. - nonexhaustive_enum_missing_visible_variants: bool, +pub(super) struct SplitConstructorSet<'tcx> { + pub(super) present: SmallVec<[Constructor<'tcx>; 1]>, + pub(super) missing: Vec<Constructor<'tcx>>, } impl ConstructorSet { @@ -1039,7 +1024,7 @@ impl ConstructorSet { /// constructors to 1/ determine which constructors of the type (if any) are missing; 2/ split /// constructors to handle non-trivial intersections e.g. on ranges or slices. #[instrument(level = "debug", skip(self, pcx, ctors), ret)] - fn split<'a, 'tcx>( + pub(super) fn split<'a, 'tcx>( &self, pcx: &PatCtxt<'_, '_, 'tcx>, ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone, @@ -1051,7 +1036,6 @@ impl ConstructorSet { let mut missing = Vec::new(); // Constructors in `ctors`, except wildcards. let mut seen = ctors.filter(|c| !(matches!(c, Opaque | Wildcard))); - let mut nonexhaustive_enum_missing_visible_variants = false; match self { ConstructorSet::Single => { if seen.next().is_none() { @@ -1063,6 +1047,7 @@ impl ConstructorSet { ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => { let seen_set: FxHashSet<_> = seen.map(|c| c.as_variant().unwrap()).collect(); let mut skipped_a_hidden_variant = false; + for variant in visible_variants { let ctor = Variant(*variant); if seen_set.contains(&variant) { @@ -1071,8 +1056,6 @@ impl ConstructorSet { missing.push(ctor); } } - nonexhaustive_enum_missing_visible_variants = - *non_exhaustive && !missing.is_empty(); for variant in hidden_variants { let ctor = Variant(*variant); @@ -1159,7 +1142,7 @@ impl ConstructorSet { ConstructorSet::Uninhabited => {} } - SplitConstructorSet { present, missing, nonexhaustive_enum_missing_visible_variants } + SplitConstructorSet { present, missing } } /// Compute the set of constructors missing from this column. @@ -1312,9 +1295,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// Values and patterns can be represented as a constructor applied to some fields. This represents /// a pattern in this form. -/// This also keeps track of whether the pattern has been found reachable during analysis. For this -/// reason we should be careful not to clone patterns for which we care about that. Use -/// `clone_and_forget_reachability` if you're sure. +/// This also uses interior mutability to keep track of whether the pattern has been found reachable +/// during analysis. For this reason they cannot be cloned. +/// A `DeconstructedPat` will almost always come from user input; the only exception are some +/// `Wildcard`s introduced during specialization. pub(crate) struct DeconstructedPat<'p, 'tcx> { ctor: Constructor<'tcx>, fields: Fields<'p, 'tcx>, @@ -1337,20 +1321,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) } } - /// Construct a pattern that matches everything that starts with this constructor. - /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern - /// `Some(_)`. - pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let fields = Fields::wildcards(pcx, &ctor); - DeconstructedPat::new(ctor, fields, pcx.ty, pcx.span) - } - - /// Clone this value. This method emphasizes that cloning loses reachability information and - /// should be done carefully. - pub(super) fn clone_and_forget_reachability(&self) -> Self { - DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span) - } - pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self { let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); let ctor; @@ -1533,98 +1503,16 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { DeconstructedPat::new(ctor, fields, pat.ty, pat.span) } - pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> { - let is_wildcard = |pat: &Pat<'_>| { - matches!(pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) - }; - let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx))); - let kind = match &self.ctor { - Single | Variant(_) => match self.ty.kind() { - ty::Tuple(..) => PatKind::Leaf { - subpatterns: subpatterns - .enumerate() - .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern }) - .collect(), - }, - ty::Adt(adt_def, _) if adt_def.is_box() => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - PatKind::Deref { subpattern: subpatterns.next().unwrap() } - } - ty::Adt(adt_def, args) => { - let variant_index = self.ctor.variant_index_for_adt(*adt_def); - let variant = &adt_def.variant(variant_index); - let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant) - .zip(subpatterns) - .map(|((field, _ty), pattern)| FieldPat { field, pattern }) - .collect(); - - if adt_def.is_enum() { - PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns } - } else { - PatKind::Leaf { subpatterns } - } - } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to reconstruct the correct constant pattern here. However a string - // literal pattern will never be reported as a non-exhaustiveness witness, so we - // ignore this issue. - ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty), - }, - Slice(slice) => { - match slice.kind { - FixedLen(_) => PatKind::Slice { - prefix: subpatterns.collect(), - slice: None, - suffix: Box::new([]), - }, - VarLen(prefix, _) => { - let mut subpatterns = subpatterns.peekable(); - let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); - if slice.array_len.is_some() { - // Improves diagnostics a bit: if the type is a known-size array, instead - // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. - // This is incorrect if the size is not known, since `[_, ..]` captures - // arrays of lengths `>= 1` whereas `[..]` captures any length. - while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { - prefix.pop(); - } - while subpatterns.peek().is_some() - && is_wildcard(subpatterns.peek().unwrap()) - { - subpatterns.next(); - } - } - let suffix: Box<[_]> = subpatterns.collect(); - let wild = Pat::wildcard_from_ty(self.ty); - PatKind::Slice { - prefix: prefix.into_boxed_slice(), - slice: Some(Box::new(wild)), - suffix, - } - } - } - } - &Str(value) => PatKind::Constant { value }, - IntRange(range) => return range.to_pat(cx.tcx, self.ty), - Wildcard | NonExhaustive | Hidden => PatKind::Wild, - Missing { .. } => bug!( - "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, - `Missing` should have been processed in `apply_constructors`" - ), - F32Range(..) | F64Range(..) | Opaque | Or => { - bug!("can't convert to pattern: {:?}", self) - } - }; - - Pat { ty: self.ty, span: DUMMY_SP, kind } - } - pub(super) fn is_or_pat(&self) -> bool { matches!(self.ctor, Or) } + pub(super) fn flatten_or_pat(&'p self) -> SmallVec<[&'p Self; 1]> { + if self.is_or_pat() { + self.iter_fields().flat_map(|p| p.flatten_or_pat()).collect() + } else { + smallvec![self] + } + } pub(super) fn ctor(&self) -> &Constructor<'tcx> { &self.ctor @@ -1804,3 +1692,131 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } } } + +/// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics +/// purposes. As such they don't use interning and can be cloned. +#[derive(Debug, Clone)] +pub(crate) struct WitnessPat<'tcx> { + ctor: Constructor<'tcx>, + pub(crate) fields: Vec<WitnessPat<'tcx>>, + ty: Ty<'tcx>, +} + +impl<'tcx> WitnessPat<'tcx> { + pub(super) fn new(ctor: Constructor<'tcx>, fields: Vec<Self>, ty: Ty<'tcx>) -> Self { + Self { ctor, fields, ty } + } + pub(super) fn wildcard(ty: Ty<'tcx>) -> Self { + Self::new(Wildcard, Vec::new(), ty) + } + + /// Construct a pattern that matches everything that starts with this constructor. + /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern + /// `Some(_)`. + pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, 'tcx>, ctor: Constructor<'tcx>) -> Self { + // Reuse `Fields::wildcards` to get the types. + let fields = Fields::wildcards(pcx, &ctor) + .iter_patterns() + .map(|deco_pat| Self::wildcard(deco_pat.ty())) + .collect(); + Self::new(ctor, fields, pcx.ty) + } + + pub(super) fn ctor(&self) -> &Constructor<'tcx> { + &self.ctor + } + pub(super) fn ty(&self) -> Ty<'tcx> { + self.ty + } + + pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Pat<'tcx> { + let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild); + let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx))); + let kind = match &self.ctor { + Single | Variant(_) => match self.ty.kind() { + ty::Tuple(..) => PatKind::Leaf { + subpatterns: subpatterns + .enumerate() + .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern }) + .collect(), + }, + ty::Adt(adt_def, _) if adt_def.is_box() => { + // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside + // of `std`). So this branch is only reachable when the feature is enabled and + // the pattern is a box pattern. + PatKind::Deref { subpattern: subpatterns.next().unwrap() } + } + ty::Adt(adt_def, args) => { + let variant_index = self.ctor.variant_index_for_adt(*adt_def); + let variant = &adt_def.variant(variant_index); + let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant) + .zip(subpatterns) + .map(|((field, _ty), pattern)| FieldPat { field, pattern }) + .collect(); + + if adt_def.is_enum() { + PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns } + } else { + PatKind::Leaf { subpatterns } + } + } + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // ignore this issue. + ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty), + }, + Slice(slice) => { + match slice.kind { + FixedLen(_) => PatKind::Slice { + prefix: subpatterns.collect(), + slice: None, + suffix: Box::new([]), + }, + VarLen(prefix, _) => { + let mut subpatterns = subpatterns.peekable(); + let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); + if slice.array_len.is_some() { + // Improves diagnostics a bit: if the type is a known-size array, instead + // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. + // This is incorrect if the size is not known, since `[_, ..]` captures + // arrays of lengths `>= 1` whereas `[..]` captures any length. + while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { + prefix.pop(); + } + while subpatterns.peek().is_some() + && is_wildcard(subpatterns.peek().unwrap()) + { + subpatterns.next(); + } + } + let suffix: Box<[_]> = subpatterns.collect(); + let wild = Pat::wildcard_from_ty(self.ty); + PatKind::Slice { + prefix: prefix.into_boxed_slice(), + slice: Some(Box::new(wild)), + suffix, + } + } + } + } + &Str(value) => PatKind::Constant { value }, + IntRange(range) => return range.to_pat(cx.tcx, self.ty), + Wildcard | NonExhaustive | Hidden => PatKind::Wild, + Missing { .. } => bug!( + "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, + `Missing` should have been processed in `apply_constructors`" + ), + F32Range(..) | F64Range(..) | Opaque | Or => { + bug!("can't convert to pattern: {:?}", self) + } + }; + + Pat { ty: self.ty, span: DUMMY_SP, kind } + } + + pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a WitnessPat<'tcx>> { + self.fields.iter() + } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 6cd73c7eaa9..a8cee5a61ed 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -213,7 +213,7 @@ //! or-patterns in the first column are expanded before being stored in the matrix. Specialization //! for a single patstack is done from a combination of [`Constructor::is_covered_by`] and //! [`PatStack::pop_head_constructor`]. The internals of how it's done mostly live in the -//! [`Fields`] struct. +//! [`super::deconstruct_pat::Fields`] struct. //! //! //! # Computing usefulness @@ -307,7 +307,7 @@ use self::ArmType::*; use self::Usefulness::*; -use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, Fields}; +use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat}; use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; use rustc_data_structures::captures::Captures; @@ -322,7 +322,6 @@ use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::fmt; -use std::iter::once; pub(crate) struct MatchCheckCtxt<'p, 'tcx> { pub(crate) tcx: TyCtxt<'tcx>, @@ -555,20 +554,20 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { /// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of /// witnesses of non-exhaustiveness when there are any. /// Which variant to use is dictated by `ArmType`. -#[derive(Debug)] -enum Usefulness<'p, 'tcx> { +#[derive(Debug, Clone)] +enum Usefulness<'tcx> { /// If we don't care about witnesses, simply remember if the pattern was useful. NoWitnesses { useful: bool }, /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole /// pattern is unreachable. - WithWitnesses(Vec<Witness<'p, 'tcx>>), + WithWitnesses(Vec<WitnessStack<'tcx>>), } -impl<'p, 'tcx> Usefulness<'p, 'tcx> { +impl<'tcx> Usefulness<'tcx> { fn new_useful(preference: ArmType) -> Self { match preference { // A single (empty) witness of reachability. - FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]), + FakeExtraWildcard => WithWitnesses(vec![WitnessStack(vec![])]), RealArm => NoWitnesses { useful: true }, } } @@ -605,8 +604,8 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { /// with the results of specializing with the other constructors. fn apply_constructor( self, - pcx: &PatCtxt<'_, 'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors + pcx: &PatCtxt<'_, '_, 'tcx>, + matrix: &Matrix<'_, 'tcx>, // used to compute missing ctors ctor: &Constructor<'tcx>, ) -> Self { match self { @@ -627,25 +626,18 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { // wildcards for fields, i.e. that matches everything that can be built with it. // For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get // the pattern `Some(_)`. - let new_patterns: Vec<DeconstructedPat<'_, '_>> = missing + let new_patterns: Vec<WitnessPat<'_>> = missing .into_iter() - .map(|missing_ctor| { - DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()) - }) + .map(|missing_ctor| WitnessPat::wild_from_ctor(pcx, missing_ctor.clone())) .collect(); witnesses .into_iter() .flat_map(|witness| { new_patterns.iter().map(move |pat| { - Witness( - witness - .0 - .iter() - .chain(once(pat)) - .map(DeconstructedPat::clone_and_forget_reachability) - .collect(), - ) + let mut stack = witness.clone(); + stack.0.push(pat.clone()); + stack }) }) .collect() @@ -667,15 +659,17 @@ enum ArmType { RealArm, } -/// A witness of non-exhaustiveness for error reporting, represented -/// as a list of patterns (in reverse order of construction) with -/// wildcards inside to represent elements that can take any inhabitant -/// of the type as a value. +/// A witness-tuple of non-exhaustiveness for error reporting, represented as a list of patterns (in +/// reverse order of construction) with wildcards inside to represent elements that can take any +/// inhabitant of the type as a value. /// -/// A witness against a list of patterns should have the same types -/// and length as the pattern matched against. Because Rust `match` -/// is always against a single pattern, at the end the witness will -/// have length 1, but in the middle of the algorithm, it can contain +/// This mirrors `PatStack`: they function similarly, except `PatStack` contains user patterns we +/// are inspecting, and `WitnessStack` contains witnesses we are constructing. +/// FIXME(Nadrieril): use the same order of patterns for both +/// +/// A `WitnessStack` should have the same types and length as the `PatStacks` we are inspecting +/// (except we store the patterns in reverse order). Because Rust `match` is always against a single +/// pattern, at the end the stack will have length 1. In the middle of the algorithm, it can contain /// multiple patterns. /// /// For example, if we are constructing a witness for the match against @@ -690,23 +684,37 @@ enum ArmType { /// # } /// ``` /// -/// We'll perform the following steps: -/// 1. Start with an empty witness -/// `Witness(vec![])` -/// 2. Push a witness `true` against the `false` -/// `Witness(vec![true])` -/// 3. Push a witness `Some(_)` against the `None` -/// `Witness(vec![true, Some(_)])` -/// 4. Apply the `Pair` constructor to the witnesses -/// `Witness(vec![Pair(Some(_), true)])` +/// We'll perform the following steps (among others): +/// - Start with a matrix representing the match +/// `PatStack(vec![Pair(None, _)])` +/// `PatStack(vec![Pair(_, false)])` +/// - Specialize with `Pair` +/// `PatStack(vec![None, _])` +/// `PatStack(vec![_, false])` +/// - Specialize with `Some` +/// `PatStack(vec![_, false])` +/// - Specialize with `_` +/// `PatStack(vec![false])` +/// - Specialize with `true` +/// // no patstacks left +/// - This is a non-exhaustive match: we have the empty witness stack as a witness. +/// `WitnessStack(vec![])` +/// - Apply `true` +/// `WitnessStack(vec![true])` +/// - Apply `_` +/// `WitnessStack(vec![true, _])` +/// - Apply `Some` +/// `WitnessStack(vec![true, Some(_)])` +/// - Apply `Pair` +/// `WitnessStack(vec![Pair(Some(_), true)])` /// /// The final `Pair(Some(_), true)` is then the resulting witness. -#[derive(Debug)] -pub(crate) struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>); +#[derive(Debug, Clone)] +pub(crate) struct WitnessStack<'tcx>(Vec<WitnessPat<'tcx>>); -impl<'p, 'tcx> Witness<'p, 'tcx> { +impl<'tcx> WitnessStack<'tcx> { /// Asserts that the witness contains a single pattern, and returns it. - fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> { + fn single_pattern(self) -> WitnessPat<'tcx> { assert_eq!(self.0.len(), 1); self.0.into_iter().next().unwrap() } @@ -724,13 +732,12 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { /// /// left_ty: struct X { a: (bool, &'static str), b: usize} /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } - fn apply_constructor(mut self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self { + fn apply_constructor(mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self { let pat = { let len = self.0.len(); let arity = ctor.arity(pcx); - let pats = self.0.drain((len - arity)..).rev(); - let fields = Fields::from_iter(pcx.cx, pats); - DeconstructedPat::new(ctor.clone(), fields, pcx.ty, pcx.span) + let fields = self.0.drain((len - arity)..).rev().collect(); + WitnessPat::new(ctor.clone(), fields, pcx.ty) }; self.0.push(pat); @@ -770,7 +777,7 @@ fn is_useful<'p, 'tcx>( lint_root: HirId, is_under_guard: bool, is_top_level: bool, -) -> Usefulness<'p, 'tcx> { +) -> Usefulness<'tcx> { debug!(?matrix, ?v); let Matrix { patterns: rows, .. } = matrix; @@ -837,8 +844,6 @@ fn is_useful<'p, 'tcx>( } // We split the head constructor of `v`. let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); - let is_non_exhaustive_and_wild = - cx.is_foreign_non_exhaustive_enum(ty) && v_ctor.is_wildcard(); // For each constructor, we compute whether there's a value that starts with it that would // witness the usefulness of `v`. let start_matrix = &matrix; @@ -859,50 +864,6 @@ fn is_useful<'p, 'tcx>( ) }); let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); - - // When all the conditions are met we have a match with a `non_exhaustive` enum - // that has the potential to trigger the `non_exhaustive_omitted_patterns` lint. - // To understand the workings checkout `Constructor::split` and `SplitWildcard::new/into_ctors` - if is_non_exhaustive_and_wild - // Only emit a lint on refutable patterns. - && cx.refutable - // We check that the match has a wildcard pattern and that wildcard is useful, - // meaning there are variants that are covered by the wildcard. Without the check - // for `witness_preference` the lint would trigger on `if let NonExhaustiveEnum::A = foo {}` - && usefulness.is_useful() && matches!(witness_preference, RealArm) - && matches!( - &ctor, - Constructor::Missing { nonexhaustive_enum_missing_visible_variants: true } - ) - { - let missing = ConstructorSet::for_ty(pcx.cx, pcx.ty) - .compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor)); - // Construct for each missing constructor a "wild" version of this constructor, that - // matches everything that can be built with it. For example, if `ctor` is a - // `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`. - let patterns = missing - .into_iter() - // Because of how we computed `nonexhaustive_enum_missing_visible_variants`, - // this will not return an empty `Vec`. - .filter(|c| !(matches!(c, Constructor::NonExhaustive | Constructor::Hidden))) - .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) - .collect::<Vec<_>>(); - - // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` - // is not exhaustive enough. - // - // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. - cx.tcx.emit_spanned_lint( - NON_EXHAUSTIVE_OMITTED_PATTERNS, - lint_root, - pcx.span, - NonExhaustiveOmittedPattern { - scrut_ty: pcx.ty, - uncovered: Uncovered::new(pcx.span, pcx.cx, patterns), - }, - ); - } - ret.extend(usefulness); } } @@ -914,6 +875,80 @@ fn is_useful<'p, 'tcx>( ret } +/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned +/// in a given column. This traverses patterns column-by-column, where a column is the intuitive +/// notion of "subpatterns that inspect the same subvalue". +/// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the +/// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete). +fn collect_nonexhaustive_missing_variants<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + column: &[&DeconstructedPat<'p, 'tcx>], +) -> Vec<WitnessPat<'tcx>> { + let ty = column[0].ty(); + let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false }; + + let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor())); + if set.present.is_empty() { + // We can't consistently handle the case where no constructors are present (since this would + // require digging deep through any type in case there's a non_exhaustive enum somewhere), + // so for consistency we refuse to handle the top-level case, where we could handle it. + return vec![]; + } + + let mut witnesses = Vec::new(); + if cx.is_foreign_non_exhaustive_enum(ty) { + witnesses.extend( + set.missing + .into_iter() + // This will list missing visible variants. + .filter(|c| !matches!(c, Constructor::Hidden | Constructor::NonExhaustive)) + .map(|missing_ctor| WitnessPat::wild_from_ctor(pcx, missing_ctor)), + ) + } + + // Recurse into the fields. + for ctor in set.present { + let arity = ctor.arity(pcx); + if arity == 0 { + continue; + } + + // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These + // columns may have different lengths in the presence of or-patterns (this is why we can't + // reuse `Matrix`). + let mut specialized_columns: Vec<Vec<_>> = (0..arity).map(|_| Vec::new()).collect(); + let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor())); + for pat in relevant_patterns { + let specialized = pat.specialize(pcx, &ctor); + for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) { + if subpat.is_or_pat() { + sub_column.extend(subpat.iter_fields()) + } else { + sub_column.push(subpat) + } + } + } + debug_assert!( + !specialized_columns[0].is_empty(), + "ctor {ctor:?} was listed as present but isn't" + ); + + let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor); + for (i, col_i) in specialized_columns.iter().enumerate() { + // Compute witnesses for each column. + let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i.as_slice()); + // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`, + // adding enough wildcards to match `arity`. + for wit in wits_for_col_i { + let mut pat = wild_pat.clone(); + pat.fields[i] = wit; + witnesses.push(pat); + } + } + } + witnesses +} + /// The arm of a match expression. #[derive(Clone, Copy, Debug)] pub(crate) struct MatchArm<'p, 'tcx> { @@ -940,7 +975,7 @@ pub(crate) struct UsefulnessReport<'p, 'tcx> { pub(crate) arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. - pub(crate) non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>, + pub(crate) non_exhaustiveness_witnesses: Vec<WitnessPat<'tcx>>, } /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which @@ -954,6 +989,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( arms: &[MatchArm<'p, 'tcx>], lint_root: HirId, scrut_ty: Ty<'tcx>, + scrut_span: Span, ) -> UsefulnessReport<'p, 'tcx> { let mut matrix = Matrix::empty(); let arm_usefulness: Vec<_> = arms @@ -978,9 +1014,39 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP)); let v = PatStack::from_pattern(wild_pattern); let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, false, true); - let non_exhaustiveness_witnesses = match usefulness { + let non_exhaustiveness_witnesses: Vec<_> = match usefulness { WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(), NoWitnesses { .. } => bug!(), }; + + // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting + // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. + if cx.refutable + && non_exhaustiveness_witnesses.is_empty() + && !matches!( + cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, lint_root).0, + rustc_session::lint::Level::Allow + ) + { + let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>(); + let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column); + + if !witnesses.is_empty() { + // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` + // is not exhaustive enough. + // + // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. + cx.tcx.emit_spanned_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + lint_root, + scrut_span, + NonExhaustiveOmittedPattern { + scrut_ty, + uncovered: Uncovered::new(scrut_span, cx, witnesses), + }, + ); + } + } + UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } } diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index a83ccf8fc3c..d07f59bc72a 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -169,22 +169,22 @@ impl CoverageCounters { self.bcb_counters[bcb].as_ref() } - pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option<BcbCounter> { - self.bcb_counters[bcb].take() - } - - pub(super) fn drain_bcb_counters( - &mut self, - ) -> impl Iterator<Item = (BasicCoverageBlock, BcbCounter)> + '_ { + pub(super) fn bcb_node_counters( + &self, + ) -> impl Iterator<Item = (BasicCoverageBlock, &BcbCounter)> { self.bcb_counters - .iter_enumerated_mut() - .filter_map(|(bcb, counter)| Some((bcb, counter.take()?))) + .iter_enumerated() + .filter_map(|(bcb, counter_kind)| Some((bcb, counter_kind.as_ref()?))) } - pub(super) fn drain_bcb_edge_counters( - &mut self, - ) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), BcbCounter)> + '_ { - self.bcb_edge_counters.drain() + /// For each edge in the BCB graph that has an associated counter, yields + /// that edge's *from* and *to* nodes, and its counter. + pub(super) fn bcb_edge_counters( + &self, + ) -> impl Iterator<Item = (BasicCoverageBlock, BasicCoverageBlock, &BcbCounter)> { + self.bcb_edge_counters + .iter() + .map(|(&(from_bcb, to_bcb), counter_kind)| (from_bcb, to_bcb, counter_kind)) } pub(super) fn take_expressions(&mut self) -> IndexVec<ExpressionId, Expression> { diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 6fdaff6b4c0..c9b36ba25ac 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -8,7 +8,7 @@ mod spans; mod tests; use self::counters::{BcbCounter, CoverageCounters}; -use self::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use self::graph::CoverageGraph; use self::spans::CoverageSpans; use crate::MirPass; @@ -104,7 +104,6 @@ struct Instrumentor<'a, 'tcx> { function_source_hash: u64, basic_coverage_blocks: CoverageGraph, coverage_counters: CoverageCounters, - mappings: Vec<Mapping>, } impl<'a, 'tcx> Instrumentor<'a, 'tcx> { @@ -145,7 +144,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { function_source_hash, basic_coverage_blocks, coverage_counters, - mappings: Vec::new(), } } @@ -168,148 +166,99 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // and all `Expression` dependencies (operands) are also generated, for any other // `BasicCoverageBlock`s not already associated with a coverage span. let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb); - let result = self - .coverage_counters - .make_bcb_counters(&mut self.basic_coverage_blocks, bcb_has_coverage_spans); - - if let Ok(()) = result { - //////////////////////////////////////////////////// - // Remove the counter or edge counter from of each coverage cpan's associated - // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR. - // - // `Coverage` statements injected from coverage spans will include the code regions - // (source code start and end positions) to be counted by the associated counter. - // - // These coverage-span-associated counters are removed from their associated - // `BasicCoverageBlock`s so that the only remaining counters in the `CoverageGraph` - // are indirect counters (to be injected next, without associated code regions). - self.inject_coverage_span_counters(&coverage_spans); - - //////////////////////////////////////////////////// - // For any remaining `BasicCoverageBlock` counters (that were not associated with - // any coverage span), inject `Coverage` statements (_without_ code region spans) - // to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on - // are in fact counted, even though they don't directly contribute to counting - // their own independent code region's coverage. - self.inject_indirect_counters(); - }; + self.coverage_counters + .make_bcb_counters(&mut self.basic_coverage_blocks, bcb_has_coverage_spans) + .unwrap_or_else(|e| { + bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e.message) + }); - if let Err(e) = result { - bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e.message) - }; + let mappings = self.create_mappings_and_inject_coverage_statements(&coverage_spans); self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: self.function_source_hash, num_counters: self.coverage_counters.num_counters(), expressions: self.coverage_counters.take_expressions(), - mappings: std::mem::take(&mut self.mappings), + mappings, })); } - /// Injects a single [`StatementKind::Coverage`] for each BCB that has one - /// or more coverage spans. - fn inject_coverage_span_counters(&mut self, coverage_spans: &CoverageSpans) { - let tcx = self.tcx; - let source_map = tcx.sess.source_map(); + /// For each [`BcbCounter`] associated with a BCB node or BCB edge, create + /// any corresponding mappings (for BCB nodes only), and inject any necessary + /// coverage statements into MIR. + fn create_mappings_and_inject_coverage_statements( + &mut self, + coverage_spans: &CoverageSpans, + ) -> Vec<Mapping> { + let source_map = self.tcx.sess.source_map(); let body_span = self.body_span; use rustc_session::RemapFileNameExt; let file_name = Symbol::intern(&self.source_file.name.for_codegen(self.tcx.sess).to_string_lossy()); - for (bcb, spans) in coverage_spans.bcbs_with_coverage_spans() { - let counter_kind = self.coverage_counters.take_bcb_counter(bcb).unwrap_or_else(|| { - bug!("Every BasicCoverageBlock should have a Counter or Expression"); - }); - - let term = counter_kind.as_term(); - self.mappings.extend(spans.iter().map(|&span| { - let code_region = make_code_region(source_map, file_name, span, body_span); - Mapping { code_region, term } - })); - - inject_statement( - self.mir_body, - self.make_mir_coverage_kind(&counter_kind), - self.bcb_leader_bb(bcb), - ); - } - } + let mut mappings = Vec::new(); + + // Process the counters and spans associated with BCB nodes. + for (bcb, counter_kind) in self.coverage_counters.bcb_node_counters() { + let spans = coverage_spans.spans_for_bcb(bcb); + let has_mappings = !spans.is_empty(); + + // If this BCB has any coverage spans, add corresponding mappings to + // the mappings table. + if has_mappings { + let term = counter_kind.as_term(); + mappings.extend(spans.iter().map(|&span| { + let code_region = make_code_region(source_map, file_name, span, body_span); + Mapping { code_region, term } + })); + } - /// At this point, any BCB with coverage counters has already had its counter injected - /// into MIR, and had its counter removed from `coverage_counters` (via `take_counter()`). - /// - /// Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not - /// associated with a coverage span, should only exist if the counter is an `Expression` - /// dependency (one of the expression operands). Collect them, and inject the additional - /// counters into the MIR, without a reportable coverage span. - fn inject_indirect_counters(&mut self) { - let mut bcb_counters_without_direct_coverage_spans = Vec::new(); - for (target_bcb, counter_kind) in self.coverage_counters.drain_bcb_counters() { - bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); - } - for ((from_bcb, target_bcb), counter_kind) in - self.coverage_counters.drain_bcb_edge_counters() - { - bcb_counters_without_direct_coverage_spans.push(( - Some(from_bcb), - target_bcb, - counter_kind, - )); + let do_inject = match counter_kind { + // Counter-increment statements always need to be injected. + BcbCounter::Counter { .. } => true, + // The only purpose of expression-used statements is to detect + // when a mapping is unreachable, so we only inject them for + // expressions with one or more mappings. + BcbCounter::Expression { .. } => has_mappings, + }; + if do_inject { + inject_statement( + self.mir_body, + self.make_mir_coverage_kind(counter_kind), + self.basic_coverage_blocks[bcb].leader_bb(), + ); + } } - for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans - { - match counter_kind { - BcbCounter::Counter { .. } => { - let inject_to_bb = if let Some(from_bcb) = edge_from_bcb { - // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in - // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the - // `target_bcb`; also called the `leader_bb`). - let from_bb = self.bcb_last_bb(from_bcb); - let to_bb = self.bcb_leader_bb(target_bcb); - - let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); - debug!( - "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ - BasicBlock {:?}, for unclaimed edge counter {:?}", - edge_from_bcb, from_bb, target_bcb, to_bb, new_bb, counter_kind, - ); - new_bb - } else { - let target_bb = self.bcb_last_bb(target_bcb); - debug!( - "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {:?}", - target_bcb, target_bb, counter_kind, - ); - target_bb - }; - - inject_statement( - self.mir_body, - self.make_mir_coverage_kind(&counter_kind), - inject_to_bb, - ); - } - // Experessions with no associated spans don't need to inject a statement. - BcbCounter::Expression { .. } => {} + // Process the counters associated with BCB edges. + for (from_bcb, to_bcb, counter_kind) in self.coverage_counters.bcb_edge_counters() { + let do_inject = match counter_kind { + // Counter-increment statements always need to be injected. + BcbCounter::Counter { .. } => true, + // BCB-edge expressions never have mappings, so they never need + // a corresponding statement. + BcbCounter::Expression { .. } => false, + }; + if !do_inject { + continue; } - } - } - #[inline] - fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { - self.bcb_data(bcb).leader_bb() - } + // We need to inject a coverage statement into a new BB between the + // last BB of `from_bcb` and the first BB of `to_bcb`. + let from_bb = self.basic_coverage_blocks[from_bcb].last_bb(); + let to_bb = self.basic_coverage_blocks[to_bcb].leader_bb(); - #[inline] - fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { - self.bcb_data(bcb).last_bb() - } + let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); + debug!( + "Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \ + requires a new MIR BasicBlock {new_bb:?} for edge counter {counter_kind:?}", + ); + + // Inject a counter into the newly-created BB. + inject_statement(self.mir_body, self.make_mir_coverage_kind(&counter_kind), new_bb); + } - #[inline] - fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { - &self.basic_coverage_blocks[bcb] + mappings } fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 3f7ba572510..704eea413e1 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -2,7 +2,7 @@ use std::cell::OnceCell; use rustc_data_structures::graph::WithNumNodes; use rustc_index::IndexVec; -use rustc_middle::mir::{self, AggregateKind, Rvalue, Statement, StatementKind}; +use rustc_middle::mir; use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP}; use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; @@ -41,13 +41,8 @@ impl CoverageSpans { !self.bcb_to_spans[bcb].is_empty() } - pub(super) fn bcbs_with_coverage_spans( - &self, - ) -> impl Iterator<Item = (BasicCoverageBlock, &[Span])> { - self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| { - // Only yield BCBs that have at least one coverage span. - (!spans.is_empty()).then_some((bcb, spans.as_slice())) - }) + pub(super) fn spans_for_bcb(&self, bcb: BasicCoverageBlock) -> &[Span] { + &self.bcb_to_spans[bcb] } } @@ -75,29 +70,15 @@ struct CoverageSpan { impl CoverageSpan { pub fn for_fn_sig(fn_sig_span: Span) -> Self { - Self { - span: fn_sig_span, - expn_span: fn_sig_span, - current_macro_or_none: Default::default(), - bcb: START_BCB, - merged_spans: vec![], - is_closure: false, - } + Self::new(fn_sig_span, fn_sig_span, START_BCB, false) } - pub fn for_statement( - statement: &Statement<'_>, + pub(super) fn new( span: Span, expn_span: Span, bcb: BasicCoverageBlock, + is_closure: bool, ) -> Self { - let is_closure = match statement.kind { - StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => { - matches!(kind, AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _, _)) - } - _ => false, - }; - Self { span, expn_span, @@ -108,17 +89,6 @@ impl CoverageSpan { } } - pub fn for_terminator(span: Span, expn_span: Span, bcb: BasicCoverageBlock) -> Self { - Self { - span, - expn_span, - current_macro_or_none: Default::default(), - bcb, - merged_spans: vec![span], - is_closure: false, - } - } - pub fn merge_from(&mut self, mut other: CoverageSpan) { debug_assert!(self.is_mergeable(&other)); self.span = self.span.to(other.span); diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 02e2cf6b05e..6189e5379ea 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,5 +1,7 @@ +use rustc_data_structures::captures::Captures; use rustc_middle::mir::{ - self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, + self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, }; use rustc_span::Span; @@ -12,7 +14,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( body_span: Span, basic_coverage_blocks: &CoverageGraph, ) -> Vec<CoverageSpan> { - let mut initial_spans = Vec::<CoverageSpan>::with_capacity(mir_body.basic_blocks.len() * 2); + let mut initial_spans = Vec::with_capacity(mir_body.basic_blocks.len() * 2); for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data)); } @@ -50,34 +52,41 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple // `Statement`s and/or `Terminator`s.) -fn bcb_to_initial_coverage_spans( - mir_body: &mir::Body<'_>, +fn bcb_to_initial_coverage_spans<'a, 'tcx>( + mir_body: &'a mir::Body<'tcx>, body_span: Span, bcb: BasicCoverageBlock, - bcb_data: &BasicCoverageBlockData, -) -> Vec<CoverageSpan> { - bcb_data - .basic_blocks - .iter() - .flat_map(|&bb| { - let data = &mir_body[bb]; - data.statements - .iter() - .filter_map(move |statement| { - filtered_statement_span(statement).map(|span| { - CoverageSpan::for_statement( - statement, - function_source_span(span, body_span), - span, - bcb, - ) - }) - }) - .chain(filtered_terminator_span(data.terminator()).map(|span| { - CoverageSpan::for_terminator(function_source_span(span, body_span), span, bcb) - })) - }) - .collect() + bcb_data: &'a BasicCoverageBlockData, +) -> impl Iterator<Item = CoverageSpan> + Captures<'a> + Captures<'tcx> { + bcb_data.basic_blocks.iter().flat_map(move |&bb| { + let data = &mir_body[bb]; + + let statement_spans = data.statements.iter().filter_map(move |statement| { + let expn_span = filtered_statement_span(statement)?; + let span = function_source_span(expn_span, body_span); + + Some(CoverageSpan::new(span, expn_span, bcb, is_closure(statement))) + }); + + let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| { + let expn_span = filtered_terminator_span(terminator)?; + let span = function_source_span(expn_span, body_span); + + Some(CoverageSpan::new(span, expn_span, bcb, false)) + }); + + statement_spans.chain(terminator_span) + }) +} + +fn is_closure(statement: &Statement<'_>) -> bool { + match statement.kind { + StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind { + AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _, _) => true, + _ => false, + }, + _ => false, + } } /// If the MIR `Statement` has a span contributive to computing coverage spans, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 1d8cbe0e21b..4009e289240 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -105,7 +105,6 @@ use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::definitions::DefPathDataName; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; -use rustc_middle::mir; use rustc_middle::mir::mono::{ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, Visibility, @@ -1279,38 +1278,8 @@ fn dump_mono_items_stats<'tcx>( Ok(()) } -fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet { - let (items, cgus) = tcx.collect_and_partition_mono_items(()); - let mut visited = DefIdSet::default(); - let mut result = items.clone(); - - for cgu in cgus { - for item in cgu.items().keys() { - if let MonoItem::Fn(ref instance) = item { - let did = instance.def_id(); - if !visited.insert(did) { - continue; - } - let body = tcx.instance_mir(instance.def); - for block in body.basic_blocks.iter() { - for statement in &block.statements { - let mir::StatementKind::Coverage(_) = statement.kind else { continue }; - let scope = statement.source_info.scope; - if let Some(inlined) = scope.inlined_instance(&body.source_scopes) { - result.insert(inlined.def_id()); - } - } - } - } - } - } - - tcx.arena.alloc(result) -} - pub fn provide(providers: &mut Providers) { providers.collect_and_partition_mono_items = collect_and_partition_mono_items; - providers.codegened_and_inlined_items = codegened_and_inlined_items; providers.is_codegened_item = |tcx, def_id| { let (all_mono_items, _) = tcx.collect_and_partition_mono_items(()); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 925ee615b09..4ad838e5aed 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1511,9 +1511,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ), ); } + + let (span, sugg, post) = if let SuggestionTarget::SimilarlyNamed = suggestion.target + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Some(span) = suggestion.span + && let Some(candidate) = suggestion.candidate.as_str().strip_prefix("_") + && snippet == candidate + { + // When the suggested binding change would be from `x` to `_x`, suggest changing the + // original binding definition instead. (#60164) + (span, snippet, ", consider changing it") + } else { + (span, suggestion.candidate.to_string(), "") + }; let msg = match suggestion.target { SuggestionTarget::SimilarlyNamed => format!( - "{} {} with a similar name exists", + "{} {} with a similar name exists{post}", suggestion.res.article(), suggestion.res.descr() ), @@ -1521,7 +1534,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { format!("maybe you meant this {}", suggestion.res.descr()) } }; - err.span_suggestion(span, msg, suggestion.candidate, Applicability::MaybeIncorrect); + err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); true } diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs new file mode 100644 index 00000000000..f42a9739320 --- /dev/null +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -0,0 +1,61 @@ +//! Module containing the translation from stable mir constructs to the rustc counterpart. +//! +//! This module will only include a few constructs to allow users to invoke internal rustc APIs +//! due to incomplete stable coverage. + +// Prefer importing stable_mir over internal rustc constructs to make this file more readable. +use crate::rustc_smir::{MaybeStable, Tables}; +use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy}; +use stable_mir::ty::{Const, GenericArgKind, GenericArgs, Region, Ty}; +use stable_mir::DefId; + +use super::RustcInternal; + +impl<'tcx> RustcInternal<'tcx> for DefId { + type T = rustc_span::def_id::DefId; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.def_ids[*self] + } +} + +impl<'tcx> RustcInternal<'tcx> for GenericArgs { + type T = rustc_ty::GenericArgsRef<'tcx>; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.tcx.mk_args_from_iter(self.0.iter().map(|arg| arg.internal(tables))) + } +} + +impl<'tcx> RustcInternal<'tcx> for GenericArgKind { + type T = rustc_ty::GenericArg<'tcx>; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + GenericArgKind::Lifetime(reg) => reg.internal(tables).into(), + GenericArgKind::Type(ty) => ty.internal(tables).into(), + GenericArgKind::Const(cnst) => cnst.internal(tables).into(), + } + } +} + +impl<'tcx> RustcInternal<'tcx> for Region { + type T = rustc_ty::Region<'tcx>; + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + todo!() + } +} + +impl<'tcx> RustcInternal<'tcx> for Ty { + type T = InternalTy<'tcx>; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match tables.types[self.0] { + MaybeStable::Stable(_) => todo!(), + MaybeStable::Rustc(ty) => ty, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for Const { + type T = rustc_ty::Const<'tcx>; + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + todo!() + } +} diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 61227e5d38f..b1ea5e898b8 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -20,6 +20,8 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::{ControlFlow, Index}; +mod internal; + impl<'tcx> Index<stable_mir::DefId> for Tables<'tcx> { type Output = DefId; @@ -231,3 +233,11 @@ impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> Index<V k } } + +/// Trait used to translate a stable construct to its rustc counterpart. +/// +/// This is basically a mirror of [crate::rustc_smir::Stable]. +pub(crate) trait RustcInternal<'tcx> { + type T; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T; +} diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs new file mode 100644 index 00000000000..8ff3958da7b --- /dev/null +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -0,0 +1,55 @@ +//! Logic required to produce a monomorphic stable body. +//! +//! We first retrieve and monomorphize the rustc body representation, i.e., we generate a +//! monomorphic body using internal representation. +//! After that, we convert the internal representation into a stable one. +use crate::rustc_smir::{Stable, Tables}; +use rustc_middle::mir; +use rustc_middle::mir::visit::MutVisitor; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +/// Builds a monomorphic body for a given instance. +pub struct BodyBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + instance: ty::Instance<'tcx>, +} + +impl<'tcx> BodyBuilder<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { + BodyBuilder { tcx, instance } + } + + pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { + let mut body = self.tcx.instance_mir(self.instance.def).clone(); + let generics = self.tcx.generics_of(self.instance.def_id()); + if generics.requires_monomorphization(self.tcx) { + self.visit_body(&mut body); + } + body.stable(tables) + } + + fn monomorphize<T>(&self, value: T) -> T + where + T: ty::TypeFoldable<TyCtxt<'tcx>>, + { + self.instance.instantiate_mir_and_normalize_erasing_regions( + self.tcx, + ty::ParamEnv::reveal_all(), + ty::EarlyBinder::bind(value), + ) + } +} + +impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { + fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, _location: mir::Location) { + *ct = self.monomorphize(*ct); + } + + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: mir::visit::TyContext) { + *ty = self.monomorphize(*ty); + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } +} diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 8a1b6b6bfe9..d5379797f1c 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,7 +7,7 @@ //! //! For now, we are developing everything inside `rustc`, thus, we keep this module private. -use crate::rustc_internal::IndexMap; +use crate::rustc_internal::{IndexMap, RustcInternal}; use crate::rustc_smir::hir::def::DefKind; use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region}; use rustc_hir as hir; @@ -26,6 +26,7 @@ use stable_mir::{self, opaque, Context, Filename}; use tracing::debug; mod alloc; +mod builder; impl<'tcx> Context for Tables<'tcx> { fn local_crate(&self) -> stable_mir::Crate { @@ -171,8 +172,9 @@ impl<'tcx> Context for Tables<'tcx> { } } - fn instance_body(&mut self, _def: InstanceDef) -> Body { - todo!("Monomorphize the body") + fn instance_body(&mut self, def: InstanceDef) -> Body { + let instance = self.instances[def]; + builder::BodyBuilder::new(self.tcx, instance).build(self) } fn instance_ty(&mut self, def: InstanceDef) -> stable_mir::ty::Ty { @@ -195,9 +197,21 @@ impl<'tcx> Context for Tables<'tcx> { let def_id = self[def_id]; let generics = self.tcx.generics_of(def_id); let result = generics.requires_monomorphization(self.tcx); - println!("req {result}: {def_id:?}"); result } + + fn resolve_instance( + &mut self, + def: stable_mir::ty::FnDef, + args: &stable_mir::ty::GenericArgs, + ) -> Option<stable_mir::mir::mono::Instance> { + let def_id = def.0.internal(self); + let args_ref = args.internal(self); + match Instance::resolve(self.tcx, ParamEnv::reveal_all(), def_id, args_ref) { + Ok(Some(instance)) => Some(instance.stable(self)), + Ok(None) | Err(_) => None, + } + } } #[derive(Clone)] diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index ee93f74e750..31c2a56faa5 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -13,7 +13,6 @@ rustc_index = { path = "../rustc_index" } rustc_arena = { path = "../rustc_arena" } scoped-tls = "1.0" unicode-width = "0.1.4" -cfg-if = "1.0" tracing = "0.1" sha1 = "0.10.0" sha2 = "0.10.1" diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index 450d5455ff9..7da7dc610ec 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -33,8 +33,8 @@ pub fn analyze_source_file( (lines, multi_byte_chars, non_narrow_chars) } -cfg_if::cfg_if! { - if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { +cfg_match! { + cfg(any(target_arch = "x86", target_arch = "x86_64")) => { fn analyze_source_file_dispatch(src: &str, lines: &mut Vec<RelativeBytePos>, multi_byte_chars: &mut Vec<MultiByteChar>, @@ -172,8 +172,8 @@ cfg_if::cfg_if! { non_narrow_chars); } } - } else { - + } + _ => { // The target (or compiler version) does not support SSE2 ... fn analyze_source_file_dispatch(src: &str, lines: &mut Vec<RelativeBytePos>, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 49b4042d148..8bb64aa5f12 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -13,21 +13,24 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +// tidy-alphabetical-start +#![allow(internal_features)] #![cfg_attr(not(bootstrap), doc(rust_logo))] #![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![deny(rustc::diagnostic_outside_of_impl)] +#![deny(rustc::untranslatable_diagnostic)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] +#![feature(cfg_match)] #![feature(if_let_guard)] -#![feature(negative_impls)] -#![feature(min_specialization)] -#![feature(rustc_attrs)] #![feature(let_chains)] -#![feature(round_char_boundary)] -#![feature(read_buf)] +#![feature(min_specialization)] +#![feature(negative_impls)] #![feature(new_uninit)] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] -#![allow(internal_features)] +#![feature(read_buf)] +#![feature(round_char_boundary)] +#![feature(rustc_attrs)] +// tidy-alphabetical-end #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_target/src/asm/csky.rs b/compiler/rustc_target/src/asm/csky.rs index 6f0e7f79949..db3d8106040 100644 --- a/compiler/rustc_target/src/asm/csky.rs +++ b/compiler/rustc_target/src/asm/csky.rs @@ -64,9 +64,9 @@ def_regs! { r20: reg = ["r20","t4"],// feature high-register r21: reg = ["r21","t5"],// feature high-register r22: reg = ["r22","t6"],// feature high-register - r23: reg = ["r23","t7", "fp"],// feature high-register - r24: reg = ["r24","t8", "sop"],// feature high-register - r25: reg = ["r25","t9","tp", "bsp"],// feature high-register + r23: reg = ["r23","t7"],// feature high-register + r24: reg = ["r24","t8"],// feature high-register + r25: reg = ["r25","t9"],// feature high-register f0: freg = ["fr0","vr0"], f1: freg = ["fr1","vr1"], f2: freg = ["fr2","vr2"], 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 86328cb78ab..78b466907b3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1644,7 +1644,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter` if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1) - && let Some(expr_span) = expr.span.find_ancestor_inside(await_expr.span) + && let Some(expr_span) = expr.span.find_ancestor_inside_same_ctxt(await_expr.span) { let removal_span = self .tcx @@ -2665,6 +2665,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Check for foreign traits being reachable. self.tcx.visible_parent_map(()).get(&def_id).is_some() }; + if Some(def_id) == self.tcx.lang_items().sized_trait() + && let Some(hir::Node::TraitItem(hir::TraitItem { + ident, + kind: hir::TraitItemKind::Type(bounds, None), + .. + })) = tcx.hir().get_if_local(item_def_id) + // Do not suggest relaxing if there is an explicit `Sized` obligation. + && !bounds.iter() + .filter_map(|bound| bound.trait_ref()) + .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait()) + { + let (span, separator) = if let [.., last] = bounds { + (last.span().shrink_to_hi(), " +") + } else { + (ident.span.shrink_to_hi(), ":") + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{separator} ?Sized"), + Applicability::MachineApplicable, + ); + } if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item { diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs index 12ac8f1ca65..19910691456 100644 --- a/compiler/stable_mir/src/error.rs +++ b/compiler/stable_mir/src/error.rs @@ -4,6 +4,7 @@ //! - [CompilerError]: This represents errors that can be raised when invoking the compiler. //! - [Error]: Generic error that represents the reason why a request that could not be fulfilled. +use std::convert::From; use std::fmt::{Debug, Display, Formatter}; use std::{error, fmt}; @@ -31,6 +32,12 @@ impl Error { } } +impl From<&str> for Error { + fn from(value: &str) -> Self { + Self(value.into()) + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 59af3f64ad3..be5ccac78c7 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -39,6 +39,7 @@ pub mod visitor; pub use error::*; use mir::mono::Instance; +use ty::{FnDef, GenericArgs}; /// Use String for now but we should replace it. pub type Symbol = String; @@ -233,6 +234,9 @@ pub trait Context { /// Item requires monomorphization. fn requires_monomorphization(&self, def_id: DefId) -> bool; + + /// Resolve an instance from the given function definition and generic arguments. + fn resolve_instance(&mut self, def: FnDef, args: &GenericArgs) -> Option<Instance>; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index ea30fe6dbcd..5eee1ec00df 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -5,9 +5,11 @@ use crate::{ty::Ty, Span}; #[derive(Clone, Debug)] pub struct Body { pub blocks: Vec<BasicBlock>, - pub locals: Vec<LocalDecl>, + pub locals: LocalDecls, } +type LocalDecls = Vec<LocalDecl>; + #[derive(Clone, Debug)] pub struct LocalDecl { pub ty: Ty, @@ -344,6 +346,7 @@ pub enum Operand { #[derive(Clone, Debug)] pub struct Place { pub local: Local, + /// projection out of a place (access a field, deref a pointer, etc) pub projection: String, } @@ -462,3 +465,25 @@ pub enum NullOp { /// Returns the offset of a field. OffsetOf(Vec<FieldIdx>), } + +impl Operand { + pub fn ty(&self, locals: &LocalDecls) -> Ty { + match self { + Operand::Copy(place) | Operand::Move(place) => place.ty(locals), + Operand::Constant(c) => c.ty(), + } + } +} + +impl Constant { + pub fn ty(&self) -> Ty { + self.literal.ty + } +} + +impl Place { + pub fn ty(&self, locals: &LocalDecls) -> Ty { + let _start_ty = locals[self.local].ty; + todo!("Implement projection") + } +} diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index d8e8ccb0454..997576fc7cb 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -1,5 +1,5 @@ use crate::mir::Body; -use crate::ty::{IndexedVal, Ty}; +use crate::ty::{FnDef, GenericArgs, IndexedVal, Ty}; use crate::{with, CrateItem, DefId, Error, Opaque}; use std::fmt::Debug; @@ -41,6 +41,15 @@ impl Instance { pub fn ty(&self) -> Ty { with(|context| context.instance_ty(self.def)) } + + /// Resolve an instance starting from a function definition and generic arguments. + pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> { + with(|context| { + context.resolve_instance(def, args).ok_or_else(|| { + crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) + }) + }) + } } /// Try to convert a crate item into an instance. diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 0ed2813bccf..8f7f8bd4e38 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -225,6 +225,7 @@ pub struct ImplDef(pub DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct RegionDef(pub DefId); +/// A list of generic arguments. #[derive(Clone, Debug)] pub struct GenericArgs(pub Vec<GenericArgKind>); diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 8ab851a0ea6..4a62013c685 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -207,7 +207,7 @@ impl<T> Box<T> { /// ``` /// let five = Box::new(5); /// ``` - #[cfg(all(not(no_global_oom_handling)))] + #[cfg(not(no_global_oom_handling))] #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] diff --git a/library/std/build.rs b/library/std/build.rs index 046ac488b1f..ad0a82eab8c 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -3,17 +3,11 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").expect("TARGET was not set"); - if target.contains("freebsd") { - if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() { - println!("cargo:rustc-cfg=freebsd12"); - } else if env::var("RUST_STD_FREEBSD_13_ABI").is_ok() { - println!("cargo:rustc-cfg=freebsd12"); - println!("cargo:rustc-cfg=freebsd13"); - } - } else if target.contains("linux") + if target.contains("linux") || target.contains("netbsd") || target.contains("dragonfly") || target.contains("openbsd") + || target.contains("freebsd") || target.contains("solaris") || target.contains("illumos") || target.contains("apple-darwin") diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs index 8db3a950c40..5689a82e00a 100644 --- a/library/std/src/os/freebsd/fs.rs +++ b/library/std/src/os/freebsd/fs.rs @@ -76,12 +76,7 @@ impl MetadataExt for Metadata { fn as_raw_stat(&self) -> &raw::stat { // The methods below use libc::stat, so they work fine when libc is built with FreeBSD 12 ABI. // This method would just return nonsense. - #[cfg(freebsd12)] panic!("as_raw_stat not supported with FreeBSD 12 ABI"); - #[cfg(not(freebsd12))] - unsafe { - &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) - } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 @@ -143,12 +138,7 @@ impl MetadataExt for Metadata { fn st_flags(&self) -> u32 { self.as_inner().as_inner().st_flags as u32 } - #[cfg(freebsd12)] fn st_lspare(&self) -> u32 { panic!("st_lspare not supported with FreeBSD 12 ABI"); } - #[cfg(not(freebsd12))] - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } } diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh index 918b19612ae..7d40db2ee23 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh @@ -4,34 +4,34 @@ set -ex # Only run the stage 1 tests on merges, not on PR CI jobs. if [[ -z "${PR_CI_JOB}" ]]; then - ../x.py --stage 1 test --skip src/tools/tidy && \ - # Run the `mir-opt` tests again but this time for a 32-bit target. - # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have - # both 32-bit and 64-bit outputs updated by the PR author, before - # the PR is approved and tested for merging. - # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, - # despite having different output on 32-bit vs 64-bit targets. - ../x.py --stage 1 test tests/mir-opt \ - --host='' --target=i686-unknown-linux-gnu && \ - # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0 - # compiler, and is sensitive to the addition of new flags. - ../x.py --stage 1 test tests/ui-fulldeps + ../x.py --stage 1 test --skip src/tools/tidy + + # Run the `mir-opt` tests again but this time for a 32-bit target. + # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have + # both 32-bit and 64-bit outputs updated by the PR author, before + # the PR is approved and tested for merging. + # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, + # despite having different output on 32-bit vs 64-bit targets. + ../x.py --stage 1 test tests/mir-opt --host='' --target=i686-unknown-linux-gnu + + # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0 + # compiler, and is sensitive to the addition of new flags. + ../x.py --stage 1 test tests/ui-fulldeps fi # NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux. -../x.py --stage 2 test --skip src/tools/tidy && \ - # Run the `mir-opt` tests again but this time for a 32-bit target. - # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have - # both 32-bit and 64-bit outputs updated by the PR author, before - # the PR is approved and tested for merging. - # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, - # despite having different output on 32-bit vs 64-bit targets. - ../x --stage 2 test tests/mir-opt \ - --host='' --target=i686-unknown-linux-gnu && \ - # Run the UI test suite again, but in `--pass=check` mode - # - # This is intended to make sure that both `--pass=check` continues to - # work. - # - ../x.ps1 --stage 2 test tests/ui --pass=check \ - --host='' --target=i686-unknown-linux-gnu +../x.py --stage 2 test --skip src/tools/tidy + +# Run the `mir-opt` tests again but this time for a 32-bit target. +# This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have +# both 32-bit and 64-bit outputs updated by the PR author, before +# the PR is approved and tested for merging. +# It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`, +# despite having different output on 32-bit vs 64-bit targets. +../x --stage 2 test tests/mir-opt --host='' --target=i686-unknown-linux-gnu + +# Run the UI test suite again, but in `--pass=check` mode +# +# This is intended to make sure that both `--pass=check` continues to +# work. +../x.ps1 --stage 2 test tests/ui --pass=check --host='' --target=i686-unknown-linux-gnu diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 93b07350ac1..5136aec9896 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -91,6 +91,10 @@ x--expand-yaml-anchors--remove: os: macos-13 # We use the standard runner for now <<: *base-job + - &job-macos-m1 + os: macos-13-xlarge + <<: *base-job + - &job-windows-8c os: windows-2019-8core-32gb <<: *base-job @@ -153,6 +157,10 @@ x--expand-yaml-anchors--remove: run: src/ci/scripts/dump-environment.sh <<: *step + - name: install awscli + run: src/ci/scripts/install-awscli.sh + <<: *step + - name: install sccache run: src/ci/scripts/install-sccache.sh <<: *step @@ -523,17 +531,14 @@ jobs: # This target only needs to support 11.0 and up as nothing else supports the hardware - name: dist-aarch64-apple env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --stage 2 + SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- - --build=x86_64-apple-darwin - --host=aarch64-apple-darwin - --target=aarch64-apple-darwin --enable-full-tools --enable-sanitizers --enable-profiler - --disable-docs --set rust.jemalloc --set llvm.ninja=false + --set rust.lto=thin RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 SELECT_XCODE: /Applications/Xcode_13.4.1.app USE_XCODE_CLANG: 1 @@ -543,15 +548,7 @@ jobs: NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 - # Corresponds to 16K page size - # - # Shouldn't be needed if jemalloc-sys is updated to - # handle this platform like iOS or if we build on - # aarch64-apple-darwin itself. - # - # https://github.com/gnzlbg/jemallocator/blob/c27a859e98e3cb790dc269773d9da71a1e918458/jemalloc-sys/build.rs#L237 - JEMALLOC_SYS_WITH_LG_PAGE: 14 - <<: *job-macos-xl + <<: *job-macos-m1 ###################### # Windows Builders # diff --git a/src/ci/scripts/install-awscli.sh b/src/ci/scripts/install-awscli.sh new file mode 100755 index 00000000000..b4a239fd3bc --- /dev/null +++ b/src/ci/scripts/install-awscli.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# This script downloads and installs the awscli binaries directly from +# Amazon. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +AWS_VERSION="2.13.25" + +# Only the macOS arm64/aarch64 GitHub Actions runner needs to have AWS +# installed; other platforms have it preinstalled. + +if isMacOS; then + platform=$(uname -m) + case $platform in + x86_64) + ;; + arm64) + file="https://awscli.amazonaws.com/AWSCLIV2-${AWS_VERSION}.pkg" + retry curl -f "${file}" -o "AWSCLIV2.pkg" + sudo installer -pkg "AWSCLIV2.pkg" -target / + ;; + *) + echo "unsupported architecture: ${platform}" + exit 1 + esac +fi diff --git a/src/doc/rustdoc/src/write-documentation/what-to-include.md b/src/doc/rustdoc/src/write-documentation/what-to-include.md index 16457ed0ff8..4bcae4d222c 100644 --- a/src/doc/rustdoc/src/write-documentation/what-to-include.md +++ b/src/doc/rustdoc/src/write-documentation/what-to-include.md @@ -117,7 +117,7 @@ rustdoc --theme awesome.css src/lib.rs Here is an example of a new theme, [Ayu]. -[Ayu]: https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/css/themes/ayu.css +[Ayu]: https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/css/rustdoc.css#L2384-L2574 [API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html#rustdoc-does-not-show-unhelpful-implementation-details-c-hidden [Documentation tests]: documentation-tests.md [on this blog]: https://blog.guillaume-gomez.fr/articles/2016-09-16+Generating+doc+with+rustdoc+and+a+custom+theme diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index fef25ad8635..26c9877dbff 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5463,6 +5463,7 @@ Released 2018-09-13 [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string [`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools +[`struct_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_field_names [`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter [`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops [`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl @@ -5625,6 +5626,7 @@ Released 2018-09-13 [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold [`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack [`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold +[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold [`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold [`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index cbcb42dad79..2c50addfd7d 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -29,7 +29,7 @@ color-print = "0.3.4" # Sync version with Cargo anstream = "0.5.0" [dev-dependencies] -ui_test = "0.20" +ui_test = "0.21.2" tester = "0.9" regex = "1.5" toml = "0.7.3" diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index e001197842b..55c0e105b30 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -30,6 +30,7 @@ because that's clearly a non-descriptive name. - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) - [Debugging](#debugging) + - [Conflicting lints](#conflicting-lints) - [PR Checklist](#pr-checklist) - [Adding configuration to a lint](#adding-configuration-to-a-lint) - [Cheat Sheet](#cheat-sheet) @@ -612,6 +613,24 @@ output in the `stdout` part. [`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html +## Conflicting lints + +There are several lints that deal with the same pattern but suggest different approaches. In other words, some lints +may suggest modifications that go in the opposite direction to what some other lints already propose for the same +code, creating conflicting diagnostics. + +When you are creating a lint that ends up in this scenario, the following tips should be encouraged to guide +classification: + +* The only case where they should be in the same category is if that category is `restriction`. For example, +`semicolon_inside_block` and `semicolon_outside_block`. +* For all the other cases, they should be in different categories with different levels of allowance. For example, +`implicit_return` (restriction, allow) and `needless_return` (style, warn). + +For lints that are in different categories, it is also recommended that at least one of them should be in the +`restriction` category. The reason for this is that the `restriction` group is the only group where we don't +recommend to enable the entire set, but cherry pick lints out of. + ## PR Checklist Before submitting your PR make sure you followed all the basic requirements: diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 2c958ccbbc2..c7eeed17954 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -100,7 +100,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat ## `msrv` The minimum rust version that the project supports -**Default Value:** `None` (`Option<String>`) +**Default Value:** `Msrv { stack: [] }` (`crate::Msrv`) --- **Affected lints:** @@ -273,6 +273,16 @@ The minimum number of enum variants for the lints about variant names to trigger * [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) +## `struct-field-name-threshold` +The minimum number of struct fields for the lints about field names to trigger + +**Default Value:** `3` (`u64`) + +--- +**Affected lints:** +* [`struct_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_variant_names) + + ## `enum-variant-size-threshold` The maximum size of an enum's variant to avoid box suggestion diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs index 96a90d599ab..56f56fff1e7 100644 --- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs +++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; -use rustc_hir::{AsyncCoroutineKind, Body, BodyId, ExprKind, CoroutineKind, QPath}; +use rustc_hir::{AsyncCoroutineKind, Body, BodyId, CoroutineKind, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 60f9bbd7458..ae8618dcaa0 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -287,5 +287,8 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { } fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { - matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::RefCellRef | sym::RefCellRefMut)) + matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::RefCellRef | sym::RefCellRefMut) + ) } diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 3f1ff66b8cf..cc9bd727937 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -3,8 +3,9 @@ use clippy_utils::macros::macro_backtrace; use clippy_utils::ty::expr_sig; use clippy_utils::{get_parent_node, is_default_equivalent, path_def_id}; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::{def::Res, Block, Expr, ExprKind, Local, Node, QPath, TyKind}; +use rustc_hir::{Block, Expr, ExprKind, Local, Node, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::print::with_forced_trimmed_paths; diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 481c44031cf..77438b27f90 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -154,9 +154,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO, crate::entry::MAP_ENTRY_INFO, crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO, - crate::enum_variants::ENUM_VARIANT_NAMES_INFO, - crate::enum_variants::MODULE_INCEPTION_INFO, - crate::enum_variants::MODULE_NAME_REPETITIONS_INFO, crate::equatable_if_let::EQUATABLE_IF_LET_INFO, crate::error_impl_error::ERROR_IMPL_ERROR_INFO, crate::escape::BOXED_LOCAL_INFO, @@ -226,6 +223,10 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, + crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, + crate::item_name_repetitions::MODULE_INCEPTION_INFO, + crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO, + crate::item_name_repetitions::STRUCT_FIELD_NAMES_INFO, crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs index 3d6d257e386..2d11fa6b647 100644 --- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; use clippy_utils::last_path_segment; +use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index 5de79133c7d..5ecd0ffadf3 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_entrypoint_fn}; +use clippy_utils::is_entrypoint_fn; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index d82ea6d2fc8..617c96b4fcb 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_c_void; use clippy_utils::path_def_id; +use clippy_utils::ty::is_c_void; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index 597fca88885..ee66c841ed2 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -1,50 +1,104 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test_function; +use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, HirId}; +use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::symbol::Ident; +use rustc_span::{BytePos, Span}; use super::IMPL_TRAIT_IN_PARAMS; +fn report( + cx: &LateContext<'_>, + param: &GenericParam<'_>, + ident: &Ident, + generics: &Generics<'_>, + first_param_span: Span, +) { + // No generics with nested generics, and no generics like FnMut(x) + span_lint_and_then( + cx, + IMPL_TRAIT_IN_PARAMS, + param.span, + "`impl Trait` used as a function parameter", + |diag| { + if let Some(gen_span) = generics.span_for_param_suggestion() { + // If there's already a generic param with the same bound, do not lint **this** suggestion. + diag.span_suggestion_with_style( + gen_span, + "add a type parameter", + format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), + rustc_errors::Applicability::HasPlaceholders, + rustc_errors::SuggestionStyle::ShowAlways, + ); + } else { + diag.span_suggestion_with_style( + Span::new( + first_param_span.lo() - rustc_span::BytePos(1), + ident.span.hi(), + ident.span.ctxt(), + ident.span.parent(), + ), + "add a type parameter", + format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), + rustc_errors::Applicability::HasPlaceholders, + rustc_errors::SuggestionStyle::ShowAlways, + ); + } + }, + ); +} + pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) { - if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() && !is_in_test_function(cx.tcx, hir_id) - { - if let FnKind::ItemFn(ident, generics, _) = kind { + if_chain! { + if let FnKind::ItemFn(ident, generics, _) = kind; + if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); + if !is_in_test_function(cx.tcx, hir_id); + then { for param in generics.params { if param.is_impl_trait() { - // No generics with nested generics, and no generics like FnMut(x) - span_lint_and_then( - cx, - IMPL_TRAIT_IN_PARAMS, - param.span, - "'`impl Trait` used as a function parameter'", - |diag| { - if let Some(gen_span) = generics.span_for_param_suggestion() { - diag.span_suggestion_with_style( - gen_span, - "add a type parameter", - format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), - rustc_errors::Applicability::HasPlaceholders, - rustc_errors::SuggestionStyle::ShowAlways, - ); - } else { - diag.span_suggestion_with_style( - Span::new( - body.params[0].span.lo() - rustc_span::BytePos(1), - ident.span.hi(), - ident.span.ctxt(), - ident.span.parent(), - ), - "add a type parameter", - format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), - rustc_errors::Applicability::HasPlaceholders, - rustc_errors::SuggestionStyle::ShowAlways, - ); - } - }, - ); + report(cx, param, ident, generics, body.params[0].span); + }; + } + } + } +} + +pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + if_chain! { + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()); + if let hir::ItemKind::Impl(impl_) = item.kind; + if let hir::Impl { of_trait, .. } = *impl_; + if of_trait.is_none(); + let body = cx.tcx.hir().body(body_id); + if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); + if !is_in_test_function(cx.tcx, impl_item.hir_id()); + then { + for param in impl_item.generics.params { + if param.is_impl_trait() { + report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span); + } + } + } + } +} + +pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { + if_chain! { + if !avoid_breaking_exported_api; + if let TraitItemKind::Fn(_, _) = trait_item.kind; + if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()); + // ^^ (Will always be a trait) + if !item.vis_span.is_empty(); // Is public + if !is_in_test_function(cx.tcx, trait_item.hir_id()); + then { + for param in trait_item.generics.params { + if param.is_impl_trait() { + let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1)); + report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi()); } } } diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index ee10334c67f..716908483e9 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -360,18 +360,26 @@ declare_clippy_lint! { } #[derive(Copy, Clone)] +#[allow(clippy::struct_field_names)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, + avoid_breaking_exported_api: bool, } impl Functions { - pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self { + pub fn new( + too_many_arguments_threshold: u64, + too_many_lines_threshold: u64, + large_error_threshold: u64, + avoid_breaking_exported_api: bool, + ) -> Self { Self { too_many_arguments_threshold, too_many_lines_threshold, large_error_threshold, + avoid_breaking_exported_api, } } } @@ -415,6 +423,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { must_use::check_impl_item(cx, item); result::check_impl_item(cx, item, self.large_error_threshold); + impl_trait_in_params::check_impl_item(cx, item); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { @@ -422,5 +431,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions { not_unsafe_ptr_arg_deref::check_trait_item(cx, item); must_use::check_trait_item(cx, item); result::check_trait_item(cx, item, self.large_error_threshold); + impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); } } diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index e332f681b6d..8b4984da3dd 100644 --- a/src/tools/clippy/clippy_lints/src/enum_variants.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -1,9 +1,10 @@ //! lint on enum variants that are prefixed or suffixed by the same characters use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; +use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; -use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start}; -use rustc_hir::{EnumDef, Item, ItemKind, OwnerId, Variant}; +use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; +use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -103,32 +104,184 @@ declare_clippy_lint! { style, "modules that have the same name as their parent module" } +declare_clippy_lint! { + /// ### What it does + /// Detects struct fields that are prefixed or suffixed + /// by the same characters or the name of the struct itself. + /// + /// ### Why is this bad? + /// Information common to all struct fields is better represented in the struct name. + /// + /// ### Limitations + /// Characters with no casing will be considered when comparing prefixes/suffixes + /// This applies to numbers and non-ascii characters without casing + /// e.g. `foo1` and `foo2` is considered to have different prefixes + /// (the prefixes are `foo1` and `foo2` respectively), as also `bar螃`, `bar蟹` + /// + /// ### Example + /// ```rust + /// struct Cake { + /// cake_sugar: u8, + /// cake_flour: u8, + /// cake_eggs: u8 + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Cake { + /// sugar: u8, + /// flour: u8, + /// eggs: u8 + /// } + /// ``` + #[clippy::version = "1.75.0"] + pub STRUCT_FIELD_NAMES, + pedantic, + "structs where all fields share a prefix/postfix or contain the name of the struct" +} -pub struct EnumVariantNames { +pub struct ItemNameRepetitions { modules: Vec<(Symbol, String, OwnerId)>, - threshold: u64, + enum_threshold: u64, + struct_threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool, } -impl EnumVariantNames { +impl ItemNameRepetitions { #[must_use] - pub fn new(threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool) -> Self { + pub fn new( + enum_threshold: u64, + struct_threshold: u64, + avoid_breaking_exported_api: bool, + allow_private_module_inception: bool, + ) -> Self { Self { modules: Vec::new(), - threshold, + enum_threshold, + struct_threshold, avoid_breaking_exported_api, allow_private_module_inception, } } } -impl_lint_pass!(EnumVariantNames => [ +impl_lint_pass!(ItemNameRepetitions => [ ENUM_VARIANT_NAMES, + STRUCT_FIELD_NAMES, MODULE_NAME_REPETITIONS, MODULE_INCEPTION ]); +#[must_use] +fn have_no_extra_prefix(prefixes: &[&str]) -> bool { + prefixes.iter().all(|p| p == &"" || p == &"_") +} + +fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &[FieldDef<'_>]) { + if (fields.len() as u64) < threshold { + return; + } + + check_struct_name_repetition(cx, item, fields); + + // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. + // this prevents linting in macros in which the location of the field identifier names differ + if !fields.iter().all(|field| item.ident.span.eq_ctxt(field.ident.span)) { + return; + } + + let mut pre: Vec<&str> = match fields.first() { + Some(first_field) => first_field.ident.name.as_str().split('_').collect(), + None => return, + }; + let mut post = pre.clone(); + post.reverse(); + for field in fields { + let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); + if field_split.len() == 1 { + return; + } + + pre = pre + .into_iter() + .zip(field_split.iter()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + post = post + .into_iter() + .zip(field_split.iter().rev()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + } + let prefix = pre.join("_"); + post.reverse(); + let postfix = match post.last() { + Some(&"") => post.join("_") + "_", + Some(_) | None => post.join("_"), + }; + if fields.len() > 1 { + let (what, value) = match ( + prefix.is_empty() || prefix.chars().all(|c| c == '_'), + postfix.is_empty(), + ) { + (true, true) => return, + (false, _) => ("pre", prefix), + (true, false) => ("post", postfix), + }; + span_lint_and_help( + cx, + STRUCT_FIELD_NAMES, + item.span, + &format!("all fields have the same {what}fix: `{value}`"), + None, + &format!("remove the {what}fixes"), + ); + } +} + +fn check_struct_name_repetition(cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + let snake_name = to_snake_case(item.ident.name.as_str()); + let item_name_words: Vec<&str> = snake_name.split('_').collect(); + for field in fields { + if field.ident.span.eq_ctxt(item.ident.span) { + //consider linting only if the field identifier has the same SyntaxContext as the item(struct) + let field_words: Vec<&str> = field.ident.name.as_str().split('_').collect(); + if field_words.len() >= item_name_words.len() { + // if the field name is shorter than the struct name it cannot contain it + if field_words.iter().zip(item_name_words.iter()).all(|(a, b)| a == b) { + span_lint_hir( + cx, + STRUCT_FIELD_NAMES, + field.hir_id, + field.span, + "field name starts with the struct's name", + ); + } + if field_words.len() > item_name_words.len() { + // lint only if the end is not covered by the start + if field_words + .iter() + .rev() + .zip(item_name_words.iter().rev()) + .all(|(a, b)| a == b) + { + span_lint_hir( + cx, + STRUCT_FIELD_NAMES, + field.hir_id, + field.span, + "field name ends with the struct's name", + ); + } + } + } + } + } +} + fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) { let name = variant.ident.name.as_str(); let item_name_chars = item_name.chars().count(); @@ -218,35 +371,7 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n ); } -#[must_use] -fn have_no_extra_prefix(prefixes: &[&str]) -> bool { - prefixes.iter().all(|p| p == &"" || p == &"_") -} - -#[must_use] -fn to_camel_case(item_name: &str) -> String { - let mut s = String::new(); - let mut up = true; - for c in item_name.chars() { - if c.is_uppercase() { - // we only turn snake case text into CamelCase - return item_name.to_string(); - } - if c == '_' { - up = true; - continue; - } - if up { - up = false; - s.extend(c.to_uppercase()); - } else { - s.push(c); - } - } - s -} - -impl LateLintPass<'_> for EnumVariantNames { +impl LateLintPass<'_> for ItemNameRepetitions { fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) { let last = self.modules.pop(); assert!(last.is_some()); @@ -303,9 +428,15 @@ impl LateLintPass<'_> for EnumVariantNames { } } } - if let ItemKind::Enum(ref def, _) = item.kind { - if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) { - check_variant(cx, self.threshold, def, item_name, item.span); + if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && span_is_local(item.span) + { + match item.kind { + ItemKind::Enum(def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span), + ItemKind::Struct(VariantData::Struct(fields, _), _) => { + check_fields(cx, self.struct_threshold, item, fields); + }, + _ => (), } } self.modules.push((item.ident.name, item_camel, item.owner_id)); diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 43e1b92c9b9..0ee291a4e9d 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Symbol}; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -52,12 +53,13 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// This is the opposite of the `iter_without_into_iter` lint. - /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method. + /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method + /// on the type or on any of the types in its `Deref` chain. /// /// ### Why is this bad? /// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains - /// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).iter()` syntax - /// in case of ambiguity with another `Intoiterator` impl. + /// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).into_iter()` syntax + /// in case of ambiguity with another `IntoIterator` impl. /// /// ### Example /// ```rust @@ -102,7 +104,20 @@ fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool { !matches!(ty.kind, TyKind::OpaqueDef(..)) } -fn type_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { +/// Returns the deref chain of a type, starting with the type itself. +fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx { + iter::successors(Some(ty), |&ty| { + if let Some(deref_did) = cx.tcx.lang_items().deref_trait() + && implements_trait(cx, ty, deref_did, &[]) + { + make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty]) + } else { + None + } + }) +} + +fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) { cx.tcx.inherent_impls(ty_did).iter().any(|&did| { cx.tcx @@ -127,7 +142,11 @@ impl LateLintPass<'_> for IterWithoutIntoIter { Mutability::Mut => sym::iter_mut, Mutability::Not => sym::iter, } - && !type_has_inherent_method(cx, ty, expected_method_name) + && !deref_chain(cx, ty) + .any(|ty| { + // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method + ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name) + }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { if item.ident.name == sym!(IntoIter) { Some(cx.tcx.hir().impl_item(item.id).expect_type().span) diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 0f35ec27665..a70a38ee08b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -50,9 +50,6 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; -use std::io; -use std::path::PathBuf; - use clippy_utils::msrvs::Msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -121,7 +118,6 @@ mod empty_structs_with_brackets; mod endian_bytes; mod entry; mod enum_clike; -mod enum_variants; mod equatable_if_let; mod error_impl_error; mod escape; @@ -166,6 +162,7 @@ mod inline_fn_without_body; mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; +mod item_name_repetitions; mod items_after_statements; mod items_after_test_module; mod iter_not_returning_iterator; @@ -362,7 +359,6 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` use crate::utils::conf::metadata::get_configuration_metadata; -use crate::utils::conf::TryConf; pub use crate::utils::conf::{lookup_conf_file, Conf}; use crate::utils::FindAll; @@ -374,65 +370,13 @@ use crate::utils::FindAll; /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. /// /// Used in `./src/driver.rs`. -pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. - let msrv = Msrv::read(&conf.msrv, sess); - let msrv = move || msrv.clone(); + let msrv = || conf.msrv.clone(); store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); } -#[doc(hidden)] -pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf { - if let Ok((_, warnings)) = path { - for warning in warnings { - sess.warn(warning.clone()); - } - } - let file_name = match path { - Ok((Some(path), _)) => path, - Ok((None, _)) => return Conf::default(), - Err(error) => { - sess.err(format!("error finding Clippy's configuration file: {error}")); - return Conf::default(); - }, - }; - - let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name); - // all conf errors are non-fatal, we just use the default conf in case of error - for error in errors { - if let Some(span) = error.span { - sess.span_err( - span, - format!("error reading Clippy's configuration file: {}", error.message), - ); - } else { - sess.err(format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - error.message - )); - } - } - - for warning in warnings { - if let Some(span) = warning.span { - sess.span_warn( - span, - format!("error reading Clippy's configuration file: {}", warning.message), - ); - } else { - sess.warn(format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - warning.message - )); - } - } - - conf -} - #[derive(Default)] struct RegistrationGroups { all: Vec<LintId>, @@ -558,7 +502,7 @@ fn register_categories(store: &mut rustc_lint::LintStore) { /// /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] -pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &'static Conf) { register_removed_non_tool_lints(store); register_categories(store); @@ -660,8 +604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - let msrv = Msrv::read(&conf.msrv, sess); - let msrv = move || msrv.clone(); + let msrv = || conf.msrv.clone(); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; @@ -762,6 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: too_many_arguments_threshold, too_many_lines_threshold, large_error_threshold, + avoid_breaking_exported_api, )) }); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); @@ -806,7 +750,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: suppress_restriction_lint_in_const, )) }); - store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst)); + let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); + store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); @@ -851,10 +796,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); let enum_variant_name_threshold = conf.enum_variant_name_threshold; + let struct_field_name_threshold = conf.struct_field_name_threshold; let allow_private_module_inception = conf.allow_private_module_inception; store.register_late_pass(move |_| { - Box::new(enum_variants::EnumVariantNames::new( + Box::new(item_name_repetitions::ItemNameRepetitions::new( enum_variant_name_threshold, + struct_field_name_threshold, avoid_breaking_exported_api, allow_private_module_inception, )) diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index f7b3b2358a0..5fffb27cda2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -185,7 +185,7 @@ fn get_vec_push<'tcx>( if let StmtKind::Semi(semi_stmt) = &stmt.kind; if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind; // Figure out the parameters for the method call - if let Some(pushed_item) = args.get(0); + if let Some(pushed_item) = args.first(); // Check that the method being called is push() on a Vec if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec); if path.ident.name.as_str() == "push"; diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 914ceca2995..5a87e75722d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -4,7 +4,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AsyncCoroutineKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, CoroutineKind, GenericArg, GenericBound, + AsyncCoroutineKind, Block, Body, Closure, CoroutineKind, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index 6c7c57ba1d6..552c57d5e02 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -103,9 +103,9 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let QPath::Resolved(_, count_func_path) = count_func_qpath; - if let Some(segment_zero) = count_func_path.segments.get(0); + if let Some(segment_zero) = count_func_path.segments.first(); if let Some(args) = segment_zero.args; - if let Some(GenericArg::Type(real_ty)) = args.args.get(0); + if let Some(GenericArg::Type(real_ty)) = args.args.first(); if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id); diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index f264424470d..9da20a28fba 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -15,12 +15,13 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does /// Suggests to use dedicated built-in methods, - /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// `is_ascii_(lowercase|uppercase|digit|hexdigit)` for checking on corresponding + /// ascii range /// /// ### Why is this bad? /// Using the built-in functions is more readable and makes it /// clear that it's not a specific subset of characters, but all - /// ASCII (lowercase|uppercase|digit) characters. + /// ASCII (lowercase|uppercase|digit|hexdigit) characters. /// ### Example /// ```rust /// fn main() { @@ -28,6 +29,7 @@ declare_clippy_lint! { /// assert!(matches!(b'X', b'A'..=b'Z')); /// assert!(matches!('2', '0'..='9')); /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// assert!(matches!('C', '0'..='9' | 'a'..='f' | 'A'..='F')); /// /// ('0'..='9').contains(&'0'); /// ('a'..='z').contains(&'a'); @@ -41,6 +43,7 @@ declare_clippy_lint! { /// assert!(b'X'.is_ascii_uppercase()); /// assert!('2'.is_ascii_digit()); /// assert!('x'.is_ascii_alphabetic()); + /// assert!('C'.is_ascii_hexdigit()); /// /// '0'.is_ascii_digit(); /// 'a'.is_ascii_lowercase(); @@ -75,6 +78,12 @@ enum CharRange { FullChar, /// '0..=9' Digit, + /// 'a..=f' + LowerHexLetter, + /// 'A..=F' + UpperHexLetter, + /// '0..=9' | 'a..=f' | 'A..=F' + HexDigit, Otherwise, } @@ -116,7 +125,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha CharRange::LowerChar => Some("is_ascii_lowercase"), CharRange::FullChar => Some("is_ascii_alphabetic"), CharRange::Digit => Some("is_ascii_digit"), - CharRange::Otherwise => None, + CharRange::HexDigit => Some("is_ascii_hexdigit"), + CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None, } { let default_snip = ".."; let mut app = Applicability::MachineApplicable; @@ -141,6 +151,12 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { CharRange::FullChar + } else if ranges.len() == 3 + && ranges.contains(&CharRange::Digit) + && ranges.contains(&CharRange::LowerHexLetter) + && ranges.contains(&CharRange::UpperHexLetter) + { + CharRange::HexDigit } else { CharRange::Otherwise } @@ -156,6 +172,8 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { match (&start_lit.node, &end_lit.node) { (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, + (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, _ => CharRange::Otherwise, } diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index a23000e5fe1..b5ab94b3a2f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -967,7 +967,6 @@ declare_clippy_lint! { "checks for unnecessary guards in match expressions" } -#[derive(Default)] pub struct Matches { msrv: Msrv, infallible_destructuring_match_linted: bool, @@ -978,7 +977,7 @@ impl Matches { pub fn new(msrv: Msrv) -> Self { Self { msrv, - ..Matches::default() + infallible_destructuring_match_linted: false, } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs index 3337b250c0e..20878f1e4df 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,7 +9,7 @@ use rustc_span::sym; use super::FILTER_MAP_IDENTITY; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) { + if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, filter_map_arg) { span_lint_and_sugg( cx, FILTER_MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs index 84a21de0ac8..8849a4f4942 100644 --- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) { + if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) { span_lint_and_sugg( cx, FLAT_MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs index ee063adac64..e7b4564c651 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_slice_of_primitives; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::LitKind; @@ -20,7 +19,6 @@ pub(super) fn check<'tcx>( if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); if cx.tcx.type_of(impl_id).instantiate_identity().is_slice(); - if let Some(_) = is_slice_of_primitives(cx, recv); if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind; then { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 7be1ce483f6..57581363cfa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -23,7 +23,7 @@ pub(super) fn check( if is_trait_method(cx, expr, sym::Iterator) || is_type_diagnostic_item(cx, caller_ty, sym::Result) || is_type_diagnostic_item(cx, caller_ty, sym::Option); - if is_expr_identity_function(cx, map_arg); + if is_expr_untyped_identity_function(cx, map_arg); if let Some(sugg_span) = expr.span.trim_start(caller.span); then { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index afdb8ce94ac..04ddaaa2f46 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -39,7 +39,7 @@ pub(super) fn check<'tcx>( if search_method == "find"; if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind; let closure_body = cx.tcx.hir().body(body); - if let Some(closure_arg) = closure_body.params.get(0); + if let Some(closure_arg) = closure_body.params.first(); then { if let hir::PatKind::Ref(..) = closure_arg.pat.kind { Some(search_snippet.replacen('&', "", 1)) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 47e2e744112..4a651396f14 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; +use hir::FnRetTy; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); if is_option || is_result || is_bool { - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind { + if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind { let body = cx.tcx.hir().body(body); let body_expr = &body.value; @@ -48,7 +49,14 @@ pub(super) fn check<'tcx>( .iter() // bindings are checked to be unused above .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - { + && matches!( + fn_decl.output, + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer, + .. + }) + ) { Applicability::MachineApplicable } else { // replacing the lambda may break type inference diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index f2773cad400..0629dee4f72 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -67,7 +67,7 @@ impl MissingDoc { if_chain! { if let Some(meta) = meta; if let MetaItemKind::List(list) = meta.kind; - if let Some(meta) = list.get(0); + if let Some(meta) = list.first(); if let Some(name) = meta.ident(); then { name.name == sym::include diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 96d83e114a4..fc088710e42 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -17,6 +17,9 @@ declare_clippy_lint! { /// Checks for imports that do not rename the item as specified /// in the `enforce-import-renames` config option. /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// import renames are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Consistency is important, if a project has defined import /// renames they should be followed. More practically, some item names are too @@ -38,7 +41,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.55.0"] pub MISSING_ENFORCED_IMPORT_RENAMES, - restriction, + style, "enforce import renames" } diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index f0b865be804..d205237a591 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{for_each_expr, Visitable}; -use clippy_utils::is_path_lang_item; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; 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 fe35126aab2..2c42a7a3676 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 @@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{DesugaringKind, Span}; declare_clippy_lint! { /// ### What it does @@ -64,7 +64,10 @@ 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(_)) || in_external_macro(cx.tcx.sess, block.span) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) + || in_external_macro(cx.tcx.sess, block.span) + || block.span.is_desugaring(DesugaringKind::Await) + { return; } let mut unsafe_ops = vec![]; diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index 38a75034cd3..377cbef7b99 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -189,7 +189,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) } fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { - block.stmts.get(0).map_or(false, |stmt| match stmt.kind { + block.stmts.first().map_or(false, |stmt| match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { if let ast::ExprKind::Continue(ref l) = e.kind { compare_labels(label, l.as_ref()) @@ -434,7 +434,7 @@ fn erode_from_back(s: &str) -> String { } fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> { - block.stmts.get(0).map(|stmt| stmt.span) + block.stmts.first().map(|stmt| stmt.span) } #[cfg(test)] diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 212d6234bdb..1d5874f6fea 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -7,7 +7,8 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; use rustc_hir::{ - Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, + BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, + PatKind, QPath, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -139,13 +140,23 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id); let is_async = match kind { FnKind::ItemFn(.., header) => { + if header.is_unsafe() { + // We don't check unsafe functions. + return; + } let attrs = cx.tcx.hir().attrs(hir_id); if header.abi != Abi::Rust || requires_exact_signature(attrs) { return; } header.is_async() }, - FnKind::Method(.., sig) => sig.header.is_async(), + FnKind::Method(.., sig) => { + if sig.header.is_unsafe() { + // We don't check unsafe functions. + return; + } + sig.header.is_async() + }, FnKind::Closure => return, }; @@ -186,20 +197,21 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { }; let infcx = cx.tcx.infer_ctxt().build(); euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); - if is_async { - let mut checked_closures = FxHashSet::default(); - - // We retrieve all the closures declared in the async function because they will - // not be found by `euv::Delegate`. - let mut closures: FxHashSet<LocalDefId> = FxHashSet::default(); - for_each_expr_with_closures(cx, body, |expr| { - if let ExprKind::Closure(closure) = expr.kind { - closures.insert(closure.def_id); - } - ControlFlow::<()>::Continue(()) - }); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); + let mut checked_closures = FxHashSet::default(); + + // We retrieve all the closures declared in the function because they will not be found + // by `euv::Delegate`. + let mut closures: FxHashSet<LocalDefId> = FxHashSet::default(); + for_each_expr_with_closures(cx, body, |expr| { + if let ExprKind::Closure(closure) = expr.kind { + closures.insert(closure.def_id); + } + ControlFlow::<()>::Continue(()) + }); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); + + if is_async { while !ctx.async_closures.is_empty() { let async_closures = ctx.async_closures.clone(); ctx.async_closures.clear(); @@ -304,10 +316,27 @@ impl<'tcx> MutablyUsedVariablesCtxt<'tcx> { } self.aliases.insert(alias, target); } + + // The goal here is to find if the current scope is unsafe or not. It stops when it finds + // a function or an unsafe block. + fn is_in_unsafe_block(&self, item: HirId) -> bool { + let hir = self.tcx.hir(); + for (parent, node) in hir.parent_iter(item) { + if let Some(fn_sig) = hir.fn_sig_by_hir_id(parent) { + return fn_sig.header.is_unsafe(); + } else if let Node::Block(block) = node { + if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { + return true; + } + } + } + false + } } impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { - fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { + #[allow(clippy::if_same_then_else)] + fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { if let euv::Place { base: euv::PlaceBase::Local(vid) @@ -327,13 +356,18 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { && matches!(base_ty.ref_mutability(), Some(Mutability::Mut)) { self.add_mutably_used_var(*vid); + } else if self.is_in_unsafe_block(id) { + // If we are in an unsafe block, any operation on this variable must not be warned + // upon! + self.add_mutably_used_var(*vid); } self.prev_bind = None; self.prev_move_to_closure.remove(vid); } } - fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) { + #[allow(clippy::if_same_then_else)] + fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { base: @@ -355,6 +389,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { || (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut)) { self.add_mutably_used_var(*vid); + } else if self.is_in_unsafe_block(id) { + // If we are in an unsafe block, any operation on this variable must not be warned + // upon! + self.add_mutably_used_var(*vid); } } else if borrow == ty::ImmBorrow { // If there is an `async block`, it'll contain a call to a closure which we need to @@ -397,7 +435,21 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { } } - fn copy(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) { + fn copy(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { + if let euv::Place { + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), + .. + } = &cmt.place + { + if self.is_in_unsafe_block(id) { + self.add_mutably_used_var(*vid); + } + } self.prev_bind = None; } @@ -427,8 +479,22 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { } } - fn bind(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { + fn bind(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { self.prev_bind = Some(id); + if let euv::Place { + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), + .. + } = &cmt.place + { + if self.is_in_unsafe_block(id) { + self.add_mutably_used_var(*vid); + } + } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 6d7df2eac62..f3c0d616473 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::is_self; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use clippy_utils::is_self; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::{Applicability, Diagnostic}; diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index d267b3de7f2..ed279a3813d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{AsyncCoroutineKind, Block, Body, Expr, ExprKind, CoroutineKind, LangItem, MatchSource, QPath}; +use rustc_hir::{AsyncCoroutineKind, Block, Body, CoroutineKind, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 2b4e3260c56..613afa46a91 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -5,9 +5,10 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_constant; use clippy_utils::macros::macro_backtrace; +use clippy_utils::{def_path_def_ids, in_constant}; use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -15,9 +16,10 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; +use rustc_middle::query::Key; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, InnerSpan, Span}; use rustc_target::abi::VariantIdx; @@ -126,128 +128,6 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, - // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is - // 'unfrozen'. However, this code causes a false negative in which - // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`. - // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` - // since it works when a pointer indirection involves (`Cell<*const T>`). - // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; - // but I'm not sure whether it's a decent way, if possible. - cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env) -} - -fn is_value_unfrozen_raw<'tcx>( - cx: &LateContext<'tcx>, - result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>, - ty: Ty<'tcx>, -) -> bool { - fn inner<'tcx>(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { - match *ty.kind() { - // the fact that we have to dig into every structs to search enums - // leads us to the point checking `UnsafeCell` directly is the only option. - ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, - // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the - // contained value. - ty::Adt(def, ..) if def.is_union() => false, - ty::Array(ty, _) => val.unwrap_branch().iter().any(|field| inner(cx, *field, ty)), - ty::Adt(def, _) if def.is_union() => false, - ty::Adt(def, args) if def.is_enum() => { - let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); - fields - .iter() - .copied() - .zip( - def.variants()[variant_index] - .fields - .iter() - .map(|field| field.ty(cx.tcx, args)), - ) - .any(|(field, ty)| inner(cx, field, ty)) - }, - ty::Adt(def, args) => val - .unwrap_branch() - .iter() - .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) - .any(|(field, ty)| inner(cx, *field, ty)), - ty::Tuple(tys) => val - .unwrap_branch() - .iter() - .zip(tys) - .any(|(field, ty)| inner(cx, *field, ty)), - _ => false, - } - } - result.map_or_else( - |err| { - // Consider `TooGeneric` cases as being unfrozen. - // This causes a false positive where an assoc const whose type is unfrozen - // have a value that is a frozen variant with a generic param (an example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). - // However, it prevents a number of false negatives that is, I think, important: - // 1. assoc consts in trait defs referring to consts of themselves (an example is - // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). - // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait - // defs (i.e. without substitute for `Self`). (e.g. borrowing - // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) - // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no - // enums. (An example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). - // One might be able to prevent these FNs correctly, and replace this with `false`; - // e.g. implementing `has_frozen_variant` described above, and not running this function - // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd - // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, - // similar to 2., but with the a frozen variant) (e.g. borrowing - // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). - // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - matches!(err, ErrorHandled::TooGeneric(..)) - }, - |val| val.map_or(true, |val| inner(cx, val, ty)), - ) -} - -fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { - let def_id = body_id.hir_id.owner.to_def_id(); - let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new(def_id, args); - let cid = rustc_middle::mir::interpret::GlobalId { - instance, - promoted: None, - }; - let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); - let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None); - is_value_unfrozen_raw(cx, result, ty) -} - -fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { - let args = cx.typeck_results().node_args(hir_id); - - let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None); - is_value_unfrozen_raw(cx, result, ty) -} - -pub fn const_eval_resolve<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, - span: Option<Span>, -) -> EvalToValTreeResult<'tcx> { - match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) { - Ok(Some(instance)) => { - let cid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval_global_id_for_typeck(param_env, cid, span) - }, - Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))), - Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))), - } -} - #[derive(Copy, Clone)] enum Source { Item { item: Span }, @@ -292,13 +172,178 @@ fn lint(cx: &LateContext<'_>, source: Source) { }); } -declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); +#[derive(Clone)] +pub struct NonCopyConst { + ignore_interior_mutability: Vec<String>, + ignore_mut_def_ids: FxHashSet<DefId>, +} + +impl_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); + +impl NonCopyConst { + pub fn new(ignore_interior_mutability: Vec<String>) -> Self { + Self { + ignore_interior_mutability, + ignore_mut_def_ids: FxHashSet::default(), + } + } + + fn is_ty_ignored(&self, ty: Ty<'_>) -> bool { + matches!(ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id)) + } + + fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env) + } + + fn is_value_unfrozen_raw_inner<'tcx>(&self, cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { + if self.is_ty_ignored(ty) { + return false; + } + match *ty.kind() { + // the fact that we have to dig into every structs to search enums + // leads us to the point checking `UnsafeCell` directly is the only option. + ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, + // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the + // contained value. + ty::Adt(def, ..) if def.is_union() => false, + ty::Array(ty, _) => val + .unwrap_branch() + .iter() + .any(|field| self.is_value_unfrozen_raw_inner(cx, *field, ty)), + ty::Adt(def, _) if def.is_union() => false, + ty::Adt(def, args) if def.is_enum() => { + let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); + let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); + fields + .iter() + .copied() + .zip( + def.variants()[variant_index] + .fields + .iter() + .map(|field| field.ty(cx.tcx, args)), + ) + .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, field, ty)) + }, + ty::Adt(def, args) => val + .unwrap_branch() + .iter() + .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) + .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)), + ty::Tuple(tys) => val + .unwrap_branch() + .iter() + .zip(tys) + .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)), + _ => false, + } + } + + fn is_value_unfrozen_raw<'tcx>( + &self, + cx: &LateContext<'tcx>, + result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>, + ty: Ty<'tcx>, + ) -> bool { + result.map_or_else( + |err| { + // Consider `TooGeneric` cases as being unfrozen. + // This causes a false positive where an assoc const whose type is unfrozen + // have a value that is a frozen variant with a generic param (an example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). + // However, it prevents a number of false negatives that is, I think, important: + // 1. assoc consts in trait defs referring to consts of themselves (an example is + // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). + // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait + // defs (i.e. without substitute for `Self`). (e.g. borrowing + // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) + // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no + // enums. (An example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). + // One might be able to prevent these FNs correctly, and replace this with `false`; + // e.g. implementing `has_frozen_variant` described above, and not running this function + // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd + // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, + // similar to 2., but with the a frozen variant) (e.g. borrowing + // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). + // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). + matches!(err, ErrorHandled::TooGeneric(..)) + }, + |val| val.map_or(true, |val| self.is_value_unfrozen_raw_inner(cx, val, ty)), + ) + } + + fn is_value_unfrozen_poly<'tcx>(&self, cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { + let def_id = body_id.hir_id.owner.to_def_id(); + let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); + let instance = ty::Instance::new(def_id, args); + let cid = rustc_middle::mir::interpret::GlobalId { + instance, + promoted: None, + }; + let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); + let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None); + self.is_value_unfrozen_raw(cx, result, ty) + } + + fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { + let args = cx.typeck_results().node_args(hir_id); + + let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None); + self.is_value_unfrozen_raw(cx, result, ty) + } + + pub fn const_eval_resolve<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ct: ty::UnevaluatedConst<'tcx>, + span: Option<Span>, + ) -> EvalToValTreeResult<'tcx> { + match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) { + Ok(Some(instance)) => { + let cid = GlobalId { + instance, + promoted: None, + }; + tcx.const_eval_global_id_for_typeck(param_env, cid, span) + }, + Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))), + Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))), + } + } +} impl<'tcx> LateLintPass<'tcx> for NonCopyConst { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + self.ignore_mut_def_ids.clear(); + let mut path = Vec::new(); + for ty in &self.ignore_interior_mutability { + path.extend(ty.split("::")); + for id in def_path_def_ids(cx, &path[..]) { + self.ignore_mut_def_ids.insert(id); + } + path.clear(); + } + } + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { if let ItemKind::Const(.., body_id) = it.kind { let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); - if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { + if !ignored_macro(cx, it) + && !self.is_ty_ignored(ty) + && Self::is_unfrozen(cx, ty) + && self.is_value_unfrozen_poly(cx, body_id, ty) + { lint(cx, Source::Item { item: it.span }); } } @@ -311,7 +356,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if is_unfrozen(cx, normalized) + if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) // When there's no default value, lint it only according to its type; // in other words, lint consts whose value *could* be unfrozen, not definitely is. // This feels inconsistent with how the lint treats generic types, @@ -324,7 +369,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // i.e. having an enum doesn't necessary mean a type has a frozen variant. // And, implementing it isn't a trivial task; it'll probably end up // re-implementing the trait predicate evaluation specific to `Freeze`. - && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized)) + && body_id_opt.map_or(true, |body_id| self.is_value_unfrozen_poly(cx, body_id, normalized)) { lint(cx, Source::Assoc { item: trait_item.span }); } @@ -367,8 +412,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if is_unfrozen(cx, normalized); - if is_value_unfrozen_poly(cx, *body_id, normalized); + if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized); + if self.is_value_unfrozen_poly(cx, *body_id, normalized); then { lint( cx, @@ -384,7 +429,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) { + if !self.is_ty_ignored(ty) + && Self::is_unfrozen(cx, ty) + && self.is_value_unfrozen_poly(cx, *body_id, normalized) + { lint(cx, Source::Assoc { item: impl_item.span }); } }, @@ -478,7 +526,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.typeck_results().expr_ty(dereferenced_expr) }; - if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) { + if !self.is_ty_ignored(ty) + && Self::is_unfrozen(cx, ty) + && self.is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) + { lint(cx, Source::Expr { expr: expr.span }); } } diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index d388dfc08f3..62ef48c8a90 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_lint_allowed; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::is_lint_allowed; use rustc_ast::ImplPolarity; use rustc_hir::def_id::DefId; use rustc_hir::{FieldDef, Item, ItemKind, Node}; diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 3dc652f9dc0..f0f8d510c7e 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -134,6 +134,7 @@ impl Usage { /// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the /// `DefId` of the function paired with the parameter's index. #[derive(Default)] +#[allow(clippy::struct_field_names)] struct Params { params: Vec<Param>, by_id: HirIdMap<usize>, diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index 136642d69dc..ea8ed28ba62 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::path_def_id; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::path_def_id; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs index 1a127c2bcf6..bd0ca66b761 100644 --- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; +use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::last_path_segment; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs index b8f787e1f1f..2e895d5f236 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -5,7 +5,7 @@ use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::visitors::for_each_expr; use rustc_errors::Applicability; -use rustc_hir::{AsyncCoroutineKind, Closure, Expr, ExprKind, CoroutineKind, MatchSource}; +use rustc_hir::{AsyncCoroutineKind, Closure, CoroutineKind, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::UpvarCapture; diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 197742b5dd4..a1a0e8f3520 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id); if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id); // the previous binding has the same mutability - if find_binding(binding_pat, ident).unwrap().1 == mutability; + if find_binding(binding_pat, ident).is_some_and(|bind| bind.1 == mutability); // the local does not change the effect of assignments to the binding. see #11290 if !affects_assignments(cx, mutability, binding_id, local.hir_id); // the local does not affect the code's drop behavior diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 9db18c2976c..2278e41be37 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -335,7 +335,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_block(&mut self, block: &'tcx Block<'_>) { if self.initialization_found { - if let Some(s) = block.stmts.get(0) { + if let Some(s) = block.stmts.first() { self.visit_stmt(s); } diff --git a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs index 3685432a253..4bfbe3bf37e 100644 --- a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_context; use clippy_utils::path_def_id; +use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 71a4b3fba1b..788678a63b7 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -578,7 +578,7 @@ impl Types { } } -#[allow(clippy::struct_excessive_bools)] +#[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 6756df8e716..72569e10f05 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -201,7 +201,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Block(block, _) = e.kind { // Extract the first statement/expression - match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) { + match (block.stmts.first().map(|stmt| &stmt.kind), block.expr) { (None, Some(expr)) => Some(expr), (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr), _ => None, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index 5aa057580e9..894de0d85c1 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -40,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind - && let Some(arg) = constructor_args.get(0) + && let Some(arg) = constructor_args.first() { if constructor.span.from_expansion() || arg.span.from_expansion() { return; @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { _ => return, } - if let Some(map_arg) = args.get(0) + if let Some(map_arg) = args.first() && let hir::ExprKind::Path(fun) = map_arg.kind { if map_arg.span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 50231d930d0..f10ed4b3d41 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -54,7 +54,6 @@ declare_clippy_lint! { "unnecessary structure name repetition whereas `Self` is applicable" } -#[derive(Default)] pub struct UseSelf { msrv: Msrv, stack: Vec<StackItem>, @@ -65,7 +64,7 @@ impl UseSelf { pub fn new(msrv: Msrv) -> Self { Self { msrv, - ..Self::default() + stack: Vec::new(), } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index f02c33cc674..aecb0c6dbfa 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -268,8 +268,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn qpath(&self, qpath: &Binding<&QPath<'_>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else { - chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); + } else if let Ok(path) = path_to_string(qpath.value) { + chain!(self, "match_qpath({qpath}, &[{}])", path); } } @@ -738,8 +738,8 @@ fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { get_attr(cx.sess(), attrs, "author").count() > 0 } -fn path_to_string(path: &QPath<'_>) -> String { - fn inner(s: &mut String, path: &QPath<'_>) { +fn path_to_string(path: &QPath<'_>) -> Result<String, ()> { + fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { match *path { QPath::Resolved(_, path) => { for (i, segment) in path.segments.iter().enumerate() { @@ -751,16 +751,18 @@ fn path_to_string(path: &QPath<'_>) -> String { }, QPath::TypeRelative(ty, segment) => match &ty.kind { hir::TyKind::Path(inner_path) => { - inner(s, inner_path); + inner(s, inner_path)?; *s += ", "; write!(s, "{:?}", segment.ident.as_str()).unwrap(); }, other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), }, - QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"), + QPath::LangItem(..) => return Err(()), } + + Ok(()) } let mut s = String::new(); - inner(&mut s, path); - s + inner(&mut s, path)?; + Ok(s) } diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 23da1de7730..8829f188fe7 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -8,8 +8,9 @@ use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::Deserialize; use std::fmt::{Debug, Display, Formatter}; use std::ops::Range; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::str::FromStr; +use std::sync::OnceLock; use std::{cmp, env, fmt, fs, io}; #[rustfmt::skip] @@ -78,62 +79,35 @@ pub struct TryConf { impl TryConf { fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { - ConfError::from_toml(file, error).into() - } -} - -impl From<ConfError> for TryConf { - fn from(value: ConfError) -> Self { Self { conf: Conf::default(), - errors: vec![value], + errors: vec![ConfError::from_toml(file, error)], warnings: vec![], } } } -impl From<io::Error> for TryConf { - fn from(value: io::Error) -> Self { - ConfError::from(value).into() - } -} - #[derive(Debug)] pub struct ConfError { pub message: String, - pub span: Option<Span>, + pub span: Span, } impl ConfError { fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { - if let Some(span) = error.span() { - Self::spanned(file, error.message(), span) - } else { - Self { - message: error.message().to_string(), - span: None, - } - } + let span = error.span().unwrap_or(0..file.source_len.0 as usize); + Self::spanned(file, error.message(), span) } fn spanned(file: &SourceFile, message: impl Into<String>, span: Range<usize>) -> Self { Self { message: message.into(), - span: Some(Span::new( + span: Span::new( file.start_pos + BytePos::from_usize(span.start), file.start_pos + BytePos::from_usize(span.end), SyntaxContext::root(), None, - )), - } - } -} - -impl From<io::Error> for ConfError { - fn from(value: io::Error) -> Self { - Self { - message: value.to_string(), - span: None, + ), } } } @@ -297,7 +271,7 @@ define_Conf! { /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, 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, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE. /// /// The minimum rust version that the project supports - (msrv: Option<String> = None), + (msrv: crate::Msrv = crate::Msrv::empty()), /// DEPRECATED LINT: BLACKLISTED_NAME. /// /// Use the Disallowed Names lint instead @@ -360,6 +334,10 @@ define_Conf! { /// /// The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold: u64 = 3), + /// Lint: STRUCT_VARIANT_NAMES. + /// + /// The minimum number of struct fields for the lints about field names to trigger + (struct_field_name_threshold: u64 = 3), /// Lint: LARGE_ENUM_VARIANT. /// /// The maximum size of an enum's variant to avoid box suggestion @@ -641,15 +619,8 @@ pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> { } } -/// Read the `toml` configuration file. -/// -/// In case of error, the function tries to continue as much as possible. -pub fn read(sess: &Session, path: &Path) -> TryConf { - let file = match sess.source_map().load_file(path) { - Err(e) => return e.into(), - Ok(file) => file, - }; - match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(&file)) { +fn deserialize(file: &SourceFile) -> TryConf { + match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(file)) { Ok(mut conf) => { extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); @@ -662,7 +633,7 @@ pub fn read(sess: &Session, path: &Path) -> TryConf { conf }, - Err(e) => TryConf::from_toml_error(&file, &e), + Err(e) => TryConf::from_toml_error(file, &e), } } @@ -672,6 +643,60 @@ fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) { } } +impl Conf { + pub fn read(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> &'static Conf { + static CONF: OnceLock<Conf> = OnceLock::new(); + CONF.get_or_init(|| Conf::read_inner(sess, path)) + } + + fn read_inner(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf { + match path { + Ok((_, warnings)) => { + for warning in warnings { + sess.warn(warning.clone()); + } + }, + Err(error) => { + sess.err(format!("error finding Clippy's configuration file: {error}")); + }, + } + + let TryConf { + mut conf, + errors, + warnings, + } = match path { + Ok((Some(path), _)) => match sess.source_map().load_file(path) { + Ok(file) => deserialize(&file), + Err(error) => { + sess.err(format!("failed to read `{}`: {error}", path.display())); + TryConf::default() + }, + }, + _ => TryConf::default(), + }; + + conf.msrv.read_cargo(sess); + + // all conf errors are non-fatal, we just use the default conf in case of error + for error in errors { + sess.span_err( + error.span, + format!("error reading Clippy's configuration file: {}", error.message), + ); + } + + for warning in warnings { + sess.span_warn( + warning.span, + format!("error reading Clippy's configuration file: {}", warning.message), + ); + } + + conf + } +} + const SEPARATOR_WIDTH: usize = 4; #[derive(Debug)] diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs index fe2f12fe833..8cdd5ea8903 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -30,7 +30,7 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { if_chain_local_span(cx, local, if_chain_span), "`let` expression should be above the `if_chain!`", ); - } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + } else if local.span.eq_ctxt(block.span) && is_if_chain_then(after, block.expr, if_chain_span) { span_lint( cx, IF_CHAIN_STYLE, diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 82f9d4e41e8..fc9afe5ca8b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -13,6 +13,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::ConstValue; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; use rustc_span::symbol::Symbol; use std::borrow::Cow; @@ -160,12 +161,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { impl InterningDefinedSymbol { fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[ - &paths::SYMBOL_AS_STR, - &paths::SYMBOL_TO_IDENT_STRING, - &paths::TO_STRING_METHOD, - ]; + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[&paths::SYMBOL_AS_STR, &paths::SYMBOL_TO_IDENT_STRING]; let call = if_chain! { if let ExprKind::AddrOf(_, _, e) = expr.kind; if let ExprKind::Unary(UnOp::Deref, e) = e.kind; @@ -186,9 +183,19 @@ impl InterningDefinedSymbol { }; // ...which converts it to a string let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + if let Some(is_to_owned) = paths + .iter() + .find_map(|path| if match_def_path(cx, did, path) { + Some(path == &paths::SYMBOL_TO_IDENT_STRING) + } else { + None + }) + .or_else(|| if cx.tcx.is_diagnostic_item(sym::to_string_method, did) { + Some(true) + } else { + None + }); then { - let is_to_owned = path.last().unwrap().ends_with("string"); return Some(SymbolStrExpr::Expr { item, is_ident, diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 8522493f67b..90091ca927a 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -9,6 +9,7 @@ arrayvec = { version = "0.7", default-features = false } if_chain = "1.0" itertools = "0.10.1" rustc-semver = "1.1" +serde = { version = "1.0" } [features] deny-warnings = [] diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 0bae7056c4f..79c04c7c7f4 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -504,7 +504,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { }, (Some(Constant::Vec(vec)), _) => { if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { - match vec.get(0) { + match vec.first() { Some(Constant::F32(x)) => Some(Constant::F32(*x)), Some(Constant::F64(x)) => Some(Constant::F64(*x)), _ => None, diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 741f9f54883..edea4b3667f 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -449,7 +449,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - } else if name.ident.name == symbol::kw::Default { return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { - let arg = args.get(0)?; + let arg = args.first()?; return match constant_simple(cx, cx.typeck_results(), arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c2c97259d38..93b37022822 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2027,48 +2027,88 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use)) } -/// Checks if an expression represents the identity function -/// Only examines closures and `std::convert::identity` -pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Checks if a function's body represents the identity function. Looks for bodies of the form: - /// * `|x| x` - /// * `|x| return x` - /// * `|x| { return x }` - /// * `|x| { return x; }` - fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { - let id = if_chain! { - if let [param] = func.params; - if let PatKind::Binding(_, id, _, _) = param.pat.kind; - then { - id - } else { - return false; - } - }; +/// Checks if a function's body represents the identity function. Looks for bodies of the form: +/// * `|x| x` +/// * `|x| return x` +/// * `|x| { return x }` +/// * `|x| { return x; }` +/// +/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead. +fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { + let id = if_chain! { + if let [param] = func.params; + if let PatKind::Binding(_, id, _, _) = param.pat.kind; + then { + id + } else { + return false; + } + }; - let mut expr = func.value; - loop { - match expr.kind { - #[rustfmt::skip] - ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, ) - | ExprKind::Ret(Some(e)) => expr = e, - #[rustfmt::skip] - ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => { - if_chain! { - if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind; - if let ExprKind::Ret(Some(ret_val)) = e.kind; - then { - expr = ret_val; - } else { - return false; - } - } + let mut expr = func.value; + loop { + match expr.kind { + ExprKind::Block( + &Block { + stmts: [], + expr: Some(e), + .. }, - _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(), - } + _, + ) + | ExprKind::Ret(Some(e)) => expr = e, + ExprKind::Block( + &Block { + stmts: [stmt], + expr: None, + .. + }, + _, + ) => { + if_chain! { + if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind; + if let ExprKind::Ret(Some(ret_val)) = e.kind; + then { + expr = ret_val; + } else { + return false; + } + } + }, + _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(), } } +} +/// This is the same as [`is_expr_identity_function`], but does not consider closures +/// with type annotations for its bindings (or similar) as identity functions: +/// * `|x: u8| x` +/// * `std::convert::identity::<u8>` +pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Closure(&Closure { body, fn_decl, .. }) + if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) => + { + is_body_identity_function(cx, cx.tcx.hir().body(body)) + }, + ExprKind::Path(QPath::Resolved(_, path)) + if path.segments.iter().all(|seg| seg.infer_args) + && let Some(did) = path.res.opt_def_id() => { + cx.tcx.is_diagnostic_item(sym::convert_identity, did) + }, + _ => false, + } +} + +/// Checks if an expression represents the identity function +/// Only examines closures and `std::convert::identity` +/// +/// NOTE: If you want to use this function to find out if a closure is unnecessary, you likely want +/// to call [`is_expr_untyped_identity_function`] instead, which makes sure that the closure doesn't +/// have type annotations. This is important because removing a closure with bindings can +/// remove type information that helped type inference before, which can then lead to compile +/// errors. +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)), _ => path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index df839c2106f..c6a48874e09 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,9 +1,7 @@ -use std::sync::OnceLock; - use rustc_ast::Attribute; use rustc_semver::RustcVersion; use rustc_session::Session; -use rustc_span::Span; +use serde::Deserialize; use crate::attrs::get_unique_attr; @@ -53,65 +51,45 @@ msrv_aliases! { 1,15,0 { MAYBE_BOUND_IN_WHERE } } -fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> { - if let Ok(version) = RustcVersion::parse(msrv) { - return Some(version); - } else if let Some(sess) = sess { - if let Some(span) = span { - sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); - } - } - None -} - /// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]` -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Msrv { stack: Vec<RustcVersion>, } +impl<'de> Deserialize<'de> for Msrv { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + let v = String::deserialize(deserializer)?; + RustcVersion::parse(&v) + .map(|v| Msrv { stack: vec![v] }) + .map_err(|_| serde::de::Error::custom("not a valid Rust version")) + } +} + impl Msrv { - fn new(initial: Option<RustcVersion>) -> Self { - Self { - stack: Vec::from_iter(initial), - } + pub fn empty() -> Msrv { + Msrv { stack: Vec::new() } } - fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self { + pub fn read_cargo(&mut self, sess: &Session) { let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") .ok() - .and_then(|v| parse_msrv(&v, None, None)); - let clippy_msrv = conf_msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - // if both files have an msrv, let's compare them and emit a warning if they differ - if let Some(cargo_msrv) = cargo_msrv - && let Some(clippy_msrv) = clippy_msrv - && clippy_msrv != cargo_msrv - { - sess.warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); + .and_then(|v| RustcVersion::parse(&v).ok()); + + match (self.current(), cargo_msrv) { + (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv], + (Some(clippy_msrv), Some(cargo_msrv)) => { + if clippy_msrv != cargo_msrv { + sess.warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); + } + }, + _ => {}, } - - Self::new(clippy_msrv.or(cargo_msrv)) - } - - /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version` - /// field in `Cargo.toml` - /// - /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the - /// `register_{late,early}_pass` callbacks - pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self { - static PARSED: OnceLock<Msrv> = OnceLock::new(); - - PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess)) } pub fn current(&self) -> Option<RustcVersion> { @@ -125,10 +103,14 @@ impl Msrv { fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> { if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") { if let Some(msrv) = msrv_attr.value_str() { - return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); - } + if let Ok(version) = RustcVersion::parse(msrv.as_str()) { + return Some(version); + } - sess.span_err(msrv_attr.span, "bad clippy attribute"); + sess.span_err(msrv_attr.span, format!("`{msrv}` is not a valid Rust version")); + } else { + sess.span_err(msrv_attr.span, "bad clippy attribute"); + } } None diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 1f2bb16f459..4a20399e364 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -98,7 +98,6 @@ pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; #[cfg(feature = "internal")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; -pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs index 03a9d3c25fd..69c25b427e1 100644 --- a/src/tools/clippy/clippy_utils/src/str_utils.rs +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -236,6 +236,59 @@ pub fn count_match_end(str1: &str, str2: &str) -> StrCount { }) } +/// Returns a `snake_case` version of the input +/// ``` +/// use clippy_utils::str_utils::to_snake_case; +/// assert_eq!(to_snake_case("AbcDef"), "abc_def"); +/// assert_eq!(to_snake_case("ABCD"), "a_b_c_d"); +/// assert_eq!(to_snake_case("AbcDD"), "abc_d_d"); +/// assert_eq!(to_snake_case("Abc1DD"), "abc1_d_d"); +/// ``` +pub fn to_snake_case(name: &str) -> String { + let mut s = String::new(); + for (i, c) in name.chars().enumerate() { + if c.is_uppercase() { + // characters without capitalization are considered lowercase + if i != 0 { + s.push('_'); + } + s.extend(c.to_lowercase()); + } else { + s.push(c); + } + } + s +} +/// Returns a `CamelCase` version of the input +/// ``` +/// use clippy_utils::str_utils::to_camel_case; +/// assert_eq!(to_camel_case("abc_def"), "AbcDef"); +/// assert_eq!(to_camel_case("a_b_c_d"), "ABCD"); +/// assert_eq!(to_camel_case("abc_d_d"), "AbcDD"); +/// assert_eq!(to_camel_case("abc1_d_d"), "Abc1DD"); +/// ``` +pub fn to_camel_case(item_name: &str) -> String { + let mut s = String::new(); + let mut up = true; + for c in item_name.chars() { + if c.is_uppercase() { + // we only turn snake case text into CamelCase + return item_name.to_string(); + } + if c == '_' { + up = true; + continue; + } + if up { + up = false; + s.extend(c.to_uppercase()); + } else { + s.push(c); + } + } + s +} + #[cfg(test)] mod test { use super::*; diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index fe2c77ab47f..7c5b5e97a5c 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-10-06" +channel = "nightly-2023-10-21" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index d47767faed9..0e81419f1fa 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -147,9 +147,9 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(sess, &conf_path); - clippy_lints::register_plugins(lint_store, sess, &conf); - clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf); + let conf = clippy_lints::Conf::read(sess, &conf_path); + clippy_lints::register_plugins(lint_store, sess, conf); + clippy_lints::register_pre_expansion_lints(lint_store, conf); clippy_lints::register_renamed(lint_store); })); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index f340cf5938a..1494c7d3179 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -105,27 +105,20 @@ static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| { // whether to run internal tests or not const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); -fn canonicalize(path: impl AsRef<Path>) -> PathBuf { - let path = path.as_ref(); - fs::create_dir_all(path).unwrap(); - fs::canonicalize(path).unwrap_or_else(|err| panic!("{} cannot be canonicalized: {err}", path.display())) -} - fn base_config(test_dir: &str) -> (Config, Args) { let mut args = Args::test().unwrap(); args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); + let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); let mut config = Config { mode: Mode::Yolo { rustfix: ui_test::RustfixMode::Everything, }, - stderr_filters: vec![(Match::PathBackslash, b"/")], - stdout_filters: vec![], filter_files: env::var("TESTNAME") .map(|filters| filters.split(',').map(str::to_string).collect()) .unwrap_or_default(), target: None, - out_dir: canonicalize(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())).join("ui_test"), + out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; config.with_args(&args, /* bless by default */ false); @@ -168,19 +161,13 @@ fn run_ui() { config .program .envs - .push(("CLIPPY_CONF_DIR".into(), Some(canonicalize("tests").into()))); - - let quiet = args.quiet; + .push(("CLIPPY_CONF_DIR".into(), Some("tests".into()))); ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - if quiet { - status_emitter::Text::quiet() - } else { - status_emitter::Text::verbose() - }, + status_emitter::Text::from(args.format), ) .unwrap(); } @@ -194,17 +181,12 @@ fn run_internal_tests() { if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling { *err = "cargo uitest --features internal -- -- --bless".into(); } - let quiet = args.quiet; ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - if quiet { - status_emitter::Text::quiet() - } else { - status_emitter::Text::verbose() - }, + status_emitter::Text::from(args.format), ) .unwrap(); } @@ -212,22 +194,9 @@ fn run_internal_tests() { fn run_ui_toml() { let (mut config, args) = base_config("ui-toml"); - config.stderr_filters = vec![ - ( - Match::Exact( - canonicalize("tests") - .parent() - .unwrap() - .to_string_lossy() - .as_bytes() - .to_vec(), - ), - b"$DIR", - ), - (Match::Exact(b"\\".to_vec()), b"/"), - ]; - - let quiet = args.quiet; + config + .stderr_filters + .push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR")); ui_test::run_tests_generic( vec![config], @@ -238,11 +207,7 @@ fn run_ui_toml() { .envs .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); }, - if quiet { - status_emitter::Text::quiet() - } else { - status_emitter::Text::verbose() - }, + status_emitter::Text::from(args.format), ) .unwrap(); } @@ -270,22 +235,9 @@ fn run_ui_cargo() { }); config.edition = None; - config.stderr_filters = vec![ - ( - Match::Exact( - canonicalize("tests") - .parent() - .unwrap() - .to_string_lossy() - .as_bytes() - .to_vec(), - ), - b"$DIR", - ), - (Match::Exact(b"\\".to_vec()), b"/"), - ]; - - let quiet = args.quiet; + config + .stderr_filters + .push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR")); let ignored_32bit = |path: &Path| { // FIXME: for some reason the modules are linted in a different order for this test @@ -297,20 +249,8 @@ fn run_ui_cargo() { |path, config| { path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config) && !ignored_32bit(path) }, - |config, path, _file_contents| { - config.out_dir = canonicalize( - std::env::current_dir() - .unwrap() - .join("target") - .join("ui_test_cargo/") - .join(path.parent().unwrap()), - ); - }, - if quiet { - status_emitter::Text::quiet() - } else { - status_emitter::Text::verbose() - }, + |_config, _path, _file_contents| {}, + status_emitter::Text::from(args.format), ) .unwrap(); } diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs index 60be2978813..f6abb3cc3d7 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -12,5 +12,5 @@ fn main() { const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; // Don't lint, not a diagnostic or language item - const OPS_MOD: [&str; 5] = ["core", "ops"]; + const OPS_MOD: [&str; 2] = ["core", "ops"]; } diff --git a/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml new file mode 100644 index 00000000000..34a1036e891 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/clippy.toml @@ -0,0 +1 @@ +ignore-interior-mutability = ["borrow_interior_mutable_const_ignore::Counted"] \ No newline at end of file diff --git a/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs new file mode 100644 index 00000000000..79c7cef6ce1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/borrow_interior_mutable_const/ignore.rs @@ -0,0 +1,37 @@ +//@compile-flags: --crate-name borrow_interior_mutable_const_ignore + +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] + +use core::cell::Cell; +use std::cmp::{Eq, PartialEq}; +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::sync::atomic::{AtomicUsize, Ordering}; + +struct Counted<T> { + count: AtomicUsize, + val: T, +} + +impl<T> Counted<T> { + const fn new(val: T) -> Self { + Self { + count: AtomicUsize::new(0), + val, + } + } +} + +enum OptionalCell { + Unfrozen(Counted<bool>), + Frozen, +} + +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Counted::new(true)); +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +fn main() { + let _ = &UNFROZEN_VARIANT; +} diff --git a/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml new file mode 100644 index 00000000000..71d13212e2a --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/clippy.toml @@ -0,0 +1 @@ +ignore-interior-mutability = ["declare_interior_mutable_const_ignore::Counted"] \ No newline at end of file diff --git a/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs new file mode 100644 index 00000000000..6385cf4f852 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/declare_interior_mutable_const/ignore.rs @@ -0,0 +1,46 @@ +//@compile-flags: --crate-name declare_interior_mutable_const_ignore + +#![warn(clippy::declare_interior_mutable_const)] +#![allow(clippy::borrow_interior_mutable_const)] + +use core::cell::Cell; +use std::cmp::{Eq, PartialEq}; +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::sync::atomic::{AtomicUsize, Ordering}; + +struct Counted<T> { + count: AtomicUsize, + val: T, +} + +impl<T> Counted<T> { + const fn new(val: T) -> Self { + Self { + count: AtomicUsize::new(0), + val, + } + } +} + +enum OptionalCell { + Unfrozen(Counted<bool>), + Frozen, +} + +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Counted::new(true)); +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +const fn unfrozen_variant() -> OptionalCell { + OptionalCell::Unfrozen(Counted::new(true)) +} + +const fn frozen_variant() -> OptionalCell { + OptionalCell::Frozen +} + +const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); +const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs b/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs deleted file mode 100644 index 8f4e178ccfe..00000000000 --- a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.rs +++ /dev/null @@ -1,16 +0,0 @@ -enum Foo { - AFoo, - BFoo, - CFoo, - DFoo, -} -enum Foo2 { - //~^ ERROR: all variants have the same postfix - AFoo, - BFoo, - CFoo, - DFoo, - EFoo, -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr b/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr deleted file mode 100644 index 11039b1db48..00000000000 --- a/src/tools/clippy/tests/ui-toml/enum_variant_names/enum_variant_names.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: all variants have the same postfix: `Foo` - --> $DIR/enum_variant_names.rs:7:1 - | -LL | / enum Foo2 { -LL | | -LL | | AFoo, -LL | | BFoo, -... | -LL | | EFoo, -LL | | } - | |_^ - | - = help: remove the postfixes and use full paths to the variants instead of glob imports - = note: `-D clippy::enum-variant-names` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]` - -error: aborting due to previous error - diff --git a/src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml new file mode 100644 index 00000000000..87e1f235741 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/clippy.toml @@ -0,0 +1 @@ +avoid-breaking-exported-api = false \ No newline at end of file diff --git a/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs new file mode 100644 index 00000000000..08fc7edf1c8 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.rs @@ -0,0 +1,16 @@ +//! As avoid-breaking-exported-api is `false`, nothing here should lint +#![warn(clippy::impl_trait_in_params)] +#![no_main] +//@no-rustfix + +pub trait Trait {} + +trait Private { + fn t(_: impl Trait); + fn tt<T: Trait>(_: T); +} + +pub trait Public { + fn t(_: impl Trait); //~ ERROR: `impl Trait` used as a function parameter + fn tt<T: Trait>(_: T); +} diff --git a/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr new file mode 100644 index 00000000000..80c4f5ed4b0 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/impl_trait_in_params/impl_trait_in_params.stderr @@ -0,0 +1,15 @@ +error: `impl Trait` used as a function parameter + --> $DIR/impl_trait_in_params.rs:14:13 + | +LL | fn t(_: impl Trait); + | ^^^^^^^^^^ + | + = note: `-D clippy::impl-trait-in-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::impl_trait_in_params)]` +help: add a type parameter + | +LL | fn t<{ /* Generic name */ }: Trait>(_: impl Trait); + | +++++++++++++++++++++++++++++++ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs index 03fa719975b..85e2fb8c797 100644 --- a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs +++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs @@ -1,4 +1,4 @@ -//@error-in-other-file: `invalid.version` is not a valid Rust version +//@error-in-other-file: not a valid Rust version #![allow(clippy::redundant_clone)] diff --git a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr index e9d8fd2e0f5..f127c2408f9 100644 --- a/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr +++ b/src/tools/clippy/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version +error: error reading Clippy's configuration file: not a valid Rust version + --> $DIR/$DIR/clippy.toml:1:8 + | +LL | msrv = "invalid.version" + | ^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/clippy.toml index f85aade6ae8..d41edbaf7fa 100644 --- a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/clippy.toml @@ -1 +1,2 @@ +struct-field-name-threshold = 0 enum-variant-name-threshold = 0 diff --git a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/item_name_repetitions.rs index 6918d7528c1..b633dcbd19e 100644 --- a/src/tools/clippy/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold0/item_name_repetitions.rs @@ -1,3 +1,5 @@ +struct Data {} + enum Actions {} fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/enum_variant_names/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/clippy.toml index 0ad7a979948..028a6279079 100644 --- a/src/tools/clippy/tests/ui-toml/enum_variant_names/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/clippy.toml @@ -1 +1,2 @@ enum-variant-name-threshold = 5 +struct-field-name-threshold = 5 diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs new file mode 100644 index 00000000000..d437311691d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.rs @@ -0,0 +1,32 @@ +#![warn(clippy::struct_field_names)] + +struct Data { + a_data: u8, + b_data: u8, + c_data: u8, + d_data: u8, +} +struct Data2 { + //~^ ERROR: all fields have the same postfix + a_data: u8, + b_data: u8, + c_data: u8, + d_data: u8, + e_data: u8, +} +enum Foo { + AFoo, + BFoo, + CFoo, + DFoo, +} +enum Foo2 { + //~^ ERROR: all variants have the same postfix + AFoo, + BFoo, + CFoo, + DFoo, + EFoo, +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr new file mode 100644 index 00000000000..33802c44bf9 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/threshold5/item_name_repetitions.stderr @@ -0,0 +1,34 @@ +error: all fields have the same postfix: `data` + --> $DIR/item_name_repetitions.rs:9:1 + | +LL | / struct Data2 { +LL | | +LL | | a_data: u8, +LL | | b_data: u8, +... | +LL | | e_data: u8, +LL | | } + | |_^ + | + = help: remove the postfixes + = note: `-D clippy::struct-field-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::struct_field_names)]` + +error: all variants have the same postfix: `Foo` + --> $DIR/item_name_repetitions.rs:23:1 + | +LL | / enum Foo2 { +LL | | +LL | | AFoo, +LL | | BFoo, +... | +LL | | EFoo, +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + = note: `-D clippy::enum-variant-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs index 830d71f61dd..cd53f504459 100644 --- a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs +++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs @@ -1,5 +1,6 @@ //! this is crate #![allow(missing_docs)] +#![allow(clippy::struct_field_names)] #![warn(clippy::missing_docs_in_private_items)] /// this is mod diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr index 1ecdabbc03e..2cf20b46049 100644 --- a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr +++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a function - --> $DIR/pub_crate_missing_doc.rs:12:5 + --> $DIR/pub_crate_missing_doc.rs:13:5 | LL | pub(crate) fn crate_no_docs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,25 +8,25 @@ LL | pub(crate) fn crate_no_docs() {} = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` error: missing documentation for a function - --> $DIR/pub_crate_missing_doc.rs:15:5 + --> $DIR/pub_crate_missing_doc.rs:16:5 | LL | pub(super) fn super_no_docs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/pub_crate_missing_doc.rs:23:9 + --> $DIR/pub_crate_missing_doc.rs:24:9 | LL | pub(crate) fn sub_crate_no_docs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/pub_crate_missing_doc.rs:33:9 + --> $DIR/pub_crate_missing_doc.rs:34:9 | LL | pub(crate) crate_field_no_docs: (), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct - --> $DIR/pub_crate_missing_doc.rs:39:5 + --> $DIR/pub_crate_missing_doc.rs:40:5 | LL | / pub(crate) struct CrateStructNoDocs { LL | | /// some docs @@ -38,13 +38,13 @@ LL | | } | |_____^ error: missing documentation for a struct field - --> $DIR/pub_crate_missing_doc.rs:42:9 + --> $DIR/pub_crate_missing_doc.rs:43:9 | LL | pub(crate) crate_field_no_docs: (), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a type alias - --> $DIR/pub_crate_missing_doc.rs:51:1 + --> $DIR/pub_crate_missing_doc.rs:52:1 | LL | type CrateTypedefNoDocs = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 4bed5c149f5..2f9eaa5178c 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -53,6 +53,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect single-char-binding-names-threshold stack-size-threshold standard-macro-braces + struct-field-name-threshold suppress-restriction-lint-in-const third-party too-large-for-stack @@ -126,6 +127,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect single-char-binding-names-threshold stack-size-threshold standard-macro-braces + struct-field-name-threshold suppress-restriction-lint-in-const third-party too-large-for-stack diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.rs b/src/tools/clippy/tests/ui/author/macro_in_closure.rs new file mode 100644 index 00000000000..444e6a12165 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.rs @@ -0,0 +1,5 @@ +fn main() { + #[clippy::author] + let print_text = |x| println!("{}", x); + print_text("hello"); +} diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout new file mode 100644 index 00000000000..9ab71986f40 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -0,0 +1,39 @@ +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Closure(CaptureBy::Ref, fn_decl, body_id, _, None) = init.kind + && let FnRetTy::DefaultReturn(_) = fn_decl.output + && expr = &cx.tcx.hir().body(body_id).value + && let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Call(func, args) = e.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["$crate", "io", "_print"]) + && args.len() == 1 + && let ExprKind::Call(func1, args1) = args[0].kind + && let ExprKind::Path(ref qpath1) = func1.kind + && args1.len() == 2 + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::Array(elements) = inner.kind + && elements.len() == 2 + && let ExprKind::Lit(ref lit) = elements[0].kind + && let LitKind::Str(s, _) = lit.node + && s.as_str() == "" + && let ExprKind::Lit(ref lit1) = elements[1].kind + && let LitKind::Str(s1, _) = lit1.node + && s1.as_str() == "\n" + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind + && let ExprKind::Array(elements1) = inner1.kind + && elements1.len() == 1 + && let ExprKind::Call(func2, args2) = elements1[0].kind + && let ExprKind::Path(ref qpath2) = func2.kind + && args2.len() == 1 + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind + && let ExprKind::Path(ref qpath3) = inner2.kind + && match_qpath(qpath3, &["x"]) + && block.expr.is_none() + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "print_text" +{ + // report your lint here +} diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.rs b/src/tools/clippy/tests/ui/author/macro_in_loop.rs new file mode 100644 index 00000000000..8a520501f8d --- /dev/null +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.rs @@ -0,0 +1,8 @@ +#![feature(stmt_expr_attributes)] + +fn main() { + #[clippy::author] + for i in 0..1 { + println!("{}", i); + } +} diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout new file mode 100644 index 00000000000..bd054b6abc4 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout @@ -0,0 +1,48 @@ +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind + && name.as_str() == "i" + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Block(block1, None) = e.kind + && block1.stmts.len() == 1 + && let StmtKind::Semi(e1) = block1.stmts[0].kind + && let ExprKind::Call(func, args) = e1.kind + && let ExprKind::Path(ref qpath1) = func.kind + && match_qpath(qpath1, &["$crate", "io", "_print"]) + && args.len() == 1 + && let ExprKind::Call(func1, args1) = args[0].kind + && let ExprKind::Path(ref qpath2) = func1.kind + && args1.len() == 2 + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind + && let ExprKind::Array(elements) = inner.kind + && elements.len() == 2 + && let ExprKind::Lit(ref lit2) = elements[0].kind + && let LitKind::Str(s, _) = lit2.node + && s.as_str() == "" + && let ExprKind::Lit(ref lit3) = elements[1].kind + && let LitKind::Str(s1, _) = lit3.node + && s1.as_str() == "\n" + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind + && let ExprKind::Array(elements1) = inner1.kind + && elements1.len() == 1 + && let ExprKind::Call(func2, args2) = elements1[0].kind + && let ExprKind::Path(ref qpath3) = func2.kind + && args2.len() == 1 + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind + && let ExprKind::Path(ref qpath4) = inner2.kind + && match_qpath(qpath4, &["i"]) + && block1.expr.is_none() + && block.expr.is_none() +{ + // report your lint here +} diff --git a/src/tools/clippy/tests/ui/get_first.fixed b/src/tools/clippy/tests/ui/get_first.fixed index b1a597fc4dd..a7cdd2a93ba 100644 --- a/src/tools/clippy/tests/ui/get_first.fixed +++ b/src/tools/clippy/tests/ui/get_first.fixed @@ -14,17 +14,20 @@ impl Bar { fn main() { let x = vec![2, 3, 5]; - let _ = x.first(); // Use x.first() + let _ = x.first(); + //~^ ERROR: accessing first element with `x.get(0)` let _ = x.get(1); let _ = x[0]; let y = [2, 3, 5]; - let _ = y.first(); // Use y.first() + let _ = y.first(); + //~^ ERROR: accessing first element with `y.get(0)` let _ = y.get(1); let _ = y[0]; let z = &[2, 3, 5]; - let _ = z.first(); // Use z.first() + let _ = z.first(); + //~^ ERROR: accessing first element with `z.get(0)` let _ = z.get(1); let _ = z[0]; @@ -37,4 +40,8 @@ fn main() { let bar = Bar { arr: [0, 1, 2] }; let _ = bar.get(0); // Do not lint, because Bar is struct. + + let non_primitives = [vec![1, 2], vec![3, 4]]; + let _ = non_primitives.first(); + //~^ ERROR: accessing first element with `non_primitives.get(0)` } diff --git a/src/tools/clippy/tests/ui/get_first.rs b/src/tools/clippy/tests/ui/get_first.rs index e27ee4be8c0..cca743c4bf5 100644 --- a/src/tools/clippy/tests/ui/get_first.rs +++ b/src/tools/clippy/tests/ui/get_first.rs @@ -14,17 +14,20 @@ impl Bar { fn main() { let x = vec![2, 3, 5]; - let _ = x.get(0); // Use x.first() + let _ = x.get(0); + //~^ ERROR: accessing first element with `x.get(0)` let _ = x.get(1); let _ = x[0]; let y = [2, 3, 5]; - let _ = y.get(0); // Use y.first() + let _ = y.get(0); + //~^ ERROR: accessing first element with `y.get(0)` let _ = y.get(1); let _ = y[0]; let z = &[2, 3, 5]; - let _ = z.get(0); // Use z.first() + let _ = z.get(0); + //~^ ERROR: accessing first element with `z.get(0)` let _ = z.get(1); let _ = z[0]; @@ -37,4 +40,8 @@ fn main() { let bar = Bar { arr: [0, 1, 2] }; let _ = bar.get(0); // Do not lint, because Bar is struct. + + let non_primitives = [vec![1, 2], vec![3, 4]]; + let _ = non_primitives.get(0); + //~^ ERROR: accessing first element with `non_primitives.get(0)` } diff --git a/src/tools/clippy/tests/ui/get_first.stderr b/src/tools/clippy/tests/ui/get_first.stderr index 56b4c29a313..8ee66e33cc8 100644 --- a/src/tools/clippy/tests/ui/get_first.stderr +++ b/src/tools/clippy/tests/ui/get_first.stderr @@ -1,23 +1,29 @@ error: accessing first element with `x.get(0)` --> $DIR/get_first.rs:17:13 | -LL | let _ = x.get(0); // Use x.first() +LL | let _ = x.get(0); | ^^^^^^^^ help: try: `x.first()` | = note: `-D clippy::get-first` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::get_first)]` error: accessing first element with `y.get(0)` - --> $DIR/get_first.rs:22:13 + --> $DIR/get_first.rs:23:13 | -LL | let _ = y.get(0); // Use y.first() +LL | let _ = y.get(0); | ^^^^^^^^ help: try: `y.first()` error: accessing first element with `z.get(0)` - --> $DIR/get_first.rs:27:13 + --> $DIR/get_first.rs:29:13 | -LL | let _ = z.get(0); // Use z.first() +LL | let _ = z.get(0); | ^^^^^^^^ help: try: `z.first()` -error: aborting due to 3 previous errors +error: accessing first element with `non_primitives.get(0)` + --> $DIR/get_first.rs:45:13 + | +LL | let _ = non_primitives.get(0); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `non_primitives.first()` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.rs b/src/tools/clippy/tests/ui/impl_trait_in_params.rs index b652e4a4abe..a6251a370d1 100644 --- a/src/tools/clippy/tests/ui/impl_trait_in_params.rs +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.rs @@ -1,20 +1,47 @@ #![allow(unused)] #![warn(clippy::impl_trait_in_params)] + //@no-rustfix pub trait Trait {} pub trait AnotherTrait<T> {} // Should warn pub fn a(_: impl Trait) {} -//~^ ERROR: '`impl Trait` used as a function parameter' -//~| NOTE: `-D clippy::impl-trait-in-params` implied by `-D warnings` +//~^ ERROR: `impl Trait` used as a function parameter pub fn c<C: Trait>(_: C, _: impl Trait) {} -//~^ ERROR: '`impl Trait` used as a function parameter' -fn d(_: impl AnotherTrait<u32>) {} +//~^ ERROR: `impl Trait` used as a function parameter // Shouldn't warn pub fn b<B: Trait>(_: B) {} fn e<T: AnotherTrait<u32>>(_: T) {} +fn d(_: impl AnotherTrait<u32>) {} + +//------ IMPLS + +pub trait Public { + // See test in ui-toml for a case where avoid-breaking-exported-api is set to false + fn t(_: impl Trait); + fn tt<T: Trait>(_: T) {} +} + +trait Private { + // This shouldn't lint + fn t(_: impl Trait); + fn tt<T: Trait>(_: T) {} +} + +struct S; +impl S { + pub fn h(_: impl Trait) {} //~ ERROR: `impl Trait` used as a function parameter + fn i(_: impl Trait) {} + pub fn j<J: Trait>(_: J) {} + pub fn k<K: AnotherTrait<u32>>(_: K, _: impl AnotherTrait<u32>) {} //~ ERROR: `impl Trait` used as a function parameter +} + +// Trying with traits +impl Public for S { + fn t(_: impl Trait) {} +} fn main() {} diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr index 36b4f27e9a4..0ae7a3672d1 100644 --- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr @@ -1,5 +1,5 @@ -error: '`impl Trait` used as a function parameter' - --> $DIR/impl_trait_in_params.rs:8:13 +error: `impl Trait` used as a function parameter + --> $DIR/impl_trait_in_params.rs:9:13 | LL | pub fn a(_: impl Trait) {} | ^^^^^^^^^^ @@ -11,7 +11,7 @@ help: add a type parameter LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {} | +++++++++++++++++++++++++++++++ -error: '`impl Trait` used as a function parameter' +error: `impl Trait` used as a function parameter --> $DIR/impl_trait_in_params.rs:11:29 | LL | pub fn c<C: Trait>(_: C, _: impl Trait) {} @@ -22,5 +22,27 @@ help: add a type parameter LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {} | +++++++++++++++++++++++++++++++ -error: aborting due to 2 previous errors +error: `impl Trait` used as a function parameter + --> $DIR/impl_trait_in_params.rs:36:17 + | +LL | pub fn h(_: impl Trait) {} + | ^^^^^^^^^^ + | +help: add a type parameter + | +LL | pub fn h<{ /* Generic name */ }: Trait>(_: impl Trait) {} + | +++++++++++++++++++++++++++++++ + +error: `impl Trait` used as a function parameter + --> $DIR/impl_trait_in_params.rs:39:45 + | +LL | pub fn k<K: AnotherTrait<u32>>(_: K, _: impl AnotherTrait<u32>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: add a type parameter + | +LL | pub fn k<K: AnotherTrait<u32>, { /* Generic name */ }: AnotherTrait<u32>>(_: K, _: impl AnotherTrait<u32>) {} + | +++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs index 6be3bb8abdd..e6ff821a8ad 100644 --- a/src/tools/clippy/tests/ui/into_iter_without_iter.rs +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs @@ -122,3 +122,37 @@ fn main() { } } } + +fn issue11635() { + // A little more involved than the original repro in the issue, but this tests that it correctly + // works for more than one deref step + + use std::ops::Deref; + + pub struct Thing(Vec<u8>); + pub struct Thing2(Thing); + + impl Deref for Thing { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl Deref for Thing2 { + type Target = Thing; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl<'a> IntoIterator for &'a Thing2 { + type Item = &'a u8; + type IntoIter = <&'a [u8] as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } + } +} diff --git a/src/tools/clippy/tests/ui/manual_filter_map.fixed b/src/tools/clippy/tests/ui/manual_filter_map.fixed index 4de45e39b10..a44c46c145c 100644 --- a/src/tools/clippy/tests/ui/manual_filter_map.fixed +++ b/src/tools/clippy/tests/ui/manual_filter_map.fixed @@ -2,6 +2,7 @@ #![warn(clippy::manual_filter_map)] #![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure #![allow(clippy::useless_vec)] +#![allow(clippy::struct_field_names)] fn main() { // is_some(), unwrap() diff --git a/src/tools/clippy/tests/ui/manual_filter_map.rs b/src/tools/clippy/tests/ui/manual_filter_map.rs index 22f316f90b6..e72d0c4305b 100644 --- a/src/tools/clippy/tests/ui/manual_filter_map.rs +++ b/src/tools/clippy/tests/ui/manual_filter_map.rs @@ -2,6 +2,7 @@ #![warn(clippy::manual_filter_map)] #![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure #![allow(clippy::useless_vec)] +#![allow(clippy::struct_field_names)] fn main() { // is_some(), unwrap() diff --git a/src/tools/clippy/tests/ui/manual_filter_map.stderr b/src/tools/clippy/tests/ui/manual_filter_map.stderr index 0bfc1f5c745..cf64bb25951 100644 --- a/src/tools/clippy/tests/ui/manual_filter_map.stderr +++ b/src/tools/clippy/tests/ui/manual_filter_map.stderr @@ -1,11 +1,11 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:8:19 + --> $DIR/manual_filter_map.rs:9:19 | LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:8:30 + --> $DIR/manual_filter_map.rs:9:30 | LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); | ^^^^^^^^^^ @@ -13,31 +13,31 @@ LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap = help: to override `-D warnings` add `#[allow(clippy::manual_filter_map)]` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:11:19 + --> $DIR/manual_filter_map.rs:12:19 | LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:11:31 + --> $DIR/manual_filter_map.rs:12:31 | LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); | ^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:14:19 + --> $DIR/manual_filter_map.rs:15:19 | LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:14:31 + --> $DIR/manual_filter_map.rs:15:31 | LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:17:10 + --> $DIR/manual_filter_map.rs:18:10 | LL | .filter(|&x| to_ref(to_opt(x)).is_some()) | __________^ @@ -45,13 +45,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:17:22 + --> $DIR/manual_filter_map.rs:18:22 | LL | .filter(|&x| to_ref(to_opt(x)).is_some()) | ^^^^^^^^^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:20:10 + --> $DIR/manual_filter_map.rs:21:10 | LL | .filter(|x| to_ref(to_opt(*x)).is_some()) | __________^ @@ -59,13 +59,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:20:21 + --> $DIR/manual_filter_map.rs:21:21 | LL | .filter(|x| to_ref(to_opt(*x)).is_some()) | ^^^^^^^^^^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:24:10 + --> $DIR/manual_filter_map.rs:25:10 | LL | .filter(|&x| to_ref(to_res(x)).is_ok()) | __________^ @@ -73,13 +73,13 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:24:22 + --> $DIR/manual_filter_map.rs:25:22 | LL | .filter(|&x| to_ref(to_res(x)).is_ok()) | ^^^^^^^^^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:27:10 + --> $DIR/manual_filter_map.rs:28:10 | LL | .filter(|x| to_ref(to_res(*x)).is_ok()) | __________^ @@ -87,13 +87,13 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:27:21 + --> $DIR/manual_filter_map.rs:28:21 | LL | .filter(|x| to_ref(to_res(*x)).is_ok()) | ^^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:33:27 + --> $DIR/manual_filter_map.rs:34:27 | LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())` @@ -102,79 +102,79 @@ LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap() = help: to override `-D warnings` add `#[allow(clippy::manual_find_map)]` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:34:28 + --> $DIR/manual_filter_map.rs:35:28 | LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:35:31 + --> $DIR/manual_filter_map.rs:36:31 | LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:36:31 + --> $DIR/manual_filter_map.rs:37:31 | LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:36:41 + --> $DIR/manual_filter_map.rs:37:41 | LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:38:30 + --> $DIR/manual_filter_map.rs:39:30 | LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:39:31 + --> $DIR/manual_filter_map.rs:40:31 | LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:40:32 + --> $DIR/manual_filter_map.rs:41:32 | LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:41:31 + --> $DIR/manual_filter_map.rs:42:31 | LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:42:32 + --> $DIR/manual_filter_map.rs:43:32 | LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:43:35 + --> $DIR/manual_filter_map.rs:44:35 | LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_filter_map.rs:44:35 + --> $DIR/manual_filter_map.rs:45:35 | LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_filter_map.rs:44:45 + --> $DIR/manual_filter_map.rs:45:45 | LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^ error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:92:10 + --> $DIR/manual_filter_map.rs:93:10 | LL | .filter(|f| f.option_field.is_some()) | __________^ @@ -182,7 +182,7 @@ LL | | .map(|f| f.option_field.clone().unwrap()); | |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:97:10 + --> $DIR/manual_filter_map.rs:98:10 | LL | .filter(|f| f.ref_field.is_some()) | __________^ @@ -190,7 +190,7 @@ LL | | .map(|f| f.ref_field.cloned().unwrap()); | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:102:10 + --> $DIR/manual_filter_map.rs:103:10 | LL | .filter(|f| f.ref_field.is_some()) | __________^ @@ -198,7 +198,7 @@ LL | | .map(|f| f.ref_field.copied().unwrap()); | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:107:10 + --> $DIR/manual_filter_map.rs:108:10 | LL | .filter(|f| f.result_field.is_ok()) | __________^ @@ -206,7 +206,7 @@ LL | | .map(|f| f.result_field.clone().unwrap()); | |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:112:10 + --> $DIR/manual_filter_map.rs:113:10 | LL | .filter(|f| f.result_field.is_ok()) | __________^ @@ -214,7 +214,7 @@ LL | | .map(|f| f.result_field.as_ref().unwrap()); | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:117:10 + --> $DIR/manual_filter_map.rs:118:10 | LL | .filter(|f| f.result_field.is_ok()) | __________^ @@ -222,7 +222,7 @@ LL | | .map(|f| f.result_field.as_deref().unwrap()); | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:122:10 + --> $DIR/manual_filter_map.rs:123:10 | LL | .filter(|f| f.result_field.is_ok()) | __________^ @@ -230,7 +230,7 @@ LL | | .map(|f| f.result_field.as_mut().unwrap()); | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:127:10 + --> $DIR/manual_filter_map.rs:128:10 | LL | .filter(|f| f.result_field.is_ok()) | __________^ @@ -238,7 +238,7 @@ LL | | .map(|f| f.result_field.as_deref_mut().unwrap()); | |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:132:10 + --> $DIR/manual_filter_map.rs:133:10 | LL | .filter(|f| f.result_field.is_ok()) | __________^ @@ -246,7 +246,7 @@ LL | | .map(|f| f.result_field.to_owned().unwrap()); | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:145:27 + --> $DIR/manual_filter_map.rs:146:27 | LL | let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x { | ___________________________^ @@ -256,7 +256,7 @@ LL | | }); | |______^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:155:10 + --> $DIR/manual_filter_map.rs:156:10 | LL | .filter(|x| matches!(x, Enum::A(_))) | __________^ diff --git a/src/tools/clippy/tests/ui/manual_find_map.fixed b/src/tools/clippy/tests/ui/manual_find_map.fixed index 0e92d25e68f..2d9a356b9bc 100644 --- a/src/tools/clippy/tests/ui/manual_find_map.fixed +++ b/src/tools/clippy/tests/ui/manual_find_map.fixed @@ -2,6 +2,7 @@ #![warn(clippy::manual_find_map)] #![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure #![allow(clippy::useless_vec)] +#![allow(clippy::struct_field_names)] fn main() { // is_some(), unwrap() diff --git a/src/tools/clippy/tests/ui/manual_find_map.rs b/src/tools/clippy/tests/ui/manual_find_map.rs index b2568c823eb..7c5cc136695 100644 --- a/src/tools/clippy/tests/ui/manual_find_map.rs +++ b/src/tools/clippy/tests/ui/manual_find_map.rs @@ -2,6 +2,7 @@ #![warn(clippy::manual_find_map)] #![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure #![allow(clippy::useless_vec)] +#![allow(clippy::struct_field_names)] fn main() { // is_some(), unwrap() diff --git a/src/tools/clippy/tests/ui/manual_find_map.stderr b/src/tools/clippy/tests/ui/manual_find_map.stderr index 0dc9ae1df03..0526382323d 100644 --- a/src/tools/clippy/tests/ui/manual_find_map.stderr +++ b/src/tools/clippy/tests/ui/manual_find_map.stderr @@ -1,11 +1,11 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:8:19 + --> $DIR/manual_find_map.rs:9:19 | LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:8:28 + --> $DIR/manual_find_map.rs:9:28 | LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); | ^^^^^^^^^^ @@ -13,31 +13,31 @@ LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap() = help: to override `-D warnings` add `#[allow(clippy::manual_find_map)]` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:11:19 + --> $DIR/manual_find_map.rs:12:19 | LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:11:29 + --> $DIR/manual_find_map.rs:12:29 | LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:14:19 + --> $DIR/manual_find_map.rs:15:19 | LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:14:29 + --> $DIR/manual_find_map.rs:15:29 | LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:17:10 + --> $DIR/manual_find_map.rs:18:10 | LL | .find(|&x| to_ref(to_opt(x)).is_some()) | __________^ @@ -45,13 +45,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:17:20 + --> $DIR/manual_find_map.rs:18:20 | LL | .find(|&x| to_ref(to_opt(x)).is_some()) | ^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:20:10 + --> $DIR/manual_find_map.rs:21:10 | LL | .find(|x| to_ref(to_opt(*x)).is_some()) | __________^ @@ -59,13 +59,13 @@ LL | | .map(|y| to_ref(to_opt(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:20:19 + --> $DIR/manual_find_map.rs:21:19 | LL | .find(|x| to_ref(to_opt(*x)).is_some()) | ^^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:24:10 + --> $DIR/manual_find_map.rs:25:10 | LL | .find(|&x| to_ref(to_res(x)).is_ok()) | __________^ @@ -73,13 +73,13 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:24:20 + --> $DIR/manual_find_map.rs:25:20 | LL | .find(|&x| to_ref(to_res(x)).is_ok()) | ^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:27:10 + --> $DIR/manual_find_map.rs:28:10 | LL | .find(|x| to_ref(to_res(*x)).is_ok()) | __________^ @@ -87,109 +87,109 @@ LL | | .map(|y| to_ref(to_res(y)).unwrap()); | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:27:19 + --> $DIR/manual_find_map.rs:28:19 | LL | .find(|x| to_ref(to_res(*x)).is_ok()) | ^^^^^^^^^^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:33:26 + --> $DIR/manual_find_map.rs:34:26 | LL | iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x)` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:34:27 + --> $DIR/manual_find_map.rs:35:27 | LL | iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| *x)` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:35:28 + --> $DIR/manual_find_map.rs:36:28 | LL | iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| **x)` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:36:27 + --> $DIR/manual_find_map.rs:37:27 | LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:37:28 + --> $DIR/manual_find_map.rs:38:28 | LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:38:31 + --> $DIR/manual_find_map.rs:39:31 | LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:39:31 + --> $DIR/manual_find_map.rs:40:31 | LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:39:41 + --> $DIR/manual_find_map.rs:40:41 | LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:41:30 + --> $DIR/manual_find_map.rs:42:30 | LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:42:31 + --> $DIR/manual_find_map.rs:43:31 | LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:43:32 + --> $DIR/manual_find_map.rs:44:32 | LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:44:31 + --> $DIR/manual_find_map.rs:45:31 | LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:45:32 + --> $DIR/manual_find_map.rs:46:32 | LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:46:35 + --> $DIR/manual_find_map.rs:47:35 | LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:47:35 + --> $DIR/manual_find_map.rs:48:35 | LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())` | note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once - --> $DIR/manual_find_map.rs:47:45 + --> $DIR/manual_find_map.rs:48:45 | LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap()); | ^^^^^^^^^ error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:95:10 + --> $DIR/manual_find_map.rs:96:10 | LL | .find(|f| f.option_field.is_some()) | __________^ @@ -197,7 +197,7 @@ LL | | .map(|f| f.option_field.clone().unwrap()); | |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:100:10 + --> $DIR/manual_find_map.rs:101:10 | LL | .find(|f| f.ref_field.is_some()) | __________^ @@ -205,7 +205,7 @@ LL | | .map(|f| f.ref_field.cloned().unwrap()); | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:105:10 + --> $DIR/manual_find_map.rs:106:10 | LL | .find(|f| f.ref_field.is_some()) | __________^ @@ -213,7 +213,7 @@ LL | | .map(|f| f.ref_field.copied().unwrap()); | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:110:10 + --> $DIR/manual_find_map.rs:111:10 | LL | .find(|f| f.result_field.is_ok()) | __________^ @@ -221,7 +221,7 @@ LL | | .map(|f| f.result_field.clone().unwrap()); | |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:115:10 + --> $DIR/manual_find_map.rs:116:10 | LL | .find(|f| f.result_field.is_ok()) | __________^ @@ -229,7 +229,7 @@ LL | | .map(|f| f.result_field.as_ref().unwrap()); | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:120:10 + --> $DIR/manual_find_map.rs:121:10 | LL | .find(|f| f.result_field.is_ok()) | __________^ @@ -237,7 +237,7 @@ LL | | .map(|f| f.result_field.as_deref().unwrap()); | |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:125:10 + --> $DIR/manual_find_map.rs:126:10 | LL | .find(|f| f.result_field.is_ok()) | __________^ @@ -245,7 +245,7 @@ LL | | .map(|f| f.result_field.as_mut().unwrap()); | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:130:10 + --> $DIR/manual_find_map.rs:131:10 | LL | .find(|f| f.result_field.is_ok()) | __________^ @@ -253,7 +253,7 @@ LL | | .map(|f| f.result_field.as_deref_mut().unwrap()); | |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:135:10 + --> $DIR/manual_find_map.rs:136:10 | LL | .find(|f| f.result_field.is_ok()) | __________^ diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed index 5be2dd280b8..9c4bd335ad8 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed @@ -33,6 +33,7 @@ fn msrv_1_23() { assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); } #[clippy::msrv = "1.24"] @@ -40,14 +41,17 @@ fn msrv_1_24() { assert!(b'1'.is_ascii_digit()); assert!('X'.is_ascii_uppercase()); assert!('x'.is_ascii_alphabetic()); + assert!('x'.is_ascii_hexdigit()); } #[clippy::msrv = "1.46"] fn msrv_1_46() { const FOO: bool = matches!('x', '0'..='9'); + const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } #[clippy::msrv = "1.47"] fn msrv_1_47() { const FOO: bool = 'x'.is_ascii_digit(); + const BAR: bool = 'x'.is_ascii_hexdigit(); } diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs index f9249e22a02..785943cd24d 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs @@ -33,6 +33,7 @@ fn msrv_1_23() { assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); } #[clippy::msrv = "1.24"] @@ -40,14 +41,17 @@ fn msrv_1_24() { assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); } #[clippy::msrv = "1.46"] fn msrv_1_46() { const FOO: bool = matches!('x', '0'..='9'); + const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } #[clippy::msrv = "1.47"] fn msrv_1_47() { const FOO: bool = matches!('x', '0'..='9'); + const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr index e0fb46e59fa..f69522c5ff8 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr @@ -98,28 +98,40 @@ LL | ('A'..='Z').contains(cool_letter); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:40:13 + --> $DIR/manual_is_ascii_check.rs:41:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:41:13 + --> $DIR/manual_is_ascii_check.rs:42:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:42:13 + --> $DIR/manual_is_ascii_check.rs:43:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:52:23 + --> $DIR/manual_is_ascii_check.rs:44:13 + | +LL | assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:55:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` -error: aborting due to 20 previous errors +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:56:23 + | +LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index cc40b162058..e756d9b5935 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -17,6 +17,9 @@ fn main() { }); let _: Result<u32, u32> = Ok(1); let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42); + // : u32 guides type inference + let _ = Ok(1).map_err(|a: u32| a); + let _ = Ok(1).map_err(std::convert::identity::<u32>); } fn not_identity(x: &u16) -> u16 { diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index 97a91aea6dc..74cbaade405 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -19,6 +19,9 @@ fn main() { }); let _: Result<u32, u32> = Ok(1).map_err(|a| a); let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42); + // : u32 guides type inference + let _ = Ok(1).map_err(|a: u32| a); + let _ = Ok(1).map_err(std::convert::identity::<u32>); } fn not_identity(x: &u16) -> u16 { diff --git a/src/tools/clippy/tests/ui/min_ident_chars.rs b/src/tools/clippy/tests/ui/min_ident_chars.rs index 030863ca0d3..f99c35d5c57 100644 --- a/src/tools/clippy/tests/ui/min_ident_chars.rs +++ b/src/tools/clippy/tests/ui/min_ident_chars.rs @@ -1,5 +1,6 @@ //@aux-build:proc_macros.rs #![allow(irrefutable_let_patterns, nonstandard_style, unused)] +#![allow(clippy::struct_field_names)] #![warn(clippy::min_ident_chars)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/min_ident_chars.stderr b/src/tools/clippy/tests/ui/min_ident_chars.stderr index 253636cf91d..e4181157ea2 100644 --- a/src/tools/clippy/tests/ui/min_ident_chars.stderr +++ b/src/tools/clippy/tests/ui/min_ident_chars.stderr @@ -1,5 +1,5 @@ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:8:8 + --> $DIR/min_ident_chars.rs:9:8 | LL | struct A { | ^ @@ -8,169 +8,169 @@ LL | struct A { = help: to override `-D warnings` add `#[allow(clippy::min_ident_chars)]` error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:9:5 + --> $DIR/min_ident_chars.rs:10:5 | LL | a: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:11:5 + --> $DIR/min_ident_chars.rs:12:5 | LL | A: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:12:5 + --> $DIR/min_ident_chars.rs:13:5 | LL | I: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:15:8 + --> $DIR/min_ident_chars.rs:16:8 | LL | struct B(u32); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:17:8 + --> $DIR/min_ident_chars.rs:18:8 | LL | struct O { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:18:5 + --> $DIR/min_ident_chars.rs:19:5 | LL | o: u32, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:23:6 + --> $DIR/min_ident_chars.rs:24:6 | LL | enum C { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:24:5 + --> $DIR/min_ident_chars.rs:25:5 | LL | D, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:25:5 + --> $DIR/min_ident_chars.rs:26:5 | LL | E, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:26:5 + --> $DIR/min_ident_chars.rs:27:5 | LL | F, | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:50:9 + --> $DIR/min_ident_chars.rs:51:9 | LL | let h = 1; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:51:9 + --> $DIR/min_ident_chars.rs:52:9 | LL | let e = 2; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:52:9 + --> $DIR/min_ident_chars.rs:53:9 | LL | let l = 3; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:53:9 + --> $DIR/min_ident_chars.rs:54:9 | LL | let l = 4; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:54:9 + --> $DIR/min_ident_chars.rs:55:9 | LL | let o = 6; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:58:10 + --> $DIR/min_ident_chars.rs:59:10 | LL | let (h, o, w) = (1, 2, 3); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:58:13 + --> $DIR/min_ident_chars.rs:59:13 | LL | let (h, o, w) = (1, 2, 3); | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:59:10 + --> $DIR/min_ident_chars.rs:60:10 | LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:59:14 + --> $DIR/min_ident_chars.rs:60:14 | LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:59:17 + --> $DIR/min_ident_chars.rs:60:17 | LL | for (a, (r, e)) in (0..1000).enumerate().enumerate() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:61:16 + --> $DIR/min_ident_chars.rs:62:16 | LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:61:19 + --> $DIR/min_ident_chars.rs:62:19 | LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:61:29 + --> $DIR/min_ident_chars.rs:62:29 | LL | while let (d, o, _i, n, g) = (true, true, false, false, true) {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:65:9 + --> $DIR/min_ident_chars.rs:66:9 | LL | let o = 1; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:66:9 + --> $DIR/min_ident_chars.rs:67:9 | LL | let o = O { o }; | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:80:4 + --> $DIR/min_ident_chars.rs:81:4 | LL | fn b() {} | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:81:21 + --> $DIR/min_ident_chars.rs:82:21 | LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ error: this ident consists of a single char - --> $DIR/min_ident_chars.rs:81:29 + --> $DIR/min_ident_chars.rs:82:29 | LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed index 2a7a2067ee0..70af604b214 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] struct A { diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs index 56ddc46c4d4..23c3e7bc5cf 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters.rs @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] struct A { diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr index aadec654908..120a3f3112e 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:11:5 + --> $DIR/misnamed_getters.rs:12:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:16:5 + --> $DIR/misnamed_getters.rs:17:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -23,7 +23,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:21:5 + --> $DIR/misnamed_getters.rs:22:5 | LL | / fn b(self) -> u8 { LL | | @@ -33,7 +33,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:26:5 + --> $DIR/misnamed_getters.rs:27:5 | LL | / fn b_mut(&mut self) -> &mut u8 { LL | | @@ -43,7 +43,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:31:5 + --> $DIR/misnamed_getters.rs:32:5 | LL | / fn c(&self) -> &u8 { LL | | @@ -53,7 +53,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:36:5 + --> $DIR/misnamed_getters.rs:37:5 | LL | / fn c_mut(&mut self) -> &mut u8 { LL | | @@ -63,7 +63,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:48:5 + --> $DIR/misnamed_getters.rs:49:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | @@ -73,7 +73,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:52:5 + --> $DIR/misnamed_getters.rs:53:5 | LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | @@ -83,7 +83,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:57:5 + --> $DIR/misnamed_getters.rs:58:5 | LL | / unsafe fn b(self) -> u8 { LL | | @@ -93,7 +93,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:62:5 + --> $DIR/misnamed_getters.rs:63:5 | LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | @@ -103,7 +103,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:75:5 + --> $DIR/misnamed_getters.rs:76:5 | LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:79:5 + --> $DIR/misnamed_getters.rs:80:5 | LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -123,7 +123,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:84:5 + --> $DIR/misnamed_getters.rs:85:5 | LL | / unsafe fn b_unchecked(self) -> u8 { LL | | @@ -133,7 +133,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:89:5 + --> $DIR/misnamed_getters.rs:90:5 | LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -143,7 +143,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:122:5 + --> $DIR/misnamed_getters.rs:123:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -153,7 +153,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:126:5 + --> $DIR/misnamed_getters.rs:127:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -163,7 +163,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:131:5 + --> $DIR/misnamed_getters.rs:132:5 | LL | / fn d(&self) -> &u8 { LL | | @@ -173,7 +173,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:135:5 + --> $DIR/misnamed_getters.rs:136:5 | LL | / fn d_mut(&mut self) -> &mut u8 { LL | | 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 4ef6f0ca92f..8afb4df20af 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 @@ -147,4 +147,11 @@ fn _field_fn_ptr(x: unsafe fn()) { } } +// await expands to an unsafe block with several operations, but this is fine.: #11312 +async fn await_desugaring_silent() { + async fn helper() {} + + helper().await; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index c9ea831f8b2..3059de8f89c 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -7,7 +7,8 @@ clippy::equatable_if_let, clippy::needless_if, clippy::needless_return, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::struct_field_names )] use std::cell::Cell; diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index b83d9c3f209..b2cbe86e223 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -7,7 +7,8 @@ clippy::equatable_if_let, clippy::needless_if, clippy::needless_return, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::struct_field_names )] use std::cell::Cell; diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index 2b189c89851..72b0670c95b 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:40:5 + --> $DIR/fixable.rs:41:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:45:5 + --> $DIR/fixable.rs:46:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:50:5 + --> $DIR/fixable.rs:51:5 | LL | / if x && y { LL | | false @@ -32,7 +32,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:58:5 + --> $DIR/fixable.rs:59:5 | LL | / if a == b { LL | | false @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a != b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:63:5 + --> $DIR/fixable.rs:64:5 | LL | / if a != b { LL | | false @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a == b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:68:5 + --> $DIR/fixable.rs:69:5 | LL | / if a < b { LL | | false @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a >= b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:73:5 + --> $DIR/fixable.rs:74:5 | LL | / if a <= b { LL | | false @@ -72,7 +72,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a > b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:78:5 + --> $DIR/fixable.rs:79:5 | LL | / if a > b { LL | | false @@ -82,7 +82,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a <= b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:83:5 + --> $DIR/fixable.rs:84:5 | LL | / if a >= b { LL | | false @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:111:5 + --> $DIR/fixable.rs:112:5 | LL | / if x { LL | | return true; @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:119:5 + --> $DIR/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:127:5 + --> $DIR/fixable.rs:128:5 | LL | / if x && y { LL | | return true; @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:135:5 + --> $DIR/fixable.rs:136:5 | LL | / if x && y { LL | | return false; @@ -132,7 +132,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:143:8 + --> $DIR/fixable.rs:144:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -141,25 +141,25 @@ LL | if x == true {}; = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:147:8 + --> $DIR/fixable.rs:148:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:157:8 + --> $DIR/fixable.rs:158:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:158:8 + --> $DIR/fixable.rs:159:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:167:12 + --> $DIR/fixable.rs:168:12 | LL | } else if returns_bool() { | ____________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:180:5 + --> $DIR/fixable.rs:181:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -180,13 +180,13 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:185:30 + --> $DIR/fixable.rs:186:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:188:9 + --> $DIR/fixable.rs:189:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs index 39d76f99900..ea5e74c4c00 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs @@ -270,6 +270,38 @@ pub async fn closure4(n: &mut usize) { })(); } +// Should not warn. +async fn _f(v: &mut Vec<()>) { + let x = || v.pop(); + _ = || || x; +} + +struct Data<T: ?Sized> { + value: T, +} +// Unsafe functions should not warn. +unsafe fn get_mut_unchecked<T>(ptr: &mut NonNull<Data<T>>) -> &mut T { + &mut (*ptr.as_ptr()).value +} +// Unsafe blocks should not warn. +fn get_mut_unchecked2<T>(ptr: &mut NonNull<Data<T>>) -> &mut T { + unsafe { &mut (*ptr.as_ptr()).value } +} + +fn set_true(b: &mut bool) { + *b = true; +} + +// Should not warn. +fn true_setter(b: &mut bool) -> impl FnOnce() + '_ { + move || set_true(b) +} + +// Should not warn. +fn filter_copy<T: Copy>(predicate: &mut impl FnMut(T) -> bool) -> impl FnMut(&T) -> bool + '_ { + move |&item| predicate(item) +} + fn main() { let mut u = 0; let mut v = vec![0]; diff --git a/src/tools/clippy/tests/ui/redundant_locals.rs b/src/tools/clippy/tests/ui/redundant_locals.rs index e81db300f15..182d067a5e9 100644 --- a/src/tools/clippy/tests/ui/redundant_locals.rs +++ b/src/tools/clippy/tests/ui/redundant_locals.rs @@ -117,6 +117,14 @@ fn macros() { let x = 1; let x = x; } + + let x = 10; + macro_rules! rebind_outer_macro { + ($x:ident) => { + let x = x; + }; + } + rebind_outer_macro!(y); } struct WithDrop(usize); diff --git a/src/tools/clippy/tests/ui/redundant_locals.stderr b/src/tools/clippy/tests/ui/redundant_locals.stderr index d794a87fe7d..30ab4aa2ea9 100644 --- a/src/tools/clippy/tests/ui/redundant_locals.stderr +++ b/src/tools/clippy/tests/ui/redundant_locals.stderr @@ -157,13 +157,13 @@ LL | let x = 1; | ^ error: redundant redefinition of a binding `a` - --> $DIR/redundant_locals.rs:144:5 + --> $DIR/redundant_locals.rs:152:5 | LL | let a = a; | ^^^^^^^^^^ | help: `a` is initially defined here - --> $DIR/redundant_locals.rs:142:9 + --> $DIR/redundant_locals.rs:150:9 | LL | let a = WithoutDrop(1); | ^ diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs index e25609f7560..51fe346d092 100644 --- a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -1,4 +1,5 @@ #![warn(clippy::rest_pat_in_fully_bound_structs)] +#![allow(clippy::struct_field_names)] struct A { a: i32, diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr index 2c221b4dbb2..a62f1d0b65f 100644 --- a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `..` pattern in struct binding. All fields were already bound - --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9 + --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9 | LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint = help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]` error: unnecessary use of `..` pattern in struct binding. All fields were already bound - --> $DIR/rest_pat_in_fully_bound_structs.rs:24:9 + --> $DIR/rest_pat_in_fully_bound_structs.rs:25:9 | LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint = help: consider removing `..` from this binding error: unnecessary use of `..` pattern in struct binding. All fields were already bound - --> $DIR/rest_pat_in_fully_bound_structs.rs:31:9 + --> $DIR/rest_pat_in_fully_bound_structs.rs:32:9 | LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/struct_fields.rs b/src/tools/clippy/tests/ui/struct_fields.rs new file mode 100644 index 00000000000..8b1a1446e3c --- /dev/null +++ b/src/tools/clippy/tests/ui/struct_fields.rs @@ -0,0 +1,331 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::struct_field_names)] +#![allow(unused)] + +#[macro_use] +extern crate proc_macros; + +struct Data1 { + field_data1: u8, + //~^ ERROR: field name ends with the struct's name + another: u8, + foo: u8, + bar: u8, +} + +struct Data2 { + another: u8, + foo: u8, + data2_field: u8, + //~^ ERROR: field name starts with the struct's name + bar: u8, +} + +struct StructData { + //~^ ERROR: all fields have the same postfix: `data` + movable_data: u8, + fixed_data: u8, + invisible_data: u8, +} + +struct DataStruct { + //~^ ERROR: all fields have the same prefix: `data` + data_movable: u8, + data_fixed: u8, + data_invisible: u8, +} + +struct DoublePrefix { + //~^ ERROR: all fields have the same prefix: `some_data` + some_data_a: bool, + some_data_b: bool, + some_data_c: bool, +} + +struct DoublePostfix { + //~^ ERROR: all fields have the same postfix: `some_data` + a_some_data: bool, + b_some_data: bool, + c_some_data: bool, +} + +#[allow(non_snake_case)] +struct NotSnakeCase { + //~^ ERROR: all fields have the same postfix: `someData` + a_someData: bool, + b_someData: bool, + c_someData: bool, +} +#[allow(non_snake_case)] +struct NotSnakeCase2 { + //~^ ERROR: all fields have the same prefix: `someData` + someData_c: bool, + someData_b: bool, + someData_a_b: bool, +} + +// no error, threshold is 3 fiels by default +struct Fooo { + foo: u8, + bar: u8, +} + +struct NonCaps { + //~^ ERROR: all fields have the same prefix: `prefix` + prefix_的: u8, + prefix_tea: u8, + prefix_cake: u8, +} + +// should not lint +#[allow(clippy::struct_field_names)] +pub mod allowed { + pub struct PubAllowed { + some_this: u8, + some_that: u8, + some_other_what: u8, + } +} + +// should not lint +struct SomeData { + foo: u8, + bar: bool, + path: u8, + answer: u8, +} + +// should not lint +pub struct NetworkLayer { + layer1: Vec<u8>, + layer2: Vec<u8>, + layer3: Vec<u8>, + layer4: Vec<u8>, +} + +//should not lint +struct North { + normal: u8, + no_left: u8, + no_right: u8, +} + +mod issue8324_from_enum_variant_names { + // 8324: enum_variant_names warns even if removing the suffix would leave an empty string + struct Phase { + pre_lookup: u8, + lookup: u8, + post_lookup: u8, + } +} + +mod issue9018_from_enum_variant_names { + struct DoLint { + //~^ ERROR: all fields have the same prefix: `_type` + _type_create: u8, + _type_read: u8, + _type_update: u8, + _type_destroy: u8, + } + + struct DoLint2 { + //~^ ERROR: all fields have the same prefix: `__type` + __type_create: u8, + __type_read: u8, + __type_update: u8, + __type_destroy: u8, + } + + struct DoLint3 { + //~^ ERROR: all fields have the same prefix: `___type` + ___type_create: u8, + ___type_read: u8, + ___type_update: u8, + ___type_destroy: u8, + } + + struct DoLint4 { + //~^ ERROR: all fields have the same postfix: `_` + create_: u8, + read_: u8, + update_: u8, + destroy_: u8, + } + + struct DoLint5 { + //~^ ERROR: all fields have the same postfix: `__` + create__: u8, + read__: u8, + update__: u8, + destroy__: u8, + } + + struct DoLint6 { + //~^ ERROR: all fields have the same postfix: `___` + create___: u8, + read___: u8, + update___: u8, + destroy___: u8, + } + + struct DoLintToo { + //~^ ERROR: all fields have the same postfix: `type` + _create_type: u8, + _update_type: u8, + _delete_type: u8, + } + + struct DoNotLint { + _foo: u8, + _bar: u8, + _baz: u8, + } + + struct DoNotLint2 { + __foo: u8, + __bar: u8, + __baz: u8, + } +} + +mod allow_attributes_on_fields { + struct Struct { + #[allow(clippy::struct_field_names)] + struct_starts_with: u8, + #[allow(clippy::struct_field_names)] + ends_with_struct: u8, + foo: u8, + } +} + +// food field should not lint +struct Foo { + food: i32, + a: i32, + b: i32, +} + +struct Proxy { + proxy: i32, + //~^ ERROR: field name starts with the struct's name + unrelated1: bool, + unrelated2: bool, +} + +// should not lint +pub struct RegexT { + __buffer: i32, + __allocated: i32, + __used: i32, +} + +mod macro_tests { + macro_rules! mk_struct { + () => { + struct MacroStruct { + some_a: i32, + some_b: i32, + some_c: i32, + } + }; + } + mk_struct!(); + //~^ ERROR: all fields have the same prefix: `some` + + macro_rules! mk_struct2 { + () => { + struct Macrobaz { + macrobaz_a: i32, + some_b: i32, + some_c: i32, + } + }; + } + mk_struct2!(); + //~^ ERROR: field name starts with the struct's name + + macro_rules! mk_struct_with_names { + ($struct_name:ident, $field:ident) => { + struct $struct_name { + $field: i32, + other_something: i32, + other_field: i32, + } + }; + } + // expands to `struct Foo { foo: i32, ... }` + mk_struct_with_names!(Foo, foo); + //~^ ERROR: field name starts with the struct's name + + // expands to a struct with all fields starting with `other` but should not + // be linted because some fields come from the macro definition and the other from the input + mk_struct_with_names!(Some, other_data); + + // should not lint when names come from different places + macro_rules! mk_struct_with_field_name { + ($field_name:ident) => { + struct Baz { + one: i32, + two: i32, + $field_name: i32, + } + }; + } + mk_struct_with_field_name!(baz_three); + + // should not lint when names come from different places + macro_rules! mk_struct_with_field_name { + ($field_name:ident) => { + struct Bazilisk { + baz_one: i32, + baz_two: i32, + $field_name: i32, + } + }; + } + mk_struct_with_field_name!(baz_three); + + macro_rules! mk_struct_full_def { + ($struct_name:ident, $field1:ident, $field2:ident, $field3:ident) => { + struct $struct_name { + $field1: i32, + $field2: i32, + $field3: i32, + } + }; + } + mk_struct_full_def!(PrefixData, some_data, some_meta, some_other); + //~^ ERROR: all fields have the same prefix: `some` +} + +// should not lint on external code +external! { + struct DataExternal { + field_data1: u8, + another: u8, + foo: u8, + bar: u8, + } + + struct NotSnakeCaseExternal { + someData_c: bool, + someData_b: bool, + someData_a_b: bool, + } + + struct DoublePrefixExternal { + some_data_a: bool, + some_data_b: bool, + some_data_c: bool, + } + + struct StructDataExternal { + movable_data: u8, + fixed_data: u8, + invisible_data: u8, + } + +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/struct_fields.stderr b/src/tools/clippy/tests/ui/struct_fields.stderr new file mode 100644 index 00000000000..4ca57715b18 --- /dev/null +++ b/src/tools/clippy/tests/ui/struct_fields.stderr @@ -0,0 +1,265 @@ +error: field name ends with the struct's name + --> $DIR/struct_fields.rs:10:5 + | +LL | field_data1: u8, + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::struct-field-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::struct_field_names)]` + +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:20:5 + | +LL | data2_field: u8, + | ^^^^^^^^^^^^^^^ + +error: all fields have the same postfix: `data` + --> $DIR/struct_fields.rs:25:1 + | +LL | / struct StructData { +LL | | +LL | | movable_data: u8, +LL | | fixed_data: u8, +LL | | invisible_data: u8, +LL | | } + | |_^ + | + = help: remove the postfixes + +error: all fields have the same prefix: `data` + --> $DIR/struct_fields.rs:32:1 + | +LL | / struct DataStruct { +LL | | +LL | | data_movable: u8, +LL | | data_fixed: u8, +LL | | data_invisible: u8, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: all fields have the same prefix: `some_data` + --> $DIR/struct_fields.rs:39:1 + | +LL | / struct DoublePrefix { +LL | | +LL | | some_data_a: bool, +LL | | some_data_b: bool, +LL | | some_data_c: bool, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: all fields have the same postfix: `some_data` + --> $DIR/struct_fields.rs:46:1 + | +LL | / struct DoublePostfix { +LL | | +LL | | a_some_data: bool, +LL | | b_some_data: bool, +LL | | c_some_data: bool, +LL | | } + | |_^ + | + = help: remove the postfixes + +error: all fields have the same postfix: `someData` + --> $DIR/struct_fields.rs:54:1 + | +LL | / struct NotSnakeCase { +LL | | +LL | | a_someData: bool, +LL | | b_someData: bool, +LL | | c_someData: bool, +LL | | } + | |_^ + | + = help: remove the postfixes + +error: all fields have the same prefix: `someData` + --> $DIR/struct_fields.rs:61:1 + | +LL | / struct NotSnakeCase2 { +LL | | +LL | | someData_c: bool, +LL | | someData_b: bool, +LL | | someData_a_b: bool, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: all fields have the same prefix: `prefix` + --> $DIR/struct_fields.rs:74:1 + | +LL | / struct NonCaps { +LL | | +LL | | prefix_的: u8, +LL | | prefix_tea: u8, +LL | | prefix_cake: u8, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: all fields have the same prefix: `_type` + --> $DIR/struct_fields.rs:124:5 + | +LL | / struct DoLint { +LL | | +LL | | _type_create: u8, +LL | | _type_read: u8, +LL | | _type_update: u8, +LL | | _type_destroy: u8, +LL | | } + | |_____^ + | + = help: remove the prefixes + +error: all fields have the same prefix: `__type` + --> $DIR/struct_fields.rs:132:5 + | +LL | / struct DoLint2 { +LL | | +LL | | __type_create: u8, +LL | | __type_read: u8, +LL | | __type_update: u8, +LL | | __type_destroy: u8, +LL | | } + | |_____^ + | + = help: remove the prefixes + +error: all fields have the same prefix: `___type` + --> $DIR/struct_fields.rs:140:5 + | +LL | / struct DoLint3 { +LL | | +LL | | ___type_create: u8, +LL | | ___type_read: u8, +LL | | ___type_update: u8, +LL | | ___type_destroy: u8, +LL | | } + | |_____^ + | + = help: remove the prefixes + +error: all fields have the same postfix: `_` + --> $DIR/struct_fields.rs:148:5 + | +LL | / struct DoLint4 { +LL | | +LL | | create_: u8, +LL | | read_: u8, +LL | | update_: u8, +LL | | destroy_: u8, +LL | | } + | |_____^ + | + = help: remove the postfixes + +error: all fields have the same postfix: `__` + --> $DIR/struct_fields.rs:156:5 + | +LL | / struct DoLint5 { +LL | | +LL | | create__: u8, +LL | | read__: u8, +LL | | update__: u8, +LL | | destroy__: u8, +LL | | } + | |_____^ + | + = help: remove the postfixes + +error: all fields have the same postfix: `___` + --> $DIR/struct_fields.rs:164:5 + | +LL | / struct DoLint6 { +LL | | +LL | | create___: u8, +LL | | read___: u8, +LL | | update___: u8, +LL | | destroy___: u8, +LL | | } + | |_____^ + | + = help: remove the postfixes + +error: all fields have the same postfix: `type` + --> $DIR/struct_fields.rs:172:5 + | +LL | / struct DoLintToo { +LL | | +LL | | _create_type: u8, +LL | | _update_type: u8, +LL | | _delete_type: u8, +LL | | } + | |_____^ + | + = help: remove the postfixes + +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:210:5 + | +LL | proxy: i32, + | ^^^^^^^^^^ + +error: all fields have the same prefix: `some` + --> $DIR/struct_fields.rs:226:13 + | +LL | / struct MacroStruct { +LL | | some_a: i32, +LL | | some_b: i32, +LL | | some_c: i32, +LL | | } + | |_____________^ +... +LL | mk_struct!(); + | ------------ in this macro invocation + | + = help: remove the prefixes + = note: this error originates in the macro `mk_struct` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:239:17 + | +LL | macrobaz_a: i32, + | ^^^^^^^^^^^^^^^ +... +LL | mk_struct2!(); + | ------------- in this macro invocation + | + = note: this error originates in the macro `mk_struct2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:251:17 + | +LL | $field: i32, + | ^^^^^^^^^^^ +... +LL | mk_struct_with_names!(Foo, foo); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `mk_struct_with_names` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: all fields have the same prefix: `some` + --> $DIR/struct_fields.rs:291:13 + | +LL | / struct $struct_name { +LL | | $field1: i32, +LL | | $field2: i32, +LL | | $field3: i32, +LL | | } + | |_____________^ +... +LL | mk_struct_full_def!(PrefixData, some_data, some_meta, some_other); + | ----------------------------------------------------------------- in this macro invocation + | + = help: remove the prefixes + = note: this error originates in the macro `mk_struct_full_def` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 21 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 304e7b7fd7f..4778eaefdbd 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] #![allow(clippy::needless_borrow)] #![allow(clippy::unnecessary_literal_unwrap)] +#![allow(clippy::unit_arg)] use std::ops::Deref; @@ -76,6 +77,8 @@ fn main() { let _ = opt.ok_or(2); let _ = nested_tuple_opt.unwrap_or(Some((1, 2))); let _ = cond.then_some(astronomers_pi); + let _ = true.then_some({}); + let _ = true.then_some({}); // Should lint - Builtin deref let r = &1; diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index ddfa6bb3ef6..d4b7fd31b1b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] #![allow(clippy::needless_borrow)] #![allow(clippy::unnecessary_literal_unwrap)] +#![allow(clippy::unit_arg)] use std::ops::Deref; @@ -76,6 +77,8 @@ fn main() { let _ = opt.ok_or_else(|| 2); let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = cond.then(|| astronomers_pi); + let _ = true.then(|| -> _ {}); + let _ = true.then(|| {}); // Should lint - Builtin deref let r = &1; diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index 4f1ca374872..1b0db4759bb 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:68:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -10,7 +10,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:69:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -18,7 +18,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:70:13 + --> $DIR/unnecessary_lazy_eval.rs:71:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -26,7 +26,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:72:13 + --> $DIR/unnecessary_lazy_eval.rs:73:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -34,7 +34,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:74:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -42,7 +42,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:74:13 + --> $DIR/unnecessary_lazy_eval.rs:75:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -50,7 +50,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:75:13 + --> $DIR/unnecessary_lazy_eval.rs:76:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -58,7 +58,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:76:13 + --> $DIR/unnecessary_lazy_eval.rs:77:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -66,7 +66,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:77:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -74,15 +74,31 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:78:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- | | | help: use `then_some(..)` instead: `then_some(astronomers_pi)` +error: unnecessary closure used with `bool::then` + --> $DIR/unnecessary_lazy_eval.rs:80:13 + | +LL | let _ = true.then(|| -> _ {}); + | ^^^^^---------------- + | | + | help: use `then_some(..)` instead: `then_some({})` + +error: unnecessary closure used with `bool::then` + --> $DIR/unnecessary_lazy_eval.rs:81:13 + | +LL | let _ = true.then(|| {}); + | ^^^^^----------- + | | + | help: use `then_some(..)` instead: `then_some({})` + error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:82:13 + --> $DIR/unnecessary_lazy_eval.rs:85:13 | LL | let _ = Some(1).unwrap_or_else(|| *r); | ^^^^^^^^--------------------- @@ -90,7 +106,7 @@ LL | let _ = Some(1).unwrap_or_else(|| *r); | help: use `unwrap_or(..)` instead: `unwrap_or(*r)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:84:13 + --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = Some(1).unwrap_or_else(|| *b); | ^^^^^^^^--------------------- @@ -98,7 +114,7 @@ LL | let _ = Some(1).unwrap_or_else(|| *b); | help: use `unwrap_or(..)` instead: `unwrap_or(*b)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:86:13 + --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r); | ^^^^^^^^^^^^^^^^^--------------------- @@ -106,7 +122,7 @@ LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r); | help: use `unwrap_or(..)` instead: `unwrap_or(&r)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:87:13 + --> $DIR/unnecessary_lazy_eval.rs:90:13 | LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b); | ^^^^^^^^^^^^^^^^^--------------------- @@ -114,7 +130,7 @@ LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b); | help: use `unwrap_or(..)` instead: `unwrap_or(&b)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:90:13 + --> $DIR/unnecessary_lazy_eval.rs:93:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -122,7 +138,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:91:13 + --> $DIR/unnecessary_lazy_eval.rs:94:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -130,7 +146,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:92:28 + --> $DIR/unnecessary_lazy_eval.rs:95:28 | LL | let _: Option<usize> = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -138,7 +154,7 @@ LL | let _: Option<usize> = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:93:13 + --> $DIR/unnecessary_lazy_eval.rs:96:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -146,7 +162,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:94:35 + --> $DIR/unnecessary_lazy_eval.rs:97:35 | LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -154,7 +170,7 @@ LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:95:28 + --> $DIR/unnecessary_lazy_eval.rs:98:28 | LL | let _: Option<usize> = None.or_else(|| None); | ^^^^^---------------- @@ -162,7 +178,7 @@ LL | let _: Option<usize> = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:98:13 + --> $DIR/unnecessary_lazy_eval.rs:101:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -170,7 +186,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:99:13 + --> $DIR/unnecessary_lazy_eval.rs:102:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -178,7 +194,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:100:13 + --> $DIR/unnecessary_lazy_eval.rs:103:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -186,7 +202,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:101:13 + --> $DIR/unnecessary_lazy_eval.rs:104:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -194,7 +210,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:102:13 + --> $DIR/unnecessary_lazy_eval.rs:105:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -202,7 +218,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:132:28 + --> $DIR/unnecessary_lazy_eval.rs:135:28 | LL | let _: Option<usize> = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -210,7 +226,7 @@ LL | let _: Option<usize> = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:133:13 + --> $DIR/unnecessary_lazy_eval.rs:136:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -218,7 +234,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:134:13 + --> $DIR/unnecessary_lazy_eval.rs:137:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -226,7 +242,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:140:13 + --> $DIR/unnecessary_lazy_eval.rs:143:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -234,7 +250,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:141:13 + --> $DIR/unnecessary_lazy_eval.rs:144:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -242,7 +258,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:142:13 + --> $DIR/unnecessary_lazy_eval.rs:145:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -250,7 +266,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:164:35 + --> $DIR/unnecessary_lazy_eval.rs:167:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -258,7 +274,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:165:35 + --> $DIR/unnecessary_lazy_eval.rs:168:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -266,7 +282,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:166:35 + --> $DIR/unnecessary_lazy_eval.rs:169:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -274,7 +290,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:168:35 + --> $DIR/unnecessary_lazy_eval.rs:171:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -282,7 +298,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:169:35 + --> $DIR/unnecessary_lazy_eval.rs:172:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -290,7 +306,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:170:35 + --> $DIR/unnecessary_lazy_eval.rs:173:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -298,7 +314,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:171:35 + --> $DIR/unnecessary_lazy_eval.rs:174:35 | LL | let _: Result<usize, usize> = res. | ___________________________________^ @@ -312,5 +328,5 @@ LL | | or_else(|_| Ok(ext_str.some_field)); | | | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` -error: aborting due to 38 previous errors +error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs index 33685bfb738..412d4aaafb4 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -25,3 +25,8 @@ fn main() { let arr = [(Some(1),)]; Some(&0).and_then(|&i| arr[i].0); } + +fn issue11672() { + // Return type annotation helps type inference and removing it can break code + let _ = true.then(|| -> &[u8] { &[] }); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 27fa560d4d7..95b02be91ca 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -25,5 +25,13 @@ LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); | | | help: use `unwrap_or(..)` instead: `unwrap_or(2)` -error: aborting due to 3 previous errors +error: unnecessary closure used with `bool::then` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:31:13 + | +LL | let _ = true.then(|| -> &[u8] { &[] }); + | ^^^^^------------------------- + | | + | help: use `then_some(..)` instead: `then_some({ &[] })` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 6856bb0ab37..419b3c30deb 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -11,6 +11,8 @@ allow-unauthenticated = [ # Have rustbot inform users about the *No Merge Policy* [no-merges] +exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust +labels = ["has-merge-commits", "S-waiting-on-author"] [autolabel."S-waiting-on-review"] new_pr = true diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index bc3882bcf2b..0335ad3c1d8 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -249624b5043013d18c00f0401ca431c1a6baa8cd +9e3f784eb2c7c847b6c3578b373c0e0bc9233ca3 diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 08b26f5fe85..48dafc40c62 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -37,7 +37,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let tcx = this.tcx; - let flags = if let Some(flags_op) = args.get(0) { + let flags = if let Some(flags_op) = args.first() { this.read_scalar(flags_op)?.to_u64()? } else { throw_ub_format!("expected at least 1 argument") diff --git a/tests/coverage-map/fn_sig_into_try.cov-map b/tests/coverage-map/fn_sig_into_try.cov-map new file mode 100644 index 00000000000..4672e7c1ce9 --- /dev/null +++ b/tests/coverage-map/fn_sig_into_try.cov-map @@ -0,0 +1,53 @@ +Function name: fn_sig_into_try::a +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0a, 01, 04, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 10, 1) to (start + 4, 2) + +Function name: fn_sig_into_try::b +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 10, 01, 02, 0f, 00, 02, 0f, 00, 10, 02, 01, 05, 00, 0c, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 16, 1) to (start + 2, 15) +- Code(Zero) at (prev + 2, 15) to (start + 0, 16) +- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: fn_sig_into_try::c +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 16, 01, 02, 17, 00, 02, 17, 00, 18, 02, 01, 05, 00, 0c, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 22, 1) to (start + 2, 23) +- Code(Zero) at (prev + 2, 23) to (start + 0, 24) +- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: fn_sig_into_try::d +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 1c, 01, 03, 0f, 00, 03, 0f, 00, 10, 02, 01, 05, 00, 0c, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 28, 1) to (start + 3, 15) +- Code(Zero) at (prev + 3, 15) to (start + 0, 16) +- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + diff --git a/tests/coverage-map/fn_sig_into_try.rs b/tests/coverage-map/fn_sig_into_try.rs new file mode 100644 index 00000000000..92850c8a188 --- /dev/null +++ b/tests/coverage-map/fn_sig_into_try.rs @@ -0,0 +1,41 @@ +#![feature(coverage_attribute)] +// compile-flags: --edition=2021 + +// Regression test for inconsistent handling of function signature spans that +// are followed by code using the `?` operator. +// +// For each of these similar functions, the line containing the function +// signature should be handled in the same way. + +fn a() -> Option<i32> +{ + Some(7i32); + Some(0) +} + +fn b() -> Option<i32> +{ + Some(7i32)?; + Some(0) +} + +fn c() -> Option<i32> +{ + let _ = Some(7i32)?; + Some(0) +} + +fn d() -> Option<i32> +{ + let _: () = (); + Some(7i32)?; + Some(0) +} + +#[coverage(off)] +fn main() { + a(); + b(); + c(); + d(); +} diff --git a/tests/coverage-map/status-quo/inline-dead.cov-map b/tests/coverage-map/status-quo/inline-dead.cov-map index 483f7ef79c6..06b64da5723 100644 --- a/tests/coverage-map/status-quo/inline-dead.cov-map +++ b/tests/coverage-map/status-quo/inline-dead.cov-map @@ -31,13 +31,15 @@ Number of file 0 mappings: 2 - Code(Counter(0)) at (prev + 7, 6) to (start + 2, 2) Function name: inline_dead::main::{closure#0} -Raw bytes (16): 0x[01, 01, 01, 01, 05, 02, 00, 09, 0d, 00, 0e, 03, 02, 05, 00, 06] +Raw bytes (23): 0x[01, 01, 02, 09, 06, 01, 05, 03, 01, 07, 17, 00, 18, 00, 02, 0d, 00, 0e, 03, 02, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 1 -- expression 0 operands: lhs = Counter(0), rhs = Counter(1) -Number of file 0 mappings: 2 -- Code(Zero) at (prev + 9, 13) to (start + 0, 14) +Number of expressions: 2 +- expression 0 operands: lhs = Counter(2), rhs = Expression(1, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 7, 23) to (start + 0, 24) +- Code(Zero) at (prev + 2, 13) to (start + 0, 14) - Code(Expression(0, Add)) at (prev + 2, 5) to (start + 0, 6) - = (c0 + c1) + = (c2 + (c0 - c1)) diff --git a/tests/coverage-map/status-quo/issue-84561.cov-map b/tests/coverage-map/status-quo/issue-84561.cov-map index 01fa7ec573c..76340b1a78c 100644 --- a/tests/coverage-map/status-quo/issue-84561.cov-map +++ b/tests/coverage-map/status-quo/issue-84561.cov-map @@ -7,15 +7,15 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 4, 10) to (start + 0, 19) Function name: <issue_84561::Foo as core::fmt::Debug>::fmt -Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 89, 01, 09, 00, 25, 05, 00, 25, 00, 26, 02, 01, 09, 00, 0f, 07, 01, 05, 00, 06] +Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 88, 01, 05, 01, 25, 05, 01, 25, 00, 26, 02, 01, 09, 00, 0f, 07, 01, 05, 00, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 -- Code(Counter(0)) at (prev + 137, 9) to (start + 0, 37) -- Code(Counter(1)) at (prev + 0, 37) to (start + 0, 38) +- Code(Counter(0)) at (prev + 136, 5) to (start + 1, 37) +- Code(Counter(1)) at (prev + 1, 37) to (start + 0, 38) - Code(Expression(0, Sub)) at (prev + 1, 9) to (start + 0, 15) = (c0 - c1) - Code(Expression(1, Add)) at (prev + 1, 5) to (start + 0, 6) diff --git a/tests/run-coverage/fn_sig_into_try.coverage b/tests/run-coverage/fn_sig_into_try.coverage new file mode 100644 index 00000000000..f1ddb1da780 --- /dev/null +++ b/tests/run-coverage/fn_sig_into_try.coverage @@ -0,0 +1,45 @@ + LL| |#![feature(coverage_attribute)] + LL| |// compile-flags: --edition=2021 + LL| | + LL| |// Regression test for inconsistent handling of function signature spans that + LL| |// are followed by code using the `?` operator. + LL| |// + LL| |// For each of these similar functions, the line containing the function + LL| |// signature should be handled in the same way. + LL| | + LL| 1|fn a() -> Option<i32> + LL| 1|{ + LL| 1| Some(7i32); + LL| 1| Some(0) + LL| 1|} + LL| | + LL| 1|fn b() -> Option<i32> + LL| 1|{ + LL| 1| Some(7i32)?; + ^0 + LL| 1| Some(0) + LL| 1|} + LL| | + LL| 1|fn c() -> Option<i32> + LL| 1|{ + LL| 1| let _ = Some(7i32)?; + ^0 + LL| 1| Some(0) + LL| 1|} + LL| | + LL| 1|fn d() -> Option<i32> + LL| 1|{ + LL| 1| let _: () = (); + LL| 1| Some(7i32)?; + ^0 + LL| 1| Some(0) + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | a(); + LL| | b(); + LL| | c(); + LL| | d(); + LL| |} + diff --git a/tests/run-coverage/fn_sig_into_try.rs b/tests/run-coverage/fn_sig_into_try.rs new file mode 100644 index 00000000000..92850c8a188 --- /dev/null +++ b/tests/run-coverage/fn_sig_into_try.rs @@ -0,0 +1,41 @@ +#![feature(coverage_attribute)] +// compile-flags: --edition=2021 + +// Regression test for inconsistent handling of function signature spans that +// are followed by code using the `?` operator. +// +// For each of these similar functions, the line containing the function +// signature should be handled in the same way. + +fn a() -> Option<i32> +{ + Some(7i32); + Some(0) +} + +fn b() -> Option<i32> +{ + Some(7i32)?; + Some(0) +} + +fn c() -> Option<i32> +{ + let _ = Some(7i32)?; + Some(0) +} + +fn d() -> Option<i32> +{ + let _: () = (); + Some(7i32)?; + Some(0) +} + +#[coverage(off)] +fn main() { + a(); + b(); + c(); + d(); +} diff --git a/tests/run-coverage/issue-84561.coverage b/tests/run-coverage/issue-84561.coverage index 222f877d36a..e693866e277 100644 --- a/tests/run-coverage/issue-84561.coverage +++ b/tests/run-coverage/issue-84561.coverage @@ -135,7 +135,7 @@ LL| 0|} LL| | LL| |impl std::fmt::Debug for Foo { - LL| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + LL| 7| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { LL| 7| write!(f, "try and succeed")?; ^0 LL| 7| Ok(()) diff --git a/tests/run-make/unknown-mod-stdin/unknown-mod.stderr b/tests/run-make/unknown-mod-stdin/unknown-mod.stderr index d7258fe4f68..81ff8393837 100644 --- a/tests/run-make/unknown-mod-stdin/unknown-mod.stderr +++ b/tests/run-make/unknown-mod-stdin/unknown-mod.stderr @@ -5,6 +5,7 @@ error[E0583]: file not found for module `unknown` | ^^^^^^^^^^^^ | = help: to create the module `unknown`, create file "unknown.rs" or "unknown/mod.rs" + = note: if there is a `mod unknown` elsewhere in the crate already, import it with `use crate::...` instead error: aborting due to previous error diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs index 39980ee7c67..aeb68bf05e1 100644 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs +++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs @@ -1,4 +1,6 @@ // Test the `rustc::span_use_eq_ctxt` internal lint +// #[cfg(bootstrap)] +// ignore-stage1 // compile-flags: -Z unstable-options #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr index b33f6212545..3d8a7dd1ec0 100644 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr +++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr @@ -1,11 +1,11 @@ error: use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` - --> $DIR/span_use_eq_ctxt.rs:12:5 + --> $DIR/span_use_eq_ctxt.rs:14:5 | LL | s.ctxt() == t.ctxt() | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/span_use_eq_ctxt.rs:5:9 + --> $DIR/span_use_eq_ctxt.rs:7:9 | LL | #![deny(rustc::span_use_eq_ctxt)] | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/stable-mir/instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index fe06d9b5cc9..288c163a6a3 100644 --- a/tests/ui-fulldeps/stable-mir/instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -15,7 +15,8 @@ extern crate rustc_smir; extern crate stable_mir; use rustc_middle::ty::TyCtxt; - +use mir::{mono::Instance, TerminatorKind::*}; +use stable_mir::ty::{TyKind, RigidTy}; use stable_mir::*; use rustc_smir::rustc_internal; use std::io::Write; @@ -43,9 +44,28 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { // For all generic items, try_from should fail. assert!(generic.iter().all(|item| mir::mono::Instance::try_from(*item).is_err())); + for instance in instances { + test_body(instance.body()) + } ControlFlow::Continue(()) } +/// Inspect the instance body +fn test_body(body: mir::Body) { + for term in body.blocks.iter().map(|bb| &bb.terminator) { + match &term.kind { + Call{ func, .. } => { + let TyKind::RigidTy(ty) = func.ty(&body.locals).kind() else { unreachable!() }; + let RigidTy::FnDef(def, args) = ty else { unreachable!() }; + let result = Instance::resolve(def, &args); + assert!(result.is_ok()); + } + Goto {..} | Assert{..} | SwitchInt{..} | Return | Drop {..} => { /* Do nothing */} + _ => { unreachable!("Unexpected terminator {term:?}") } + } + } +} + /// This test will generate and analyze a dummy crate using the stable mir. /// For that, it will first write the dummy crate into a file. @@ -56,6 +76,7 @@ fn main() { generate_input(&path).unwrap(); let args = vec![ "rustc".to_string(), + "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), @@ -78,6 +99,9 @@ fn generate_input(path: &str) -> std::io::Result<()> { }} pub fn monomorphic() {{ + let v = vec![10]; + let dup = ty_param(&v); + assert_eq!(v, dup); }} pub mod foo {{ diff --git a/tests/ui/async-await/unnecessary-await.rs b/tests/ui/async-await/unnecessary-await.rs index cd1e2871432..93b68f018e4 100644 --- a/tests/ui/async-await/unnecessary-await.rs +++ b/tests/ui/async-await/unnecessary-await.rs @@ -31,4 +31,9 @@ async fn with_macros() { f!(()); } +// Regression test for issue #117014. +async fn desugaring_span_ctxt() { + for x in [] {}.await //~ ERROR `()` is not a future +} + fn main() {} diff --git a/tests/ui/async-await/unnecessary-await.stderr b/tests/ui/async-await/unnecessary-await.stderr index 9a2a035b2dd..620370a6113 100644 --- a/tests/ui/async-await/unnecessary-await.stderr +++ b/tests/ui/async-await/unnecessary-await.stderr @@ -49,6 +49,19 @@ LL | f!(()); = note: required for `()` to implement `IntoFuture` = note: this error originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error[E0277]: `()` is not a future + --> $DIR/unnecessary-await.rs:36:20 + | +LL | for x in [] {}.await + | -^^^^^ + | || + | |`()` is not a future + | help: remove the `.await` + | + = help: the trait `Future` is not implemented for `()` + = note: () must be a future or must implement `IntoFuture` to be awaited + = note: required for `()` to implement `IntoFuture` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/error-codes/E0583.stderr b/tests/ui/error-codes/E0583.stderr index c7bbbf11499..6707f2864f2 100644 --- a/tests/ui/error-codes/E0583.stderr +++ b/tests/ui/error-codes/E0583.stderr @@ -5,6 +5,7 @@ LL | mod module_that_doesnt_exist; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs" or "$DIR/module_that_doesnt_exist/mod.rs" + = note: if there is a `mod module_that_doesnt_exist` elsewhere in the crate already, import it with `use crate::...` instead error: aborting due to previous error diff --git a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.rs b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.rs index 9b646060adf..1922bfb4913 100644 --- a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.rs +++ b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.rs @@ -3,13 +3,17 @@ #![deny(non_exhaustive_omitted_patterns)] //~^ WARNING unknown lint: `non_exhaustive_omitted_patterns` //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` +//~| WARNING unknown lint: `non_exhaustive_omitted_patterns` #![allow(non_exhaustive_omitted_patterns)] //~^ WARNING unknown lint: `non_exhaustive_omitted_patterns` //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` +//~| WARNING unknown lint: `non_exhaustive_omitted_patterns` fn main() { enum Foo { - A, B, C, + A, + B, + C, } #[allow(non_exhaustive_omitted_patterns)] @@ -17,18 +21,22 @@ fn main() { //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` + //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` match Foo::A { + //~^ ERROR non-exhaustive patterns: `Foo::C` not covered Foo::A => {} Foo::B => {} } - //~^^^^ ERROR non-exhaustive patterns: `Foo::C` not covered + #[warn(non_exhaustive_omitted_patterns)] + //~^ WARNING unknown lint: `non_exhaustive_omitted_patterns` + //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` + //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` + //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` + //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` match Foo::A { Foo::A => {} Foo::B => {} - #[warn(non_exhaustive_omitted_patterns)] _ => {} } - //~^^^ WARNING unknown lint: `non_exhaustive_omitted_patterns` - //~| WARNING unknown lint: `non_exhaustive_omitted_patterns` } diff --git a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr index 1c14622d637..eb61c4cf159 100644 --- a/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr +++ b/tests/ui/feature-gates/feature-gate-non_exhaustive_omitted_patterns_lint.stderr @@ -10,7 +10,7 @@ LL | #![deny(non_exhaustive_omitted_patterns)] = note: `#[warn(unknown_lints)]` on by default warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:6:1 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:7:1 | LL | #![allow(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | #![allow(non_exhaustive_omitted_patterns)] = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:19:5 | LL | #[allow(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)] = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:19:5 | LL | #[allow(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,16 +41,27 @@ LL | #[allow(non_exhaustive_omitted_patterns)] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:29:9 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:31:5 | -LL | #[warn(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[warn(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the `non_exhaustive_omitted_patterns` lint is unstable = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable warning: unknown lint: `non_exhaustive_omitted_patterns` + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:31:5 + | +LL | #[warn(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `non_exhaustive_omitted_patterns` lint is unstable + = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information + = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unknown lint: `non_exhaustive_omitted_patterns` --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:3:1 | LL | #![deny(non_exhaustive_omitted_patterns)] @@ -62,7 +73,7 @@ LL | #![deny(non_exhaustive_omitted_patterns)] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:6:1 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:7:1 | LL | #![allow(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +84,7 @@ LL | #![allow(non_exhaustive_omitted_patterns)] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:19:5 | LL | #[allow(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +95,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:19:5 | LL | #[allow(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,10 +106,21 @@ LL | #[allow(non_exhaustive_omitted_patterns)] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unknown lint: `non_exhaustive_omitted_patterns` - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:29:9 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:31:5 + | +LL | #[warn(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `non_exhaustive_omitted_patterns` lint is unstable + = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information + = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unknown lint: `non_exhaustive_omitted_patterns` + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:31:5 | -LL | #[warn(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[warn(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the `non_exhaustive_omitted_patterns` lint is unstable = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information @@ -106,18 +128,19 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0004]: non-exhaustive patterns: `Foo::C` not covered - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:20:11 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:25:11 | LL | match Foo::A { | ^^^^^^ pattern `Foo::C` not covered | note: `Foo` defined here - --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:12:15 + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:16:9 | LL | enum Foo { | --- -LL | A, B, C, - | ^ not covered +... +LL | C, + | ^ not covered = note: the matched value is of type `Foo` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -125,6 +148,50 @@ LL ~ Foo::B => {}, LL + Foo::C => todo!() | -error: aborting due to previous error; 10 warnings emitted +warning: unknown lint: `non_exhaustive_omitted_patterns` + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:3:1 + | +LL | #![deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `non_exhaustive_omitted_patterns` lint is unstable + = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information + = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unknown lint: `non_exhaustive_omitted_patterns` + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:7:1 + | +LL | #![allow(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `non_exhaustive_omitted_patterns` lint is unstable + = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information + = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unknown lint: `non_exhaustive_omitted_patterns` + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:19:5 + | +LL | #[allow(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `non_exhaustive_omitted_patterns` lint is unstable + = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information + = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unknown lint: `non_exhaustive_omitted_patterns` + --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:31:5 + | +LL | #[warn(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `non_exhaustive_omitted_patterns` lint is unstable + = note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information + = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to previous error; 16 warnings emitted For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/invalid/invalid-llvm-passes.rs b/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs index ee28f5eb6d6..ee28f5eb6d6 100644 --- a/tests/ui/invalid/invalid-llvm-passes.rs +++ b/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs diff --git a/tests/ui/invalid/invalid-llvm-passes.stderr b/tests/ui/invalid-compile-flags/invalid-llvm-passes.stderr index ae1f85e41e4..ae1f85e41e4 100644 --- a/tests/ui/invalid/invalid-llvm-passes.stderr +++ b/tests/ui/invalid-compile-flags/invalid-llvm-passes.stderr diff --git a/tests/ui/invalid-module-declaration/invalid-module-declaration.stderr b/tests/ui/invalid-module-declaration/invalid-module-declaration.stderr index 7bc8efd7e4a..08aec25928c 100644 --- a/tests/ui/invalid-module-declaration/invalid-module-declaration.stderr +++ b/tests/ui/invalid-module-declaration/invalid-module-declaration.stderr @@ -5,6 +5,7 @@ LL | pub mod baz; | ^^^^^^^^^^^^ | = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs" or "$DIR/auxiliary/foo/bar/baz/mod.rs" + = note: if there is a `mod baz` elsewhere in the crate already, import it with `use crate::...` instead error: aborting due to previous error diff --git a/tests/ui/lifetimes/borrowck-let-suggestion.stderr b/tests/ui/lifetimes/borrowck-let-suggestion.stderr index da0078698ae..62119664764 100644 --- a/tests/ui/lifetimes/borrowck-let-suggestion.stderr +++ b/tests/ui/lifetimes/borrowck-let-suggestion.stderr @@ -10,6 +10,10 @@ LL | x.use_mut(); | - borrow later used here | = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider consuming the `Vec<i32>` when turning it into an `Iterator` + | +LL | let mut x = vec![1].into_iter(); + | ~~~~~~~~~ help: consider using a `let` binding to create a longer lived value | LL ~ let binding = vec![1]; diff --git a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr index 31e4206a546..5f4d19439ac 100644 --- a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr +++ b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr @@ -5,6 +5,7 @@ LL | mod missing; | ^^^^^^^^^^^^ | = help: to create the module `missing`, create file "$DIR/foo/missing.rs" or "$DIR/foo/missing/mod.rs" + = note: if there is a `mod missing` elsewhere in the crate already, import it with `use crate::...` instead error: aborting due to previous error diff --git a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr index 9d252398b7a..d5f5ea87059 100644 --- a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr +++ b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr @@ -5,6 +5,7 @@ LL | mod missing; | ^^^^^^^^^^^^ | = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs" or "$DIR/foo_inline/inline/missing/mod.rs" + = note: if there is a `mod missing` elsewhere in the crate already, import it with `use crate::...` instead error: aborting due to previous error diff --git a/tests/ui/modules/special_module_name.stderr b/tests/ui/modules/special_module_name.stderr index bc4b4f1b318..84473bf8c0b 100644 --- a/tests/ui/modules/special_module_name.stderr +++ b/tests/ui/modules/special_module_name.stderr @@ -5,6 +5,7 @@ LL | mod lib; | ^^^^^^^^ | = help: to create the module `lib`, create file "$DIR/lib.rs" or "$DIR/lib/mod.rs" + = note: if there is a `mod lib` elsewhere in the crate already, import it with `use crate::...` instead error[E0583]: file not found for module `main` --> $DIR/special_module_name.rs:4:1 @@ -13,6 +14,7 @@ LL | mod main; | ^^^^^^^^^ | = help: to create the module `main`, create file "$DIR/main.rs" or "$DIR/main/mod.rs" + = note: if there is a `mod main` elsewhere in the crate already, import it with `use crate::...` instead warning: found module declaration for lib.rs --> $DIR/special_module_name.rs:1:1 diff --git a/tests/ui/object-safety/assoc_type_bounds_implicit_sized.fixed b/tests/ui/object-safety/assoc_type_bounds_implicit_sized.fixed new file mode 100644 index 00000000000..45c7e07a264 --- /dev/null +++ b/tests/ui/object-safety/assoc_type_bounds_implicit_sized.fixed @@ -0,0 +1,10 @@ +// run-rustfix +trait TraitWithAType { + type Item: ?Sized; +} +trait Trait {} +struct A {} +impl TraitWithAType for A { + type Item = dyn Trait; //~ ERROR E0277 +} +fn main() {} diff --git a/tests/ui/object-safety/assoc_type_bounds_implicit_sized.rs b/tests/ui/object-safety/assoc_type_bounds_implicit_sized.rs new file mode 100644 index 00000000000..c3e958f4983 --- /dev/null +++ b/tests/ui/object-safety/assoc_type_bounds_implicit_sized.rs @@ -0,0 +1,10 @@ +// run-rustfix +trait TraitWithAType { + type Item; +} +trait Trait {} +struct A {} +impl TraitWithAType for A { + type Item = dyn Trait; //~ ERROR E0277 +} +fn main() {} diff --git a/tests/ui/object-safety/assoc_type_bounds_implicit_sized.stderr b/tests/ui/object-safety/assoc_type_bounds_implicit_sized.stderr new file mode 100644 index 00000000000..110e7150733 --- /dev/null +++ b/tests/ui/object-safety/assoc_type_bounds_implicit_sized.stderr @@ -0,0 +1,20 @@ +error[E0277]: the size for values of type `(dyn Trait + 'static)` cannot be known at compilation time + --> $DIR/assoc_type_bounds_implicit_sized.rs:8:17 + | +LL | type Item = dyn Trait; + | ^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Trait + 'static)` +note: required by a bound in `TraitWithAType::Item` + --> $DIR/assoc_type_bounds_implicit_sized.rs:3:5 + | +LL | type Item; + | ^^^^^^^^^^ required by this bound in `TraitWithAType::Item` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Item: ?Sized; + | ++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/object-safety/assoc_type_bounds_sized_used.stderr b/tests/ui/object-safety/assoc_type_bounds_sized_used.stderr index 224d33fb2da..b67a1244ece 100644 --- a/tests/ui/object-safety/assoc_type_bounds_sized_used.stderr +++ b/tests/ui/object-safety/assoc_type_bounds_sized_used.stderr @@ -33,6 +33,10 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` LL - fn bop<T: Bop + ?Sized>() { LL + fn bop<T: Bop>() { | +help: consider relaxing the implicit `Sized` restriction + | +LL | type Bar: Default + ?Sized + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/mod_file_not_exist.stderr b/tests/ui/parser/mod_file_not_exist.stderr index 62456d51880..c2f9d30d9ec 100644 --- a/tests/ui/parser/mod_file_not_exist.stderr +++ b/tests/ui/parser/mod_file_not_exist.stderr @@ -5,6 +5,7 @@ LL | mod not_a_real_file; | ^^^^^^^^^^^^^^^^^^^^ | = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs" + = note: if there is a `mod not_a_real_file` elsewhere in the crate already, import it with `use crate::...` instead error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_not_exist.rs:7:16 diff --git a/tests/ui/parser/mod_file_not_exist_windows.stderr b/tests/ui/parser/mod_file_not_exist_windows.stderr index d5143dbe982..53b09d8ca53 100644 --- a/tests/ui/parser/mod_file_not_exist_windows.stderr +++ b/tests/ui/parser/mod_file_not_exist_windows.stderr @@ -5,6 +5,7 @@ LL | mod not_a_real_file; | ^^^^^^^^^^^^^^^^^^^^ | = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs" + = note: if there is a `mod not_a_real_file` elsewhere in the crate already, import it with `use crate::...` instead error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` --> $DIR/mod_file_not_exist_windows.rs:7:16 diff --git a/tests/ui/parser/unsafe-mod.stderr b/tests/ui/parser/unsafe-mod.stderr index dac6e7a3550..fbe24aa10da 100644 --- a/tests/ui/parser/unsafe-mod.stderr +++ b/tests/ui/parser/unsafe-mod.stderr @@ -5,6 +5,7 @@ LL | unsafe mod n; | ^^^^^^^^^^^^^ | = help: to create the module `n`, create file "$DIR/n.rs" or "$DIR/n/mod.rs" + = note: if there is a `mod n` elsewhere in the crate already, import it with `use crate::...` instead error: module cannot be declared unsafe --> $DIR/unsafe-mod.rs:1:1 diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs index 3482af74752..ecfeb3f9b98 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs @@ -13,8 +13,8 @@ use enums::{ EmptyNonExhaustiveEnum, NestedNonExhaustive, NonExhaustiveEnum, NonExhaustiveSingleVariant, VariantNonExhaustive, }; -use unstable::{UnstableEnum, OnlyUnstableEnum, UnstableStruct, OnlyUnstableStruct}; use structs::{FunctionalRecord, MixedVisFields, NestedStruct, NormalStruct}; +use unstable::{OnlyUnstableEnum, OnlyUnstableStruct, UnstableEnum, UnstableStruct}; #[non_exhaustive] #[derive(Default)] @@ -35,10 +35,10 @@ fn main() { let enumeration = Bar::A; // Ok: this is a crate local non_exhaustive enum + #[deny(non_exhaustive_omitted_patterns)] match enumeration { Bar::A => {} Bar::B => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } @@ -51,50 +51,87 @@ fn main() { _ => {} } + #[deny(non_exhaustive_omitted_patterns)] match non_enum { + //~^ some variants are not matched explicitly NonExhaustiveEnum::Unit => {} NonExhaustiveEnum::Tuple(_) => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } - //~^^ some variants are not matched explicitly + #[deny(non_exhaustive_omitted_patterns)] match non_enum { + //~^ some variants are not matched explicitly NonExhaustiveEnum::Unit | NonExhaustiveEnum::Struct { .. } => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } - //~^^ some variants are not matched explicitly let x = 5; + // We ignore the guard. + #[deny(non_exhaustive_omitted_patterns)] match non_enum { NonExhaustiveEnum::Unit if x > 10 => {} NonExhaustiveEnum::Tuple(_) => {} NonExhaustiveEnum::Struct { .. } => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } - //~^^ some variants are not matched explicitly + + #[deny(non_exhaustive_omitted_patterns)] + match (non_enum, true) { + (NonExhaustiveEnum::Unit, true) => {} + (NonExhaustiveEnum::Tuple(_), false) => {} + (NonExhaustiveEnum::Struct { .. }, false) => {} + _ => {} + } + #[deny(non_exhaustive_omitted_patterns)] + match (non_enum, true) { + //~^ some variants are not matched explicitly + (NonExhaustiveEnum::Unit, true) => {} + (NonExhaustiveEnum::Tuple(_), false) => {} + _ => {} + } + + #[deny(non_exhaustive_omitted_patterns)] + match (true, non_enum) { + (true, NonExhaustiveEnum::Unit) => {} + (false, NonExhaustiveEnum::Tuple(_)) => {} + (false, NonExhaustiveEnum::Struct { .. }) => {} + _ => {} + } + #[deny(non_exhaustive_omitted_patterns)] + match (true, non_enum) { + //~^ some variants are not matched explicitly + (true, NonExhaustiveEnum::Unit) => {} + (false, NonExhaustiveEnum::Tuple(_)) => {} + _ => {} + } + + #[deny(non_exhaustive_omitted_patterns)] + match Some(non_enum) { + //~^ some variants are not matched explicitly + Some(NonExhaustiveEnum::Unit) => {} + Some(NonExhaustiveEnum::Tuple(_)) => {} + _ => {} + } // Ok: all covered and not `unreachable-patterns` #[deny(unreachable_patterns)] + #[deny(non_exhaustive_omitted_patterns)] match non_enum { NonExhaustiveEnum::Unit => {} NonExhaustiveEnum::Tuple(_) => {} NonExhaustiveEnum::Struct { .. } => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } #[deny(non_exhaustive_omitted_patterns)] match NestedNonExhaustive::B { + //~^ some variants are not matched explicitly NestedNonExhaustive::A(NonExhaustiveEnum::Unit) => {} NestedNonExhaustive::A(_) => {} NestedNonExhaustive::B => {} _ => {} } - //~^^ some variants are not matched explicitly - //~^^^^^ some variants are not matched explicitly #[warn(non_exhaustive_omitted_patterns)] match VariantNonExhaustive::Baz(1, 2) { @@ -120,30 +157,36 @@ fn main() { #[warn(non_exhaustive_omitted_patterns)] let MixedVisFields { a, b, .. } = MixedVisFields::default(); - // Ok: because this only has 1 variant + // Ok: this only has 1 variant #[deny(non_exhaustive_omitted_patterns)] match NonExhaustiveSingleVariant::A(true) { NonExhaustiveSingleVariant::A(true) => {} _ => {} } + // We can't catch the case below, so for consistency we don't catch this one either. #[deny(non_exhaustive_omitted_patterns)] match NonExhaustiveSingleVariant::A(true) { _ => {} } - //~^^ some variants are not matched explicitly + // We can't catch this case, because this would require digging fully through all the values of + // any type we encounter. We need to be able to only consider present constructors. + #[deny(non_exhaustive_omitted_patterns)] + match &NonExhaustiveSingleVariant::A(true) { + _ => {} + } // Ok: we don't lint on `if let` expressions #[deny(non_exhaustive_omitted_patterns)] if let NonExhaustiveEnum::Tuple(_) = non_enum {} + #[deny(non_exhaustive_omitted_patterns)] match UnstableEnum::Stable { + //~^ some variants are not matched explicitly UnstableEnum::Stable => {} UnstableEnum::Stable2 => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } - //~^^ some variants are not matched explicitly // Ok: the feature is on and all variants are matched #[deny(non_exhaustive_omitted_patterns)] @@ -164,10 +207,10 @@ fn main() { #[deny(non_exhaustive_omitted_patterns)] match OnlyUnstableEnum::Unstable { + //~^ some variants are not matched explicitly OnlyUnstableEnum::Unstable => {} _ => {} } - //~^^ some variants are not matched explicitly #[warn(non_exhaustive_omitted_patterns)] let OnlyUnstableStruct { unstable, .. } = OnlyUnstableStruct::new(); @@ -194,14 +237,13 @@ fn main() { let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit; //~^ refutable pattern in local binding - // Check that matching on a reference results in a correctly spanned diagnostic #[deny(non_exhaustive_omitted_patterns)] match &non_enum { + //~^ some variants are not matched explicitly NonExhaustiveEnum::Unit => {} NonExhaustiveEnum::Tuple(_) => {} _ => {} } - //~^^ some variants are not matched explicitly } #[deny(non_exhaustive_omitted_patterns)] diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr index 923394474b2..7db61f1241e 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr @@ -1,5 +1,5 @@ warning: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:102:9 + --> $DIR/omitted-patterns.rs:139:9 | LL | VariantNonExhaustive::Bar { x, .. } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed @@ -7,13 +7,13 @@ LL | VariantNonExhaustive::Bar { x, .. } => {} = help: ensure that all fields are mentioned explicitly by adding the suggested fields = note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:99:12 + --> $DIR/omitted-patterns.rs:136:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:107:9 + --> $DIR/omitted-patterns.rs:144:9 | LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed @@ -21,13 +21,13 @@ LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalReco = help: ensure that all fields are mentioned explicitly by adding the suggested fields = note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:106:12 + --> $DIR/omitted-patterns.rs:143:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:115:29 + --> $DIR/omitted-patterns.rs:152:29 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed @@ -35,13 +35,13 @@ LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = Nested = help: ensure that all fields are mentioned explicitly by adding the suggested fields = note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:114:12 + --> $DIR/omitted-patterns.rs:151:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:115:9 + --> $DIR/omitted-patterns.rs:152:9 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed @@ -50,7 +50,7 @@ LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = Nested = note: the pattern is of type `NestedStruct` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:173:9 + --> $DIR/omitted-patterns.rs:216:9 | LL | let OnlyUnstableStruct { unstable, .. } = OnlyUnstableStruct::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `unstable2` not listed @@ -58,13 +58,13 @@ LL | let OnlyUnstableStruct { unstable, .. } = OnlyUnstableStruct::new(); = help: ensure that all fields are mentioned explicitly by adding the suggested fields = note: the pattern is of type `OnlyUnstableStruct` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:172:12 + --> $DIR/omitted-patterns.rs:215:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:181:9 + --> $DIR/omitted-patterns.rs:224:9 | LL | let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `unstable` not listed @@ -72,120 +72,125 @@ LL | let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); = help: ensure that all fields are mentioned explicitly by adding the suggested fields = note: the pattern is of type `UnstableStruct` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:180:12 + --> $DIR/omitted-patterns.rs:223:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:58:9 + --> $DIR/omitted-patterns.rs:55:11 | -LL | _ => {} - | ^ pattern `NonExhaustiveEnum::Struct { .. }` not covered +LL | match non_enum { + | ^^^^^^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:57:16 + --> $DIR/omitted-patterns.rs:54:12 | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:65:9 + --> $DIR/omitted-patterns.rs:63:11 | -LL | _ => {} - | ^ pattern `NonExhaustiveEnum::Tuple(_)` not covered +LL | match non_enum { + | ^^^^^^^^ pattern `NonExhaustiveEnum::Tuple(_)` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:64:16 + --> $DIR/omitted-patterns.rs:62:12 | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:75:9 + --> $DIR/omitted-patterns.rs:87:11 | -LL | _ => {} - | ^ pattern `NonExhaustiveEnum::Unit` not covered +LL | match (non_enum, true) { + | ^^^^^^^^^^^^^^^^ pattern `(NonExhaustiveEnum::Struct { .. }, _)` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found + = note: the matched value is of type `(NonExhaustiveEnum, bool)` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:74:16 + --> $DIR/omitted-patterns.rs:86:12 | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:92:32 + --> $DIR/omitted-patterns.rs:102:11 | -LL | NestedNonExhaustive::A(_) => {} - | ^ patterns `NonExhaustiveEnum::Tuple(_)` and `NonExhaustiveEnum::Struct { .. }` not covered +LL | match (true, non_enum) { + | ^^^^^^^^^^^^^^^^ pattern `(_, NonExhaustiveEnum::Struct { .. })` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found + = note: the matched value is of type `(bool, NonExhaustiveEnum)` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:89:12 + --> $DIR/omitted-patterns.rs:101:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:94:9 + --> $DIR/omitted-patterns.rs:110:11 | -LL | _ => {} - | ^ pattern `NestedNonExhaustive::C` not covered +LL | match Some(non_enum) { + | ^^^^^^^^^^^^^^ pattern `Some(NonExhaustiveEnum::Struct { .. })` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found + = note: the matched value is of type `Option<NonExhaustiveEnum>` and the `non_exhaustive_omitted_patterns` attribute was found +note: the lint level is defined here + --> $DIR/omitted-patterns.rs:109:12 + | +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:132:9 + --> $DIR/omitted-patterns.rs:128:11 | -LL | _ => {} - | ^ pattern `NonExhaustiveSingleVariant::A(_)` not covered +LL | match NestedNonExhaustive::B { + | ^^^^^^^^^^^^^^^^^^^^^^ patterns `NestedNonExhaustive::C`, `NestedNonExhaustive::A(NonExhaustiveEnum::Tuple(_))` and `NestedNonExhaustive::A(NonExhaustiveEnum::Struct { .. })` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found + = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:130:12 + --> $DIR/omitted-patterns.rs:127:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:144:9 + --> $DIR/omitted-patterns.rs:184:11 | -LL | _ => {} - | ^ pattern `UnstableEnum::Unstable` not covered +LL | match UnstableEnum::Stable { + | ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Unstable` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `UnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:143:16 + --> $DIR/omitted-patterns.rs:183:12 | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:168:9 + --> $DIR/omitted-patterns.rs:209:11 | -LL | _ => {} - | ^ pattern `OnlyUnstableEnum::Unstable2` not covered +LL | match OnlyUnstableEnum::Unstable { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `OnlyUnstableEnum::Unstable2` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `OnlyUnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:165:12 + --> $DIR/omitted-patterns.rs:208:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0005]: refutable pattern in local binding - --> $DIR/omitted-patterns.rs:194:9 + --> $DIR/omitted-patterns.rs:237:9 | LL | let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit; | ^^^^^^^^^^^^^^^ pattern `_` not covered @@ -199,15 +204,15 @@ LL | let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit | ++++++++++++++++ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:202:9 + --> $DIR/omitted-patterns.rs:241:11 | -LL | _ => {} - | ^ pattern `NonExhaustiveEnum::Struct { .. }` not covered +LL | match &non_enum { + | ^^^^^^^^^ pattern `&NonExhaustiveEnum::Struct { .. }` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found + = note: the matched value is of type `&NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:198:12 + --> $DIR/omitted-patterns.rs:240:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs index 82ee68687ed..1828fdef901 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs @@ -6,23 +6,23 @@ // aux-build:unstable.rs extern crate unstable; -use unstable::{UnstableEnum, OnlyUnstableEnum, UnstableStruct, OnlyUnstableStruct}; +use unstable::{OnlyUnstableEnum, OnlyUnstableStruct, UnstableEnum, UnstableStruct}; fn main() { // OK: this matches all the stable variants + #[deny(non_exhaustive_omitted_patterns)] match UnstableEnum::Stable { UnstableEnum::Stable => {} UnstableEnum::Stable2 => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } + #[deny(non_exhaustive_omitted_patterns)] match UnstableEnum::Stable { + //~^ some variants are not matched explicitly UnstableEnum::Stable => {} - #[deny(non_exhaustive_omitted_patterns)] _ => {} } - //~^^ some variants are not matched explicitly // Ok: although this is a bit odd, we don't have anything to report // since there is no stable variants and the feature is off diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr index f38368590fb..27939176f75 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr @@ -13,18 +13,18 @@ LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some variants are not matched explicitly - --> $DIR/stable-omitted-patterns.rs:23:9 + --> $DIR/stable-omitted-patterns.rs:21:11 | -LL | _ => {} - | ^ pattern `UnstableEnum::Stable2` not covered +LL | match UnstableEnum::Stable { + | ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Stable2` not covered | = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `UnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/stable-omitted-patterns.rs:22:16 + --> $DIR/stable-omitted-patterns.rs:20:12 | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr index 7639ae9f6a4..8ca6a37c625 100644 --- a/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr +++ b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr @@ -5,6 +5,7 @@ LL | mod řųśť; | ^^^^^^^^^ | = help: to create the module `řųśť`, create file "$DIR/řųśť.rs" or "$DIR/řųśť/mod.rs" + = note: if there is a `mod řųśť` elsewhere in the crate already, import it with `use crate::...` instead error[E0754]: trying to load file for module `řųśť` with non-ascii identifier name --> $DIR/mod_file_nonascii_forbidden.rs:1:5 diff --git a/tests/ui/suggestions/issue-81839.stderr b/tests/ui/suggestions/issue-81839.stderr index 6d0a0c7b3fa..238ee637c7d 100644 --- a/tests/ui/suggestions/issue-81839.stderr +++ b/tests/ui/suggestions/issue-81839.stderr @@ -4,15 +4,22 @@ error[E0308]: `match` arms have incompatible types LL | / match num { LL | | 1 => { LL | | cx.answer_str("hi"); - | | -------------------- - | | | | - | | | help: consider removing this semicolon - | | this is found to be of type `()` + | | -------------------- this is found to be of type `()` LL | | } LL | | _ => cx.answer_str("hi"), | | ^^^^^^^^^^^^^^^^^^^ expected `()`, found future LL | | } | |_____- `match` arms have incompatible types + | +help: consider removing this semicolon + | +LL - cx.answer_str("hi"); +LL + cx.answer_str("hi") + | +help: consider using a semicolon here, but this will discard any values in the match arms + | +LL | }; + | + error: aborting due to previous error diff --git a/tests/ui/suggestions/silenced-binding-typo.fixed b/tests/ui/suggestions/silenced-binding-typo.fixed new file mode 100644 index 00000000000..e0f9e31bef3 --- /dev/null +++ b/tests/ui/suggestions/silenced-binding-typo.fixed @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let x = 42; //~ HELP + let _y = x; //~ ERROR +} diff --git a/tests/ui/suggestions/silenced-binding-typo.rs b/tests/ui/suggestions/silenced-binding-typo.rs new file mode 100644 index 00000000000..6cadd5a93a7 --- /dev/null +++ b/tests/ui/suggestions/silenced-binding-typo.rs @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let _x = 42; //~ HELP + let _y = x; //~ ERROR +} diff --git a/tests/ui/suggestions/silenced-binding-typo.stderr b/tests/ui/suggestions/silenced-binding-typo.stderr new file mode 100644 index 00000000000..9c0e6e26569 --- /dev/null +++ b/tests/ui/suggestions/silenced-binding-typo.stderr @@ -0,0 +1,14 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/silenced-binding-typo.rs:4:14 + | +LL | let _y = x; + | ^ + | +help: a local variable with a similar name exists, consider changing it + | +LL | let x = 42; + | ~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/typeck/issue-36708.stderr b/tests/ui/typeck/issue-36708.stderr index 140f19f1ff7..f1e0f471928 100644 --- a/tests/ui/typeck/issue-36708.stderr +++ b/tests/ui/typeck/issue-36708.stderr @@ -2,7 +2,12 @@ error[E0049]: method `foo` has 1 type parameter but its trait declaration has 0 --> $DIR/issue-36708.rs:8:12 | LL | fn foo<T>() {} - | ^ found 1 type parameter, expected 0 + | ^ found 1 type parameter + | + ::: $DIR/auxiliary/issue-36708.rs:4:5 + | +LL | fn foo(); + | --------- expected 0 type parameters error: aborting due to previous error diff --git a/tests/ui/wf/wf-unsafe-trait-obj-match.stderr b/tests/ui/wf/wf-unsafe-trait-obj-match.stderr index 66504e44060..d5b23572ff5 100644 --- a/tests/ui/wf/wf-unsafe-trait-obj-match.stderr +++ b/tests/ui/wf/wf-unsafe-trait-obj-match.stderr @@ -11,6 +11,10 @@ LL | | } | = note: expected reference `&S` found reference `&R` +help: consider using a semicolon here, but this will discard any values in the match arms + | +LL | }; + | + error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/wf-unsafe-trait-obj-match.rs:26:21 diff --git a/triagebot.toml b/triagebot.toml index fbdc28787ff..e3c4233c843 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -23,10 +23,10 @@ allow-unauthenticated = [ "needs-triage", ] -[review-submitted] -# This label is added when a "request changes" review is submitted. -reviewed_label = "S-waiting-on-author" -# These labels are removed when a "request changes" review is submitted. +[review-submitted] +# This label is added when a "request changes" review is submitted. +reviewed_label = "S-waiting-on-author" +# These labels are removed when a "request changes" review is submitted. review_labels = ["S-waiting-on-review"] [review-requested] @@ -586,17 +586,19 @@ message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x message = "Third-party dependency whitelist may have been modified! You must ensure that any new dependencies have compatible licenses before merging." cc = ["@davidtwco", "@wesleywiser"] -[mentions."src/bootstrap/config.rs"] -message = "This PR changes `src/bootstrap/config.rs`. If appropriate, please also update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/lib.rs` and `change-id` in `config.example.toml`." +[mentions."src/bootstrap/src/core/config"] +message = "This PR modifies `src/bootstrap/src/core/config`. If appropriate, please also update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/lib.rs` and `change-id` in `config.example.toml`." +[mentions."src/bootstrap/defaults"] +message = "This PR modifies `src/bootstrap/defaults`. If appropriate, please also update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/lib.rs` and `change-id` in `config.example.toml`." [mentions."config.example.toml"] -message = "This PR changes `config.example.toml`. If appropriate, please also update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/lib.rs` and `change-id` in `config.example.toml`." +message = "This PR changes `config.example.toml`. If appropriate, please also update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/lib.rs` and `change-id` in `config.example.toml`." [mentions."src/bootstrap/defaults/config.compiler.toml"] message = "This PR changes src/bootstrap/defaults/config.compiler.toml. If appropriate, please also update `config.codegen.toml` so the defaults are in sync." [mentions."src/bootstrap/defaults/config.codegen.toml"] message = "This PR changes src/bootstrap/defaults/config.codegen.toml. If appropriate, please also update `config.compiler.toml` so the defaults are in sync." -[mentions."src/bootstrap/llvm.rs"] +[mentions."src/bootstrap/src/core/build_steps/llvm.rs"] message = "This PR changes how LLVM is built. Consider updating src/bootstrap/download-ci-llvm-stamp." [mentions."tests/ui/deriving/deriving-all-codegen.stdout"] |
