diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8c207e7ea115..9bde9c4fbffc 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 232c9c9441fb..6c96f415f44c 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",
@@ -5740,33 +5739,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
diff --git a/README.md b/README.md
index f0c45f341d81..a88ee4b8bf06 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.
+
+Table of content
+
+- [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)
+
+
+
## 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 00816c0f2539..ecf7930ff6b0 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 8b4efd4e3948..aa1a2bad2cf2 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 652d6eca3f6e..47d9a3b93f72 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 7c324421be9c..8079913cb0ff 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 28a37b7995b7..5ad5e0e8140c 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 31a4b209826f..1ed896c6bf0e 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 16e7a4bafaee..c68968b4fde2 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 95ff6b754220..1e24d1b113fe 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 9f24c043a504..149f1618f5c0 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 ae13ab3b0ca6..000000000000
--- 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
-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 eb452c5cd377..000000000000
--- 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
-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>(
- 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 5c9dc7b361f9..000000000000
--- 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 de89c4f5eff8..f10b4d6b9d4f 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 86ef127badd4..3735ac1c17b1 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 60ac6bc9951e..3e48fb006dea 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 - < config.toml < 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 49f51f9f956b..d3a7decc5430 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::>()
diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs
index ebd9b728d90b..6692d1b85eac 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 dd2127d554dd..ed0772342548 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 8992de5a923f..148193b5a975 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 {
- 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 f75add90aa2c..2f825b801acd 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::>();
+ 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 204a73b788a6..cf7c7e6be60f 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 f77bd53e76c6..dce4e199e17e 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 efdb44248d1d..008565e4c7b9 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 9511f1700e10..5d7f385c6e4a 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(never_type)]
-#![feature(type_alias_impl_trait)]
-#![feature(lazy_cell)]
-#![feature(rustc_attrs)]
#![feature(negative_impls)]
+#![feature(never_type)]
+#![feature(ptr_alignment_type)]
+#![feature(rustc_attrs)]
+#![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 b067f9d4502d..a8c442377fbc 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 DynSend for T {}
impl 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 where T]
[std::sync::mpsc::Sender 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 where T: DynSync]
);
}
-);
+}
pub fn assert_dyn_sync() {}
pub fn assert_dyn_send() {}
diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs
index e688feb5fe17..ef7375a73206 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 {
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 {
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 {
let field = 1;
let contents = fs::read("/proc/self/statm").ok()?;
@@ -912,7 +914,8 @@ cfg_if! {
let npages = s.parse::().ok()?;
Some(npages * 4096)
}
- } else {
+ }
+ _ => {
pub fn get_resident_set_size() -> Option {
None
}
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index cca043ba0d18..62fff604f813 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 6c7e68246ea5..8b93829623db 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 e3a0ae3570eb..d86632c47fc5 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 a1470cc69c30..74e2b157dd0a 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 aef880acbe1b..84e986488a6a 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 fdeaf7f6850d..97ab3d0b7517 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 c8d42937f7bd..918e484b9f44 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 b5f873c9257f..cd5206a837ff 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 9397e98343e3..9e2ff3820af6 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,
pub opt_suggest_box_span: Option,
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 4f98932a88d4..c09dd1864185 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>,
+ witnesses: Vec>,
) -> 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 9d38087fdeb9..9156af3425ae 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>,
) {
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>,
+ witnesses: Vec>,
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- >,
+ patterns: impl Iterator
- >,
) -> Vec {
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 bbc0aeb66cfd..86018340a69e 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>,
- /// 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>,
}
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
- > + 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>,
+ ty: Ty<'tcx>,
+}
+
+impl<'tcx> WitnessPat<'tcx> {
+ pub(super) fn new(ctor: Constructor<'tcx>, fields: Vec, 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
- > {
+ 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 6cd73c7eaa95..a8cee5a61edb 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>),
+ WithWitnesses(Vec>),
}
-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> = missing
+ let new_patterns: Vec> = 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>);
+#[derive(Debug, Clone)]
+pub(crate) struct WitnessStack<'tcx>(Vec>);
-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::>();
-
- // 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> {
+ 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> = (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>,
+ pub(crate) non_exhaustiveness_witnesses: Vec>,
}
/// 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::>();
+ 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 a83ccf8fc3c2..d07f59bc72af 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 {
- self.bcb_counters[bcb].take()
- }
-
- pub(super) fn drain_bcb_counters(
- &mut self,
- ) -> impl Iterator
- + '_ {
+ pub(super) fn bcb_node_counters(
+ &self,
+ ) -> impl Iterator
- {
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
- + '_ {
- 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
- {
+ 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 {
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 6fdaff6b4c02..c9b36ba25ac3 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,
}
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);
+ 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 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();
- };
-
- 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 {
+ 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 mut mappings = Vec::new();
- 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 }
- }));
+ // 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();
- inject_statement(
- self.mir_body,
- self.make_mir_coverage_kind(&counter_kind),
- self.bcb_leader_bb(bcb),
- );
- }
- }
+ // 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,
- ));
- }
-
- 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 { .. } => {}
+ 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(),
+ );
}
}
- }
- #[inline]
- fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock {
- self.bcb_data(bcb).leader_bb()
- }
+ // 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_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock {
- self.bcb_data(bcb).last_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_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData {
- &self.basic_coverage_blocks[bcb]
+ 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);
+ }
+
+ 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 3f7ba5725104..704eea413e11 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
- {
- 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 02e2cf6b05e3..6189e5379ea0 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 {
- let mut initial_spans = Vec::::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 {
- 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
- + 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 1d8cbe0e21b7..4009e2892406 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 925ee615b095..4ad838e5aed2 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 000000000000..f42a97393206
--- /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 61227e5d38fd..b1ea5e898b81 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 for Tables<'tcx> {
type Output = DefId;
@@ -231,3 +233,11 @@ impl Index {
+ 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 000000000000..8ff3958da7bd
--- /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(&self, value: T) -> T
+ where
+ T: ty::TypeFoldable>,
+ {
+ 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 8a1b6b6bfe9e..d5379797f1c6 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 {
+ 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 ee93f74e750d..31c2a56faa53 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 450d5455ff93..7da7dc610ecf 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,
multi_byte_chars: &mut Vec,
@@ -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,
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 49b4042d1488..8bb64aa5f128 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))]
-#![feature(array_windows)]
-#![feature(if_let_guard)]
-#![feature(negative_impls)]
-#![feature(min_specialization)]
-#![feature(rustc_attrs)]
-#![feature(let_chains)]
-#![feature(round_char_boundary)]
-#![feature(read_buf)]
-#![feature(new_uninit)]
-#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
-#![allow(internal_features)]
+#![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(let_chains)]
+#![feature(min_specialization)]
+#![feature(negative_impls)]
+#![feature(new_uninit)]
+#![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 6f0e7f799495..db3d8106040b 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 86328cb78abb..78b466907b32 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 12ac8f1ca65a..199106914562 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 59af3f64ad34..be5ccac78c7c 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;
}
// 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 ea30fe6dbcd8..5eee1ec00df5 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,
- pub locals: Vec,
+ pub locals: LocalDecls,
}
+type LocalDecls = Vec;
+
#[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),
}
+
+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 d8e8ccb04548..997576fc7cbf 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 {
+ 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 0ed2813bccf5..8f7f8bd4e384 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);
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 8ab851a0ea64..4a62013c685f 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -207,7 +207,7 @@ impl Box {
/// ```
/// 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 046ac488b1ff..ad0a82eab8ca 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 8db3a950c40f..5689a82e00a3 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 918b19612ae4..7d40db2ee234 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 93b07350ac17..5136aec9896f 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 000000000000..b4a239fd3bc8
--- /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 16457ed0ff82..4bcae4d222c7 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 fef25ad8635a..26c9877dbffb 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 cbcb42dad79b..2c50addfd7df 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 e001197842bd..55c0e105b307 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 2c958ccbbc24..c7eeed179546 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`)
+**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 96a90d599aba..56f56fff1e78 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 60f9bbd7458b..ae8618dcaa06 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 3f1ff66b8cf6..cc9bd727937b 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 481c44031cf7..77438b27f900 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 3d6d257e386b..2d11fa6b647d 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 5de79133c7dc..5ecd0ffadf36 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 d82ea6d2fc80..617c96b4fcbc 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 597fca888851..ee66c841ed25 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 ee10334c67f1..716908483e9d 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
similarity index 60%
rename from src/tools/clippy/clippy_lints/src/enum_variants.rs
rename to src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index e332f681b6db..8b4984da3dd1 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 43e1b92c9b9a..0ee291a4e9da 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
- > + '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 0f35ec276657..a70a38ee08bf 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, Vec)>) -> 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,
@@ -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::>();
@@ -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 f7b3b2358a0b..5fffb27cda2f 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 914ceca2995d..5a87e75722d0 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 6c7c57ba1d6b..552c57d5e025 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 f264424470da..9da20a28fba5 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 a23000e5fe15..b5ab94b3a2f8 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 3337b250c0e7..20878f1e4dfe 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 84a21de0ac86..8849a4f49420 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 ee063adac64a..e7b4564c651e 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 7be1ce483f63..57581363cfa0 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 afdb8ce94ac4..04ddaaa2f461 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 47e2e744112c..4a651396f14d 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 f2773cad400c..0629dee4f722 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 96d83e114a41..fc088710e429 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 f0b865be8042..d205237a5915 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 fe35126aab21..2c42a7a3676a 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 38a75034cd31..377cbef7b99e 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 {
- 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 212d6234bdb3..1d5874f6feac 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);
+
+ 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 = 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 {
- 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 = 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);
-
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 6d7df2eac629..f3c0d616473f 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 d267b3de7f25..ed279a3813d7 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 2b4e3260c56b..613afa46a912 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`.
- // 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