Auto merge of #3133 - rust-lang:rustup-2023-10-22, r=RalfJung

Automatic Rustup
This commit is contained in:
bors 2023-10-22 06:42:10 +00:00
commit f6be93fc61
240 changed files with 4118 additions and 2301 deletions

View File

@ -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

View File

@ -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"

View File

@ -11,6 +11,20 @@ standard library, and documentation.
If you wish to _contribute_ to the compiler, you should read
[CONTRIBUTING.md](CONTRIBUTING.md) instead.
<details>
<summary>Table of content</summary>
- [Quick Start](#quick-start)
- [Installing from Source](#installing-from-source)
- [Building Documentation](#building-documentation)
- [Notes](#notes)
- [Getting Help](#getting-help)
- [Contributing](#contributing)
- [License](#license)
- [Trademark](#trademark)
</details>
## Quick Start
Read ["Installation"] from [The Book].

View File

@ -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(

View File

@ -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:

View File

@ -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

View File

@ -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",

View File

@ -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"] }

View File

@ -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);

View File

@ -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());

View File

@ -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",
);

View File

@ -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()];

View File

@ -1,24 +0,0 @@
From a8fb97120d71252538b6b026695df40d02696bdb Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Sat, 15 Aug 2020 20:04:38 +0200
Subject: [PATCH] [rand] Disable failing test
---
src/distributions/uniform.rs | 1 +
1 file changed, 1 insertion(+), 0 deletions(-)
diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs
index 480b859..c80bb6f 100644
--- a/src/distributions/uniform.rs
+++ b/src/distributions/uniform.rs
@@ -1314,6 +1314,7 @@ mod tests {
not(target_arch = "wasm32"),
not(target_arch = "asmjs")
))]
+ #[ignore] // Requires unwinding
fn test_float_assertions() {
use super::SampleUniform;
use std::panic::catch_unwind;
--
2.20.1

View File

@ -1,47 +0,0 @@
From eec874c889b8d24e5ad50faded24288150f057b1 Mon Sep 17 00:00:00 2001
From: Afonso Bordado <afonsobordado@az8.co>
Date: Tue, 27 Sep 2022 08:13:58 +0100
Subject: [PATCH] Disable rand tests on mingw
---
rand_distr/src/pareto.rs | 2 ++
rand_distr/tests/value_stability.rs | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/rand_distr/src/pareto.rs b/rand_distr/src/pareto.rs
index 217899e..9cedeb7 100644
--- a/rand_distr/src/pareto.rs
+++ b/rand_distr/src/pareto.rs
@@ -107,6 +107,8 @@ mod tests {
}
#[test]
+ // This is broken on x86_64-pc-windows-gnu presumably due to a broken powf implementation
+ #[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)]
fn value_stability() {
fn test_samples<F: Float + Debug + Display + LowerExp, D: Distribution<F>>(
distr: D, thresh: F, expected: &[F],
diff --git a/rand_distr/tests/value_stability.rs b/rand_distr/tests/value_stability.rs
index 192ba74..0101ace 100644
--- a/rand_distr/tests/value_stability.rs
+++ b/rand_distr/tests/value_stability.rs
@@ -72,6 +72,8 @@ fn unit_disc_stability() {
}
#[test]
+// This is broken on x86_64-pc-windows-gnu
+#[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)]
fn pareto_stability() {
test_samples(213, Pareto::new(1.0, 1.0).unwrap(), &[
1.0423688f32, 2.1235929, 4.132709, 1.4679428,
@@ -143,6 +145,8 @@ fn inverse_gaussian_stability() {
}
#[test]
+// This is broken on x86_64-pc-windows-gnu
+#[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)]
fn gamma_stability() {
// Gamma has 3 cases: shape == 1, shape < 1, shape > 1
test_samples(223, Gamma::new(1.0, 5.0).unwrap(), &[
--
2.25.1

View File

@ -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",
]

View File

@ -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",

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2023-10-09"
channel = "nightly-2023-10-21"
components = ["rust-src", "rustc-dev", "llvm-tools"]

View File

@ -1,7 +1,10 @@
#!/usr/bin/env bash
set -e
./y.sh build
# Compiletest expects all standard library paths to start with /rustc/FAKE_PREFIX.
# CG_CLIF_STDLIB_REMAP_PATH_PREFIX will cause cg_clif's build system to pass
# --remap-path-prefix to handle this.
CG_CLIF_STDLIB_REMAP_PATH_PREFIX=/rustc/FAKE_PREFIX ./y.sh build
echo "[SETUP] Rust fork"
git clone https://github.com/rust-lang/rust.git || true
@ -13,23 +16,6 @@ git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')"
git -c user.name=Dummy -c user.email=dummy@example.com -c commit.gpgSign=false \
am ../patches/*-stdlib-*.patch
git apply - <<EOF
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
index d95b5b7f17f..00b6f0e3635 100644
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2018"
[dependencies]
core = { path = "../core" }
-compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "0.1.66", features = ['rustc-dep-of-std', 'no-asm'] }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
rand_xorshift = "0.3.0"
EOF
cat > config.toml <<EOF
change-id = 115898
@ -49,11 +35,6 @@ verbose-tests = false
EOF
popd
# FIXME remove once inline asm is fully supported
export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc"
export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd build/stdlib; pwd)"
# Allow the testsuite to use llvm tools
host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
export LLVM_BIN_DIR="$(rustc --print sysroot)/lib/rustlib/$host_triple/bin"

View File

@ -27,8 +27,6 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
# missing features
# ================
rm -r tests/run-make/comment-section # cg_clif doesn't yet write the .comment section
# requires stack unwinding
# FIXME add needs-unwind to these tests
rm -r tests/run-make/libtest-junit
@ -44,10 +42,8 @@ rm tests/ui/proc-macro/allowed-signatures.rs
rm tests/ui/proc-macro/no-mangle-in-proc-macro-issue-111888.rs
# vendor intrinsics
rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected
rm tests/ui/sse2.rs # CodegenBackend::target_features not yet implemented
rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant"
rm tests/ui/simd/intrinsic/generic-bswap-byte.rs # simd_bswap not yet implemented
rm tests/ui/simd/intrinsic/generic-arithmetic-pass.rs # many missing simd intrinsics
# exotic linkages
rm tests/ui/issues/issue-33992.rs # unsupported linkages
@ -115,21 +111,6 @@ rm tests/ui/consts/issue-33537.rs # same
rm tests/ui/layout/valid_range_oob.rs # different ICE message
rm tests/ui/const-generics/generic_const_exprs/issue-80742.rs # gives error instead of ICE with cg_clif
rm tests/ui/consts/issue-miri-1910.rs # different error message
rm tests/ui/consts/offset_ub.rs # same
rm tests/ui/consts/const-eval/ub-slice-get-unchecked.rs # same
rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # same
rm tests/ui/lint/lint-const-item-mutation.rs # same
rm tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs # same
rm tests/ui/suggestions/derive-trait-for-method-call.rs # same
rm tests/ui/typeck/issue-46112.rs # same
rm tests/ui/consts/const_cmp_type_id.rs # same
rm tests/ui/consts/issue-73976-monomorphic.rs # same
rm tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs # same
rm tests/ui/consts/const-eval/nonnull_as_ref_ub.rs # same
rm tests/ui/consts/issue-94675.rs # same
rm tests/ui/associated-types/issue-85103-layout-debug.rs # same
# rustdoc-clif passes extra args, suppressing the help message when no args are passed
rm -r tests/run-make/issue-88756-default-output

View File

@ -20,7 +20,7 @@ use crate::prelude::*;
pub(crate) fn producer() -> String {
format!(
"cg_clif (rustc {}, cranelift {})",
"rustc version {} with cranelift {}",
rustc_interface::util::rustc_version_str().unwrap_or("unknown version"),
cranelift_codegen::VERSION,
)

View File

@ -394,28 +394,31 @@ pub(crate) fn run_aot(
let modules = tcx.sess.time("codegen mono items", || {
cgus.iter()
.enumerate()
.map(|(i, cgu)| match cgu_reuse[i] {
CguReuse::No => {
let dep_node = cgu.codegen_dep_node(tcx);
tcx.dep_graph
.with_task(
dep_node,
tcx,
(
backend_config.clone(),
global_asm_config.clone(),
cgu.name(),
concurrency_limiter.acquire(tcx.sess.diagnostic()),
),
module_codegen,
Some(rustc_middle::dep_graph::hash_result),
)
.0
}
CguReuse::PreLto => unreachable!("LTO not yet supported"),
CguReuse::PostLto => {
concurrency_limiter.job_already_done();
OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))
.map(|(i, cgu)| {
let cgu_reuse =
if backend_config.disable_incr_cache { CguReuse::No } else { cgu_reuse[i] };
match cgu_reuse {
CguReuse::No => {
let dep_node = cgu.codegen_dep_node(tcx);
tcx.dep_graph
.with_task(
dep_node,
tcx,
(
backend_config.clone(),
global_asm_config.clone(),
cgu.name(),
concurrency_limiter.acquire(tcx.sess.diagnostic()),
),
module_codegen,
Some(rustc_middle::dep_graph::hash_result),
)
.0
}
CguReuse::PreLto | CguReuse::PostLto => {
concurrency_limiter.job_already_done();
OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))
}
}
})
.collect::<Vec<_>>()

View File

@ -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(),
}

View File

@ -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),
}
}

View File

@ -1,6 +1,6 @@
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(all(doc, not(bootstrap)), allow(internal_features))]
#![cfg_attr(all(doc, not(bootstrap)), feature(rustdoc_internals))]
#![cfg_attr(all(doc, not(bootstrap)), doc(rust_logo))]
#![feature(rustc_private)]
// Note: please avoid adding other feature gates where possible
#![warn(rust_2018_idioms)]
@ -189,7 +189,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
}
fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> {
vec![]
vec![] // FIXME necessary for #[cfg(target_feature]
}
fn print_version(&self) {

View File

@ -4,14 +4,16 @@ use crate::coverageinfo::ffi::CounterMappingRegion;
use crate::coverageinfo::map_data::FunctionCoverage;
use crate::llvm;
use rustc_codegen_ssa::traits::ConstMethods;
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::mir;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefIdSet;
use rustc_span::Symbol;
/// Generates and exports the Coverage Map.
@ -94,8 +96,14 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
let mut unused_function_names = Vec::new();
let covfun_section_name = coverageinfo::covfun_section_name(cx);
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
if !is_used {
unused_function_names.push(mangled_function_name);
}
save_function_record(
cx,
&covfun_section_name,
@ -107,6 +115,25 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
);
}
// For unused functions, we need to take their mangled names and store them
// in a specially-named global array. LLVM's `InstrProfiling` pass will
// detect this global and include those names in its `__llvm_prf_names`
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
if !unused_function_names.is_empty() {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
let name_globals = unused_function_names
.into_iter()
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
.collect::<Vec<_>>();
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names");
llvm::set_global_constant(array, true);
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
llvm::set_initializer(array, initializer);
}
// Save the coverage data value to LLVM IR
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
}
@ -287,13 +314,12 @@ fn save_function_record(
/// `-Clink-dead-code` will not generate code for unused generic functions.)
///
/// We can find the unused functions (including generic functions) by the set difference of all MIR
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query
/// `codegened_and_inlined_items`).
/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`codegenned_and_inlined_items`).
///
/// These unused functions are then codegen'd in one of the CGUs which is marked as the
/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating
/// code regions for the same function more than once which can lead to linker errors regarding
/// duplicate symbols.
/// These unused functions don't need to be codegenned, but we do need to add them to the function
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
/// We also end up adding their symbol names to a special global array that LLVM will include in
/// its embedded coverage data.
fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
@ -324,19 +350,80 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
})
.collect();
let codegenned_def_ids = tcx.codegened_and_inlined_items(());
let codegenned_def_ids = codegenned_and_inlined_items(tcx);
for non_codegenned_def_id in
eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id))
{
// For each `DefId` that should have coverage instrumentation but wasn't
// codegenned, add it to the function coverage map as an unused function.
for def_id in eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id)) {
// Skip any function that didn't have coverage data added to it by the
// coverage instrumentor.
let body = tcx.instance_mir(ty::InstanceDef::Item(non_codegenned_def_id));
let body = tcx.instance_mir(ty::InstanceDef::Item(def_id));
let Some(function_coverage_info) = body.function_coverage_info.as_deref() else {
continue;
};
debug!("generating unused fn: {:?}", non_codegenned_def_id);
cx.define_unused_fn(non_codegenned_def_id, function_coverage_info);
debug!("generating unused fn: {def_id:?}");
let instance = declare_unused_fn(tcx, def_id);
add_unused_function_coverage(cx, instance, function_coverage_info);
}
}
/// All items participating in code generation together with (instrumented)
/// items inlined into them.
fn codegenned_and_inlined_items(tcx: TyCtxt<'_>) -> DefIdSet {
let (items, cgus) = tcx.collect_and_partition_mono_items(());
let mut visited = DefIdSet::default();
let mut result = items.clone();
for cgu in cgus {
for item in cgu.items().keys() {
if let mir::mono::MonoItem::Fn(ref instance) = item {
let did = instance.def_id();
if !visited.insert(did) {
continue;
}
let body = tcx.instance_mir(instance.def);
for block in body.basic_blocks.iter() {
for statement in &block.statements {
let mir::StatementKind::Coverage(_) = statement.kind else { continue };
let scope = statement.source_info.scope;
if let Some(inlined) = scope.inlined_instance(&body.source_scopes) {
result.insert(inlined.def_id());
}
}
}
}
}
}
result
}
fn declare_unused_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::Instance<'tcx> {
ty::Instance::new(
def_id,
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
)
}
fn add_unused_function_coverage<'tcx>(
cx: &CodegenCx<'_, 'tcx>,
instance: ty::Instance<'tcx>,
function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo,
) {
// An unused function's mappings will automatically be rewritten to map to
// zero, because none of its counters/expressions are marked as seen.
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
if let Some(coverage_context) = cx.coverage_context() {
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
} else {
bug!("Could not get the `coverage_context`");
}
}

View File

@ -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

View File

@ -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" }

View File

@ -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;
}

View File

@ -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;

View File

@ -1,11 +1,12 @@
cfg_if!(
if #[cfg(not(parallel_compiler))] {
cfg_match! {
cfg(not(parallel_compiler)) => {
pub auto trait DynSend {}
pub auto trait DynSync {}
impl<T> DynSend for T {}
impl<T> DynSync for T {}
} else {
}
_ => {
#[rustc_on_unimplemented(
message = "`{Self}` doesn't implement `DynSend`. \
Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`"
@ -48,13 +49,10 @@ cfg_if!(
[std::io::StdoutLock<'_>]
[std::io::StderrLock<'_>]
);
cfg_if!(
// Consistent with `std`
// `os_imp::Env` is `!Send` in these platforms
if #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] {
impl !DynSend for std::env::VarsOs {}
}
);
#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))]
// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms
impl !DynSend for std::env::VarsOs {}
macro_rules! already_send {
($([$ty: ty])*) => {
@ -123,13 +121,10 @@ cfg_if!(
[std::sync::mpsc::Receiver<T> where T]
[std::sync::mpsc::Sender<T> where T]
);
cfg_if!(
// Consistent with `std`
// `os_imp::Env` is `!Sync` in these platforms
if #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] {
impl !DynSync for std::env::VarsOs {}
}
);
#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))]
// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms
impl !DynSync for std::env::VarsOs {}
macro_rules! already_sync {
($([$ty: ty])*) => {
@ -183,7 +178,7 @@ cfg_if!(
[thin_vec::ThinVec<T> where T: DynSync]
);
}
);
}
pub fn assert_dyn_sync<T: ?Sized + DynSync>() {}
pub fn assert_dyn_send<T: ?Sized + DynSend>() {}

View File

@ -859,8 +859,8 @@ fn get_thread_id() -> u32 {
}
// Memory reporting
cfg_if! {
if #[cfg(windows)] {
cfg_match! {
cfg(windows) => {
pub fn get_resident_set_size() -> Option<usize> {
use std::mem;
@ -885,7 +885,8 @@ cfg_if! {
Some(pmc.WorkingSetSize)
}
} else if #[cfg(target_os = "macos")] {
}
cfg(target_os = "macos") => {
pub fn get_resident_set_size() -> Option<usize> {
use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO};
use std::mem;
@ -903,7 +904,8 @@ cfg_if! {
}
}
}
} else if #[cfg(unix)] {
}
cfg(unix) => {
pub fn get_resident_set_size() -> Option<usize> {
let field = 1;
let contents = fs::read("/proc/self/statm").ok()?;
@ -912,7 +914,8 @@ cfg_if! {
let npages = s.parse::<usize>().ok()?;
Some(npages * 4096)
}
} else {
}
_ => {
pub fn get_resident_set_size() -> Option<usize> {
None
}

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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),),
);
}

View File

@ -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,

View File

@ -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(

View File

@ -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",

View File

@ -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}`" }
}

View File

@ -541,6 +541,7 @@ pub struct MatchExpressionArmCause<'tcx> {
pub prior_arm_ty: Ty<'tcx>,
pub prior_arm_span: Span,
pub scrut_span: Span,
pub scrut_hir_id: hir::HirId,
pub source: hir::MatchSource,
pub prior_arms: Vec<Span>,
pub opt_suggest_box_span: Option<Span>,

View File

@ -1,6 +1,6 @@
use crate::{
fluent_generated as fluent,
thir::pattern::{deconstruct_pat::DeconstructedPat, MatchCheckCtxt},
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
};
use rustc_errors::{
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@ -810,7 +810,7 @@ impl<'tcx> Uncovered<'tcx> {
pub fn new<'p>(
span: Span,
cx: &MatchCheckCtxt<'p, 'tcx>,
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
witnesses: Vec<WitnessPat<'tcx>>,
) -> Self {
let witness_1 = witnesses.get(0).unwrap().to_pat(cx);
Self {

View File

@ -1,4 +1,4 @@
use super::deconstruct_pat::{Constructor, DeconstructedPat};
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
use super::usefulness::{
compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
};
@ -279,7 +279,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let scrut = &self.thir[scrut];
let scrut_ty = scrut.ty;
let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty);
let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty, scrut.span);
match source {
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
@ -473,7 +473,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let pattern = self.lower_pattern(&mut cx, pat);
let pattern_ty = pattern.ty();
let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false };
let report = compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty);
let report =
compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty, pattern.span());
// Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
// only care about exhaustiveness here.
@ -662,7 +663,7 @@ fn is_let_irrefutable<'p, 'tcx>(
pat: &'p DeconstructedPat<'p, 'tcx>,
) -> bool {
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty(), pat.span());
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
// This also reports unreachable sub-patterns though, so we can't just replace it with an
@ -701,8 +702,8 @@ fn report_arm_reachability<'p, 'tcx>(
}
}
fn collect_non_exhaustive_tys<'p, 'tcx>(
pat: &DeconstructedPat<'p, 'tcx>,
fn collect_non_exhaustive_tys<'tcx>(
pat: &WitnessPat<'tcx>,
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
@ -718,7 +719,7 @@ fn non_exhaustive_match<'p, 'tcx>(
thir: &Thir<'tcx>,
scrut_ty: Ty<'tcx>,
sp: Span,
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
witnesses: Vec<WitnessPat<'tcx>>,
arms: &[ArmId],
expr_span: Span,
) -> ErrorGuaranteed {
@ -896,10 +897,10 @@ fn non_exhaustive_match<'p, 'tcx>(
pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
witnesses: &[DeconstructedPat<'p, 'tcx>],
witnesses: &[WitnessPat<'tcx>],
) -> String {
const LIMIT: usize = 3;
let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_pat(cx).to_string();
match witnesses {
[] => bug!(),
[witness] => format!("`{}`", witness.to_pat(cx)),
@ -916,7 +917,7 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
}
pub(crate) fn pattern_not_covered_label(
witnesses: &[DeconstructedPat<'_, '_>],
witnesses: &[WitnessPat<'_>],
joined_patterns: &str,
) -> String {
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
@ -927,7 +928,7 @@ fn adt_defined_here<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
err: &mut Diagnostic,
ty: Ty<'tcx>,
witnesses: &[DeconstructedPat<'p, 'tcx>],
witnesses: &[WitnessPat<'tcx>],
) {
let ty = ty.peel_refs();
if let ty::Adt(def, _) = ty.kind() {
@ -958,7 +959,7 @@ fn adt_defined_here<'p, 'tcx>(
fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
cx: &MatchCheckCtxt<'p, 'tcx>,
def: AdtDef<'tcx>,
patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
) -> Vec<Span> {
use Constructor::*;
let mut covered = vec![];

View File

@ -629,18 +629,11 @@ pub(super) enum Constructor<'tcx> {
/// `#[doc(hidden)]` ones.
Hidden,
/// Fake extra constructor for constructors that are not seen in the matrix, as explained in the
/// code for [`Constructor::split`]. The carried `bool` is used for the
/// `non_exhaustive_omitted_patterns` lint.
Missing {
nonexhaustive_enum_missing_visible_variants: bool,
},
/// code for [`Constructor::split`].
Missing,
}
impl<'tcx> Constructor<'tcx> {
pub(super) fn is_wildcard(&self) -> bool {
matches!(self, Wildcard)
}
pub(super) fn is_non_exhaustive(&self) -> bool {
matches!(self, NonExhaustive)
}
@ -778,14 +771,8 @@ impl<'tcx> Constructor<'tcx> {
let all_missing = split_set.present.is_empty();
let report_when_all_missing =
pcx.is_top_level && !IntRange::is_integral(pcx.ty);
let ctor = if all_missing && !report_when_all_missing {
Wildcard
} else {
Missing {
nonexhaustive_enum_missing_visible_variants: split_set
.nonexhaustive_enum_missing_visible_variants,
}
};
let ctor =
if all_missing && !report_when_all_missing { Wildcard } else { Missing };
smallvec![ctor]
} else {
split_set.present
@ -905,11 +892,9 @@ pub(super) enum ConstructorSet {
/// either fully included in or disjoint from each constructor in the column. This avoids
/// non-trivial intersections like between `0..10` and `5..15`.
#[derive(Debug)]
struct SplitConstructorSet<'tcx> {
present: SmallVec<[Constructor<'tcx>; 1]>,
missing: Vec<Constructor<'tcx>>,
/// For the `non_exhaustive_omitted_patterns` lint.
nonexhaustive_enum_missing_visible_variants: bool,
pub(super) struct SplitConstructorSet<'tcx> {
pub(super) present: SmallVec<[Constructor<'tcx>; 1]>,
pub(super) missing: Vec<Constructor<'tcx>>,
}
impl ConstructorSet {
@ -1039,7 +1024,7 @@ impl ConstructorSet {
/// constructors to 1/ determine which constructors of the type (if any) are missing; 2/ split
/// constructors to handle non-trivial intersections e.g. on ranges or slices.
#[instrument(level = "debug", skip(self, pcx, ctors), ret)]
fn split<'a, 'tcx>(
pub(super) fn split<'a, 'tcx>(
&self,
pcx: &PatCtxt<'_, '_, 'tcx>,
ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
@ -1051,7 +1036,6 @@ impl ConstructorSet {
let mut missing = Vec::new();
// Constructors in `ctors`, except wildcards.
let mut seen = ctors.filter(|c| !(matches!(c, Opaque | Wildcard)));
let mut nonexhaustive_enum_missing_visible_variants = false;
match self {
ConstructorSet::Single => {
if seen.next().is_none() {
@ -1063,6 +1047,7 @@ impl ConstructorSet {
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
let seen_set: FxHashSet<_> = seen.map(|c| c.as_variant().unwrap()).collect();
let mut skipped_a_hidden_variant = false;
for variant in visible_variants {
let ctor = Variant(*variant);
if seen_set.contains(&variant) {
@ -1071,8 +1056,6 @@ impl ConstructorSet {
missing.push(ctor);
}
}
nonexhaustive_enum_missing_visible_variants =
*non_exhaustive && !missing.is_empty();
for variant in hidden_variants {
let ctor = Variant(*variant);
@ -1159,7 +1142,7 @@ impl ConstructorSet {
ConstructorSet::Uninhabited => {}
}
SplitConstructorSet { present, missing, nonexhaustive_enum_missing_visible_variants }
SplitConstructorSet { present, missing }
}
/// Compute the set of constructors missing from this column.
@ -1312,9 +1295,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
/// Values and patterns can be represented as a constructor applied to some fields. This represents
/// a pattern in this form.
/// This also keeps track of whether the pattern has been found reachable during analysis. For this
/// reason we should be careful not to clone patterns for which we care about that. Use
/// `clone_and_forget_reachability` if you're sure.
/// This also uses interior mutability to keep track of whether the pattern has been found reachable
/// during analysis. For this reason they cannot be cloned.
/// A `DeconstructedPat` will almost always come from user input; the only exception are some
/// `Wildcard`s introduced during specialization.
pub(crate) struct DeconstructedPat<'p, 'tcx> {
ctor: Constructor<'tcx>,
fields: Fields<'p, 'tcx>,
@ -1337,20 +1321,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) }
}
/// Construct a pattern that matches everything that starts with this constructor.
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
/// `Some(_)`.
pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
let fields = Fields::wildcards(pcx, &ctor);
DeconstructedPat::new(ctor, fields, pcx.ty, pcx.span)
}
/// Clone this value. This method emphasizes that cloning loses reachability information and
/// should be done carefully.
pub(super) fn clone_and_forget_reachability(&self) -> Self {
DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span)
}
pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self {
let mkpat = |pat| DeconstructedPat::from_pat(cx, pat);
let ctor;
@ -1533,98 +1503,16 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
DeconstructedPat::new(ctor, fields, pat.ty, pat.span)
}
pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> {
let is_wildcard = |pat: &Pat<'_>| {
matches!(pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
};
let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx)));
let kind = match &self.ctor {
Single | Variant(_) => match self.ty.kind() {
ty::Tuple(..) => PatKind::Leaf {
subpatterns: subpatterns
.enumerate()
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
.collect(),
},
ty::Adt(adt_def, _) if adt_def.is_box() => {
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
// of `std`). So this branch is only reachable when the feature is enabled and
// the pattern is a box pattern.
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
}
ty::Adt(adt_def, args) => {
let variant_index = self.ctor.variant_index_for_adt(*adt_def);
let variant = &adt_def.variant(variant_index);
let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
.zip(subpatterns)
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
.collect();
if adt_def.is_enum() {
PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns }
} else {
PatKind::Leaf { subpatterns }
}
}
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
// be careful to reconstruct the correct constant pattern here. However a string
// literal pattern will never be reported as a non-exhaustiveness witness, so we
// ignore this issue.
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
_ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
},
Slice(slice) => {
match slice.kind {
FixedLen(_) => PatKind::Slice {
prefix: subpatterns.collect(),
slice: None,
suffix: Box::new([]),
},
VarLen(prefix, _) => {
let mut subpatterns = subpatterns.peekable();
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
if slice.array_len.is_some() {
// Improves diagnostics a bit: if the type is a known-size array, instead
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
// This is incorrect if the size is not known, since `[_, ..]` captures
// arrays of lengths `>= 1` whereas `[..]` captures any length.
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
prefix.pop();
}
while subpatterns.peek().is_some()
&& is_wildcard(subpatterns.peek().unwrap())
{
subpatterns.next();
}
}
let suffix: Box<[_]> = subpatterns.collect();
let wild = Pat::wildcard_from_ty(self.ty);
PatKind::Slice {
prefix: prefix.into_boxed_slice(),
slice: Some(Box::new(wild)),
suffix,
}
}
}
}
&Str(value) => PatKind::Constant { value },
IntRange(range) => return range.to_pat(cx.tcx, self.ty),
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
Missing { .. } => bug!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
),
F32Range(..) | F64Range(..) | Opaque | Or => {
bug!("can't convert to pattern: {:?}", self)
}
};
Pat { ty: self.ty, span: DUMMY_SP, kind }
}
pub(super) fn is_or_pat(&self) -> bool {
matches!(self.ctor, Or)
}
pub(super) fn flatten_or_pat(&'p self) -> SmallVec<[&'p Self; 1]> {
if self.is_or_pat() {
self.iter_fields().flat_map(|p| p.flatten_or_pat()).collect()
} else {
smallvec![self]
}
}
pub(super) fn ctor(&self) -> &Constructor<'tcx> {
&self.ctor
@ -1804,3 +1692,131 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
}
}
}
/// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics
/// purposes. As such they don't use interning and can be cloned.
#[derive(Debug, Clone)]
pub(crate) struct WitnessPat<'tcx> {
ctor: Constructor<'tcx>,
pub(crate) fields: Vec<WitnessPat<'tcx>>,
ty: Ty<'tcx>,
}
impl<'tcx> WitnessPat<'tcx> {
pub(super) fn new(ctor: Constructor<'tcx>, fields: Vec<Self>, ty: Ty<'tcx>) -> Self {
Self { ctor, fields, ty }
}
pub(super) fn wildcard(ty: Ty<'tcx>) -> Self {
Self::new(Wildcard, Vec::new(), ty)
}
/// Construct a pattern that matches everything that starts with this constructor.
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
/// `Some(_)`.
pub(super) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, 'tcx>, ctor: Constructor<'tcx>) -> Self {
// Reuse `Fields::wildcards` to get the types.
let fields = Fields::wildcards(pcx, &ctor)
.iter_patterns()
.map(|deco_pat| Self::wildcard(deco_pat.ty()))
.collect();
Self::new(ctor, fields, pcx.ty)
}
pub(super) fn ctor(&self) -> &Constructor<'tcx> {
&self.ctor
}
pub(super) fn ty(&self) -> Ty<'tcx> {
self.ty
}
pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Pat<'tcx> {
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
let mut subpatterns = self.iter_fields().map(|p| Box::new(p.to_pat(cx)));
let kind = match &self.ctor {
Single | Variant(_) => match self.ty.kind() {
ty::Tuple(..) => PatKind::Leaf {
subpatterns: subpatterns
.enumerate()
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
.collect(),
},
ty::Adt(adt_def, _) if adt_def.is_box() => {
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
// of `std`). So this branch is only reachable when the feature is enabled and
// the pattern is a box pattern.
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
}
ty::Adt(adt_def, args) => {
let variant_index = self.ctor.variant_index_for_adt(*adt_def);
let variant = &adt_def.variant(variant_index);
let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
.zip(subpatterns)
.map(|((field, _ty), pattern)| FieldPat { field, pattern })
.collect();
if adt_def.is_enum() {
PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns }
} else {
PatKind::Leaf { subpatterns }
}
}
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
// be careful to reconstruct the correct constant pattern here. However a string
// literal pattern will never be reported as a non-exhaustiveness witness, so we
// ignore this issue.
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
_ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
},
Slice(slice) => {
match slice.kind {
FixedLen(_) => PatKind::Slice {
prefix: subpatterns.collect(),
slice: None,
suffix: Box::new([]),
},
VarLen(prefix, _) => {
let mut subpatterns = subpatterns.peekable();
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
if slice.array_len.is_some() {
// Improves diagnostics a bit: if the type is a known-size array, instead
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
// This is incorrect if the size is not known, since `[_, ..]` captures
// arrays of lengths `>= 1` whereas `[..]` captures any length.
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
prefix.pop();
}
while subpatterns.peek().is_some()
&& is_wildcard(subpatterns.peek().unwrap())
{
subpatterns.next();
}
}
let suffix: Box<[_]> = subpatterns.collect();
let wild = Pat::wildcard_from_ty(self.ty);
PatKind::Slice {
prefix: prefix.into_boxed_slice(),
slice: Some(Box::new(wild)),
suffix,
}
}
}
}
&Str(value) => PatKind::Constant { value },
IntRange(range) => return range.to_pat(cx.tcx, self.ty),
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
Missing { .. } => bug!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
),
F32Range(..) | F64Range(..) | Opaque | Or => {
bug!("can't convert to pattern: {:?}", self)
}
};
Pat { ty: self.ty, span: DUMMY_SP, kind }
}
pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a WitnessPat<'tcx>> {
self.fields.iter()
}
}

View File

@ -213,7 +213,7 @@
//! or-patterns in the first column are expanded before being stored in the matrix. Specialization
//! for a single patstack is done from a combination of [`Constructor::is_covered_by`] and
//! [`PatStack::pop_head_constructor`]. The internals of how it's done mostly live in the
//! [`Fields`] struct.
//! [`super::deconstruct_pat::Fields`] struct.
//!
//!
//! # Computing usefulness
@ -307,7 +307,7 @@
use self::ArmType::*;
use self::Usefulness::*;
use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, Fields};
use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat};
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
use rustc_data_structures::captures::Captures;
@ -322,7 +322,6 @@ use rustc_span::{Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::fmt;
use std::iter::once;
pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
pub(crate) tcx: TyCtxt<'tcx>,
@ -555,20 +554,20 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
/// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of
/// witnesses of non-exhaustiveness when there are any.
/// Which variant to use is dictated by `ArmType`.
#[derive(Debug)]
enum Usefulness<'p, 'tcx> {
#[derive(Debug, Clone)]
enum Usefulness<'tcx> {
/// If we don't care about witnesses, simply remember if the pattern was useful.
NoWitnesses { useful: bool },
/// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
/// pattern is unreachable.
WithWitnesses(Vec<Witness<'p, 'tcx>>),
WithWitnesses(Vec<WitnessStack<'tcx>>),
}
impl<'p, 'tcx> Usefulness<'p, 'tcx> {
impl<'tcx> Usefulness<'tcx> {
fn new_useful(preference: ArmType) -> Self {
match preference {
// A single (empty) witness of reachability.
FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]),
FakeExtraWildcard => WithWitnesses(vec![WitnessStack(vec![])]),
RealArm => NoWitnesses { useful: true },
}
}
@ -605,8 +604,8 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
/// with the results of specializing with the other constructors.
fn apply_constructor(
self,
pcx: &PatCtxt<'_, 'p, 'tcx>,
matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors
pcx: &PatCtxt<'_, '_, 'tcx>,
matrix: &Matrix<'_, 'tcx>, // used to compute missing ctors
ctor: &Constructor<'tcx>,
) -> Self {
match self {
@ -627,25 +626,18 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
// wildcards for fields, i.e. that matches everything that can be built with it.
// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get
// the pattern `Some(_)`.
let new_patterns: Vec<DeconstructedPat<'_, '_>> = missing
let new_patterns: Vec<WitnessPat<'_>> = missing
.into_iter()
.map(|missing_ctor| {
DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())
})
.map(|missing_ctor| WitnessPat::wild_from_ctor(pcx, missing_ctor.clone()))
.collect();
witnesses
.into_iter()
.flat_map(|witness| {
new_patterns.iter().map(move |pat| {
Witness(
witness
.0
.iter()
.chain(once(pat))
.map(DeconstructedPat::clone_and_forget_reachability)
.collect(),
)
let mut stack = witness.clone();
stack.0.push(pat.clone());
stack
})
})
.collect()
@ -667,15 +659,17 @@ enum ArmType {
RealArm,
}
/// A witness of non-exhaustiveness for error reporting, represented
/// as a list of patterns (in reverse order of construction) with
/// wildcards inside to represent elements that can take any inhabitant
/// of the type as a value.
/// A witness-tuple of non-exhaustiveness for error reporting, represented as a list of patterns (in
/// reverse order of construction) with wildcards inside to represent elements that can take any
/// inhabitant of the type as a value.
///
/// A witness against a list of patterns should have the same types
/// and length as the pattern matched against. Because Rust `match`
/// is always against a single pattern, at the end the witness will
/// have length 1, but in the middle of the algorithm, it can contain
/// This mirrors `PatStack`: they function similarly, except `PatStack` contains user patterns we
/// are inspecting, and `WitnessStack` contains witnesses we are constructing.
/// FIXME(Nadrieril): use the same order of patterns for both
///
/// A `WitnessStack` should have the same types and length as the `PatStacks` we are inspecting
/// (except we store the patterns in reverse order). Because Rust `match` is always against a single
/// pattern, at the end the stack will have length 1. In the middle of the algorithm, it can contain
/// multiple patterns.
///
/// For example, if we are constructing a witness for the match against
@ -690,23 +684,37 @@ enum ArmType {
/// # }
/// ```
///
/// We'll perform the following steps:
/// 1. Start with an empty witness
/// `Witness(vec![])`
/// 2. Push a witness `true` against the `false`
/// `Witness(vec![true])`
/// 3. Push a witness `Some(_)` against the `None`
/// `Witness(vec![true, Some(_)])`
/// 4. Apply the `Pair` constructor to the witnesses
/// `Witness(vec![Pair(Some(_), true)])`
/// We'll perform the following steps (among others):
/// - Start with a matrix representing the match
/// `PatStack(vec![Pair(None, _)])`
/// `PatStack(vec![Pair(_, false)])`
/// - Specialize with `Pair`
/// `PatStack(vec![None, _])`
/// `PatStack(vec![_, false])`
/// - Specialize with `Some`
/// `PatStack(vec![_, false])`
/// - Specialize with `_`
/// `PatStack(vec![false])`
/// - Specialize with `true`
/// // no patstacks left
/// - This is a non-exhaustive match: we have the empty witness stack as a witness.
/// `WitnessStack(vec![])`
/// - Apply `true`
/// `WitnessStack(vec![true])`
/// - Apply `_`
/// `WitnessStack(vec![true, _])`
/// - Apply `Some`
/// `WitnessStack(vec![true, Some(_)])`
/// - Apply `Pair`
/// `WitnessStack(vec![Pair(Some(_), true)])`
///
/// The final `Pair(Some(_), true)` is then the resulting witness.
#[derive(Debug)]
pub(crate) struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>);
#[derive(Debug, Clone)]
pub(crate) struct WitnessStack<'tcx>(Vec<WitnessPat<'tcx>>);
impl<'p, 'tcx> Witness<'p, 'tcx> {
impl<'tcx> WitnessStack<'tcx> {
/// Asserts that the witness contains a single pattern, and returns it.
fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> {
fn single_pattern(self) -> WitnessPat<'tcx> {
assert_eq!(self.0.len(), 1);
self.0.into_iter().next().unwrap()
}
@ -724,13 +732,12 @@ impl<'p, 'tcx> Witness<'p, 'tcx> {
///
/// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
fn apply_constructor(mut self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
fn apply_constructor(mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
let pat = {
let len = self.0.len();
let arity = ctor.arity(pcx);
let pats = self.0.drain((len - arity)..).rev();
let fields = Fields::from_iter(pcx.cx, pats);
DeconstructedPat::new(ctor.clone(), fields, pcx.ty, pcx.span)
let fields = self.0.drain((len - arity)..).rev().collect();
WitnessPat::new(ctor.clone(), fields, pcx.ty)
};
self.0.push(pat);
@ -770,7 +777,7 @@ fn is_useful<'p, 'tcx>(
lint_root: HirId,
is_under_guard: bool,
is_top_level: bool,
) -> Usefulness<'p, 'tcx> {
) -> Usefulness<'tcx> {
debug!(?matrix, ?v);
let Matrix { patterns: rows, .. } = matrix;
@ -837,8 +844,6 @@ fn is_useful<'p, 'tcx>(
}
// We split the head constructor of `v`.
let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
let is_non_exhaustive_and_wild =
cx.is_foreign_non_exhaustive_enum(ty) && v_ctor.is_wildcard();
// For each constructor, we compute whether there's a value that starts with it that would
// witness the usefulness of `v`.
let start_matrix = &matrix;
@ -859,50 +864,6 @@ fn is_useful<'p, 'tcx>(
)
});
let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor);
// When all the conditions are met we have a match with a `non_exhaustive` enum
// that has the potential to trigger the `non_exhaustive_omitted_patterns` lint.
// To understand the workings checkout `Constructor::split` and `SplitWildcard::new/into_ctors`
if is_non_exhaustive_and_wild
// Only emit a lint on refutable patterns.
&& cx.refutable
// We check that the match has a wildcard pattern and that wildcard is useful,
// meaning there are variants that are covered by the wildcard. Without the check
// for `witness_preference` the lint would trigger on `if let NonExhaustiveEnum::A = foo {}`
&& usefulness.is_useful() && matches!(witness_preference, RealArm)
&& matches!(
&ctor,
Constructor::Missing { nonexhaustive_enum_missing_visible_variants: true }
)
{
let missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
.compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
// Construct for each missing constructor a "wild" version of this constructor, that
// matches everything that can be built with it. For example, if `ctor` is a
// `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`.
let patterns = missing
.into_iter()
// Because of how we computed `nonexhaustive_enum_missing_visible_variants`,
// this will not return an empty `Vec`.
.filter(|c| !(matches!(c, Constructor::NonExhaustive | Constructor::Hidden)))
.map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
.collect::<Vec<_>>();
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
// is not exhaustive enough.
//
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
cx.tcx.emit_spanned_lint(
NON_EXHAUSTIVE_OMITTED_PATTERNS,
lint_root,
pcx.span,
NonExhaustiveOmittedPattern {
scrut_ty: pcx.ty,
uncovered: Uncovered::new(pcx.span, pcx.cx, patterns),
},
);
}
ret.extend(usefulness);
}
}
@ -914,6 +875,80 @@ fn is_useful<'p, 'tcx>(
ret
}
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
/// in a given column. This traverses patterns column-by-column, where a column is the intuitive
/// notion of "subpatterns that inspect the same subvalue".
/// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the
/// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
column: &[&DeconstructedPat<'p, 'tcx>],
) -> Vec<WitnessPat<'tcx>> {
let ty = column[0].ty();
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor()));
if set.present.is_empty() {
// We can't consistently handle the case where no constructors are present (since this would
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
// so for consistency we refuse to handle the top-level case, where we could handle it.
return vec![];
}
let mut witnesses = Vec::new();
if cx.is_foreign_non_exhaustive_enum(ty) {
witnesses.extend(
set.missing
.into_iter()
// This will list missing visible variants.
.filter(|c| !matches!(c, Constructor::Hidden | Constructor::NonExhaustive))
.map(|missing_ctor| WitnessPat::wild_from_ctor(pcx, missing_ctor)),
)
}
// Recurse into the fields.
for ctor in set.present {
let arity = ctor.arity(pcx);
if arity == 0 {
continue;
}
// We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
// columns may have different lengths in the presence of or-patterns (this is why we can't
// reuse `Matrix`).
let mut specialized_columns: Vec<Vec<_>> = (0..arity).map(|_| Vec::new()).collect();
let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
for pat in relevant_patterns {
let specialized = pat.specialize(pcx, &ctor);
for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) {
if subpat.is_or_pat() {
sub_column.extend(subpat.iter_fields())
} else {
sub_column.push(subpat)
}
}
}
debug_assert!(
!specialized_columns[0].is_empty(),
"ctor {ctor:?} was listed as present but isn't"
);
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
for (i, col_i) in specialized_columns.iter().enumerate() {
// Compute witnesses for each column.
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i.as_slice());
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
// adding enough wildcards to match `arity`.
for wit in wits_for_col_i {
let mut pat = wild_pat.clone();
pat.fields[i] = wit;
witnesses.push(pat);
}
}
}
witnesses
}
/// The arm of a match expression.
#[derive(Clone, Copy, Debug)]
pub(crate) struct MatchArm<'p, 'tcx> {
@ -940,7 +975,7 @@ pub(crate) struct UsefulnessReport<'p, 'tcx> {
pub(crate) arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>,
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
/// exhaustiveness.
pub(crate) non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
pub(crate) non_exhaustiveness_witnesses: Vec<WitnessPat<'tcx>>,
}
/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
@ -954,6 +989,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
arms: &[MatchArm<'p, 'tcx>],
lint_root: HirId,
scrut_ty: Ty<'tcx>,
scrut_span: Span,
) -> UsefulnessReport<'p, 'tcx> {
let mut matrix = Matrix::empty();
let arm_usefulness: Vec<_> = arms
@ -978,9 +1014,39 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
let v = PatStack::from_pattern(wild_pattern);
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, false, true);
let non_exhaustiveness_witnesses = match usefulness {
let non_exhaustiveness_witnesses: Vec<_> = match usefulness {
WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
NoWitnesses { .. } => bug!(),
};
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
if cx.refutable
&& non_exhaustiveness_witnesses.is_empty()
&& !matches!(
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, lint_root).0,
rustc_session::lint::Level::Allow
)
{
let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>();
let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
if !witnesses.is_empty() {
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
// is not exhaustive enough.
//
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
cx.tcx.emit_spanned_lint(
NON_EXHAUSTIVE_OMITTED_PATTERNS,
lint_root,
scrut_span,
NonExhaustiveOmittedPattern {
scrut_ty,
uncovered: Uncovered::new(scrut_span, cx, witnesses),
},
);
}
}
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
}

View File

@ -169,22 +169,22 @@ impl CoverageCounters {
self.bcb_counters[bcb].as_ref()
}
pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option<BcbCounter> {
self.bcb_counters[bcb].take()
}
pub(super) fn drain_bcb_counters(
&mut self,
) -> impl Iterator<Item = (BasicCoverageBlock, BcbCounter)> + '_ {
pub(super) fn bcb_node_counters(
&self,
) -> impl Iterator<Item = (BasicCoverageBlock, &BcbCounter)> {
self.bcb_counters
.iter_enumerated_mut()
.filter_map(|(bcb, counter)| Some((bcb, counter.take()?)))
.iter_enumerated()
.filter_map(|(bcb, counter_kind)| Some((bcb, counter_kind.as_ref()?)))
}
pub(super) fn drain_bcb_edge_counters(
&mut self,
) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), BcbCounter)> + '_ {
self.bcb_edge_counters.drain()
/// For each edge in the BCB graph that has an associated counter, yields
/// that edge's *from* and *to* nodes, and its counter.
pub(super) fn bcb_edge_counters(
&self,
) -> impl Iterator<Item = (BasicCoverageBlock, BasicCoverageBlock, &BcbCounter)> {
self.bcb_edge_counters
.iter()
.map(|(&(from_bcb, to_bcb), counter_kind)| (from_bcb, to_bcb, counter_kind))
}
pub(super) fn take_expressions(&mut self) -> IndexVec<ExpressionId, Expression> {

View File

@ -8,7 +8,7 @@ mod spans;
mod tests;
use self::counters::{BcbCounter, CoverageCounters};
use self::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
use self::graph::CoverageGraph;
use self::spans::CoverageSpans;
use crate::MirPass;
@ -104,7 +104,6 @@ struct Instrumentor<'a, 'tcx> {
function_source_hash: u64,
basic_coverage_blocks: CoverageGraph,
coverage_counters: CoverageCounters,
mappings: Vec<Mapping>,
}
impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
@ -145,7 +144,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
function_source_hash,
basic_coverage_blocks,
coverage_counters,
mappings: Vec::new(),
}
}
@ -168,148 +166,99 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
// and all `Expression` dependencies (operands) are also generated, for any other
// `BasicCoverageBlock`s not already associated with a coverage span.
let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb);
let result = self
.coverage_counters
.make_bcb_counters(&mut self.basic_coverage_blocks, bcb_has_coverage_spans);
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<Mapping> {
let source_map = self.tcx.sess.source_map();
let body_span = self.body_span;
use rustc_session::RemapFileNameExt;
let file_name =
Symbol::intern(&self.source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
for (bcb, spans) in coverage_spans.bcbs_with_coverage_spans() {
let counter_kind = self.coverage_counters.take_bcb_counter(bcb).unwrap_or_else(|| {
bug!("Every BasicCoverageBlock should have a Counter or Expression");
});
let 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 {

View File

@ -2,7 +2,7 @@ use std::cell::OnceCell;
use rustc_data_structures::graph::WithNumNodes;
use rustc_index::IndexVec;
use rustc_middle::mir::{self, AggregateKind, Rvalue, Statement, StatementKind};
use rustc_middle::mir;
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP};
use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
@ -41,13 +41,8 @@ impl CoverageSpans {
!self.bcb_to_spans[bcb].is_empty()
}
pub(super) fn bcbs_with_coverage_spans(
&self,
) -> impl Iterator<Item = (BasicCoverageBlock, &[Span])> {
self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| {
// Only yield BCBs that have at least one coverage span.
(!spans.is_empty()).then_some((bcb, spans.as_slice()))
})
pub(super) fn spans_for_bcb(&self, bcb: BasicCoverageBlock) -> &[Span] {
&self.bcb_to_spans[bcb]
}
}
@ -75,29 +70,15 @@ struct CoverageSpan {
impl CoverageSpan {
pub fn for_fn_sig(fn_sig_span: Span) -> Self {
Self {
span: fn_sig_span,
expn_span: fn_sig_span,
current_macro_or_none: Default::default(),
bcb: START_BCB,
merged_spans: vec![],
is_closure: false,
}
Self::new(fn_sig_span, fn_sig_span, START_BCB, false)
}
pub fn for_statement(
statement: &Statement<'_>,
pub(super) fn new(
span: Span,
expn_span: Span,
bcb: BasicCoverageBlock,
is_closure: bool,
) -> Self {
let is_closure = match statement.kind {
StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => {
matches!(kind, AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _, _))
}
_ => false,
};
Self {
span,
expn_span,
@ -108,17 +89,6 @@ impl CoverageSpan {
}
}
pub fn for_terminator(span: Span, expn_span: Span, bcb: BasicCoverageBlock) -> Self {
Self {
span,
expn_span,
current_macro_or_none: Default::default(),
bcb,
merged_spans: vec![span],
is_closure: false,
}
}
pub fn merge_from(&mut self, mut other: CoverageSpan) {
debug_assert!(self.is_mergeable(&other));
self.span = self.span.to(other.span);

View File

@ -1,5 +1,7 @@
use rustc_data_structures::captures::Captures;
use rustc_middle::mir::{
self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
use rustc_span::Span;
@ -12,7 +14,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
body_span: Span,
basic_coverage_blocks: &CoverageGraph,
) -> Vec<CoverageSpan> {
let mut initial_spans = Vec::<CoverageSpan>::with_capacity(mir_body.basic_blocks.len() * 2);
let mut initial_spans = Vec::with_capacity(mir_body.basic_blocks.len() * 2);
for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
}
@ -50,34 +52,41 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
// `Statement`s and/or `Terminator`s.)
fn bcb_to_initial_coverage_spans(
mir_body: &mir::Body<'_>,
fn bcb_to_initial_coverage_spans<'a, 'tcx>(
mir_body: &'a mir::Body<'tcx>,
body_span: Span,
bcb: BasicCoverageBlock,
bcb_data: &BasicCoverageBlockData,
) -> Vec<CoverageSpan> {
bcb_data
.basic_blocks
.iter()
.flat_map(|&bb| {
let data = &mir_body[bb];
data.statements
.iter()
.filter_map(move |statement| {
filtered_statement_span(statement).map(|span| {
CoverageSpan::for_statement(
statement,
function_source_span(span, body_span),
span,
bcb,
)
})
})
.chain(filtered_terminator_span(data.terminator()).map(|span| {
CoverageSpan::for_terminator(function_source_span(span, body_span), span, bcb)
}))
})
.collect()
bcb_data: &'a BasicCoverageBlockData,
) -> impl Iterator<Item = CoverageSpan> + Captures<'a> + Captures<'tcx> {
bcb_data.basic_blocks.iter().flat_map(move |&bb| {
let data = &mir_body[bb];
let statement_spans = data.statements.iter().filter_map(move |statement| {
let expn_span = filtered_statement_span(statement)?;
let span = function_source_span(expn_span, body_span);
Some(CoverageSpan::new(span, expn_span, bcb, is_closure(statement)))
});
let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| {
let expn_span = filtered_terminator_span(terminator)?;
let span = function_source_span(expn_span, body_span);
Some(CoverageSpan::new(span, expn_span, bcb, false))
});
statement_spans.chain(terminator_span)
})
}
fn is_closure(statement: &Statement<'_>) -> bool {
match statement.kind {
StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind {
AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _, _) => true,
_ => false,
},
_ => false,
}
}
/// If the MIR `Statement` has a span contributive to computing coverage spans,

View File

@ -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(());

View File

@ -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
}

View File

@ -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!()
}
}

View File

@ -20,6 +20,8 @@ use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{ControlFlow, Index};
mod internal;
impl<'tcx> Index<stable_mir::DefId> for Tables<'tcx> {
type Output = DefId;
@ -231,3 +233,11 @@ impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> Index<V
k
}
}
/// Trait used to translate a stable construct to its rustc counterpart.
///
/// This is basically a mirror of [crate::rustc_smir::Stable].
pub(crate) trait RustcInternal<'tcx> {
type T;
fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T;
}

View File

@ -0,0 +1,55 @@
//! Logic required to produce a monomorphic stable body.
//!
//! We first retrieve and monomorphize the rustc body representation, i.e., we generate a
//! monomorphic body using internal representation.
//! After that, we convert the internal representation into a stable one.
use crate::rustc_smir::{Stable, Tables};
use rustc_middle::mir;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::ty::{self, Ty, TyCtxt};
/// Builds a monomorphic body for a given instance.
pub struct BodyBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
instance: ty::Instance<'tcx>,
}
impl<'tcx> BodyBuilder<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self {
BodyBuilder { tcx, instance }
}
pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body {
let mut body = self.tcx.instance_mir(self.instance.def).clone();
let generics = self.tcx.generics_of(self.instance.def_id());
if generics.requires_monomorphization(self.tcx) {
self.visit_body(&mut body);
}
body.stable(tables)
}
fn monomorphize<T>(&self, value: T) -> T
where
T: ty::TypeFoldable<TyCtxt<'tcx>>,
{
self.instance.instantiate_mir_and_normalize_erasing_regions(
self.tcx,
ty::ParamEnv::reveal_all(),
ty::EarlyBinder::bind(value),
)
}
}
impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> {
fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, _location: mir::Location) {
*ct = self.monomorphize(*ct);
}
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: mir::visit::TyContext) {
*ty = self.monomorphize(*ty);
}
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
}

View File

@ -7,7 +7,7 @@
//!
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
use crate::rustc_internal::IndexMap;
use crate::rustc_internal::{IndexMap, RustcInternal};
use crate::rustc_smir::hir::def::DefKind;
use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region};
use rustc_hir as hir;
@ -26,6 +26,7 @@ use stable_mir::{self, opaque, Context, Filename};
use tracing::debug;
mod alloc;
mod builder;
impl<'tcx> Context for Tables<'tcx> {
fn local_crate(&self) -> stable_mir::Crate {
@ -171,8 +172,9 @@ impl<'tcx> Context for Tables<'tcx> {
}
}
fn instance_body(&mut self, _def: InstanceDef) -> Body {
todo!("Monomorphize the body")
fn instance_body(&mut self, def: InstanceDef) -> Body {
let instance = self.instances[def];
builder::BodyBuilder::new(self.tcx, instance).build(self)
}
fn instance_ty(&mut self, def: InstanceDef) -> stable_mir::ty::Ty {
@ -195,9 +197,21 @@ impl<'tcx> Context for Tables<'tcx> {
let def_id = self[def_id];
let generics = self.tcx.generics_of(def_id);
let result = generics.requires_monomorphization(self.tcx);
println!("req {result}: {def_id:?}");
result
}
fn resolve_instance(
&mut self,
def: stable_mir::ty::FnDef,
args: &stable_mir::ty::GenericArgs,
) -> Option<stable_mir::mir::mono::Instance> {
let def_id = def.0.internal(self);
let args_ref = args.internal(self);
match Instance::resolve(self.tcx, ParamEnv::reveal_all(), def_id, args_ref) {
Ok(Some(instance)) => Some(instance.stable(self)),
Ok(None) | Err(_) => None,
}
}
}
#[derive(Clone)]

View File

@ -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"

View File

@ -33,8 +33,8 @@ pub fn analyze_source_file(
(lines, multi_byte_chars, non_narrow_chars)
}
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
cfg_match! {
cfg(any(target_arch = "x86", target_arch = "x86_64")) => {
fn analyze_source_file_dispatch(src: &str,
lines: &mut Vec<RelativeBytePos>,
multi_byte_chars: &mut Vec<MultiByteChar>,
@ -172,8 +172,8 @@ cfg_if::cfg_if! {
non_narrow_chars);
}
}
} else {
}
_ => {
// The target (or compiler version) does not support SSE2 ...
fn analyze_source_file_dispatch(src: &str,
lines: &mut Vec<RelativeBytePos>,

View File

@ -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;

View File

@ -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"],

View File

@ -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
{

View File

@ -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)

View File

@ -39,6 +39,7 @@ pub mod visitor;
pub use error::*;
use mir::mono::Instance;
use ty::{FnDef, GenericArgs};
/// Use String for now but we should replace it.
pub type Symbol = String;
@ -233,6 +234,9 @@ pub trait Context {
/// Item requires monomorphization.
fn requires_monomorphization(&self, def_id: DefId) -> bool;
/// Resolve an instance from the given function definition and generic arguments.
fn resolve_instance(&mut self, def: FnDef, args: &GenericArgs) -> Option<Instance>;
}
// A thread local variable that stores a pointer to the tables mapping between TyCtxt

View File

@ -5,9 +5,11 @@ use crate::{ty::Ty, Span};
#[derive(Clone, Debug)]
pub struct Body {
pub blocks: Vec<BasicBlock>,
pub locals: Vec<LocalDecl>,
pub locals: LocalDecls,
}
type LocalDecls = Vec<LocalDecl>;
#[derive(Clone, Debug)]
pub struct LocalDecl {
pub ty: Ty,
@ -344,6 +346,7 @@ pub enum Operand {
#[derive(Clone, Debug)]
pub struct Place {
pub local: Local,
/// projection out of a place (access a field, deref a pointer, etc)
pub projection: String,
}
@ -462,3 +465,25 @@ pub enum NullOp {
/// Returns the offset of a field.
OffsetOf(Vec<FieldIdx>),
}
impl Operand {
pub fn ty(&self, locals: &LocalDecls) -> Ty {
match self {
Operand::Copy(place) | Operand::Move(place) => place.ty(locals),
Operand::Constant(c) => c.ty(),
}
}
}
impl Constant {
pub fn ty(&self) -> Ty {
self.literal.ty
}
}
impl Place {
pub fn ty(&self, locals: &LocalDecls) -> Ty {
let _start_ty = locals[self.local].ty;
todo!("Implement projection")
}
}

View File

@ -1,5 +1,5 @@
use crate::mir::Body;
use crate::ty::{IndexedVal, Ty};
use crate::ty::{FnDef, GenericArgs, IndexedVal, Ty};
use crate::{with, CrateItem, DefId, Error, Opaque};
use std::fmt::Debug;
@ -41,6 +41,15 @@ impl Instance {
pub fn ty(&self) -> Ty {
with(|context| context.instance_ty(self.def))
}
/// Resolve an instance starting from a function definition and generic arguments.
pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
with(|context| {
context.resolve_instance(def, args).ok_or_else(|| {
crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))
})
})
}
}
/// Try to convert a crate item into an instance.

View File

@ -225,6 +225,7 @@ pub struct ImplDef(pub DefId);
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct RegionDef(pub DefId);
/// A list of generic arguments.
#[derive(Clone, Debug)]
pub struct GenericArgs(pub Vec<GenericArgKind>);

View File

@ -207,7 +207,7 @@ impl<T> Box<T> {
/// ```
/// let five = Box::new(5);
/// ```
#[cfg(all(not(no_global_oom_handling)))]
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]

View File

@ -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")

View File

@ -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
}
}

View File

@ -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

View File

@ -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 #

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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:

View File

@ -100,7 +100,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
## `msrv`
The minimum rust version that the project supports
**Default Value:** `None` (`Option<String>`)
**Default Value:** `Msrv { stack: [] }` (`crate::Msrv`)
---
**Affected lints:**
@ -273,6 +273,16 @@ The minimum number of enum variants for the lints about variant names to trigger
* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
## `struct-field-name-threshold`
The minimum number of struct fields for the lints about field names to trigger
**Default Value:** `3` (`u64`)
---
**Affected lints:**
* [`struct_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_variant_names)
## `enum-variant-size-threshold`
The maximum size of an enum's variant to avoid box suggestion

View File

@ -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};

View File

@ -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)
)
}

View File

@ -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;

View File

@ -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,

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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 */ }}: {}", &param.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 */ }}: {}>", &param.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 */ }}: {}", &param.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 */ }}: {}>", &param.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());
}
}
}

View File

@ -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);
}
}

View File

@ -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));

View File

@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Symbol};
use std::iter;
declare_clippy_lint! {
/// ### What it does
@ -52,12 +53,13 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// This is the opposite of the `iter_without_into_iter` lint.
/// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method.
/// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method
/// on the type or on any of the types in its `Deref` chain.
///
/// ### Why is this bad?
/// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains
/// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).iter()` syntax
/// in case of ambiguity with another `Intoiterator` impl.
/// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).into_iter()` syntax
/// in case of ambiguity with another `IntoIterator` impl.
///
/// ### Example
/// ```rust
@ -102,7 +104,20 @@ fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool {
!matches!(ty.kind, TyKind::OpaqueDef(..))
}
fn type_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
/// Returns the deref chain of a type, starting with the type itself.
fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
iter::successors(Some(ty), |&ty| {
if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
&& implements_trait(cx, ty, deref_did, &[])
{
make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
} else {
None
}
})
}
fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
cx.tcx.inherent_impls(ty_did).iter().any(|&did| {
cx.tcx
@ -127,7 +142,11 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
Mutability::Mut => sym::iter_mut,
Mutability::Not => sym::iter,
}
&& !type_has_inherent_method(cx, ty, expected_method_name)
&& !deref_chain(cx, ty)
.any(|ty| {
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
})
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
if item.ident.name == sym!(IntoIter) {
Some(cx.tcx.hir().impl_item(item.id).expect_type().span)

View File

@ -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, its used in `update_lints`
use crate::utils::conf::metadata::get_configuration_metadata;
use crate::utils::conf::TryConf;
pub use crate::utils::conf::{lookup_conf_file, Conf};
use crate::utils::FindAll;
@ -374,65 +370,13 @@ use crate::utils::FindAll;
/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
///
/// Used in `./src/driver.rs`.
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
let msrv = Msrv::read(&conf.msrv, sess);
let msrv = move || msrv.clone();
let msrv = || conf.msrv.clone();
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
}
#[doc(hidden)]
pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
if let Ok((_, warnings)) = path {
for warning in warnings {
sess.warn(warning.clone());
}
}
let file_name = match path {
Ok((Some(path), _)) => path,
Ok((None, _)) => return Conf::default(),
Err(error) => {
sess.err(format!("error finding Clippy's configuration file: {error}"));
return Conf::default();
},
};
let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name);
// all conf errors are non-fatal, we just use the default conf in case of error
for error in errors {
if let Some(span) = error.span {
sess.span_err(
span,
format!("error reading Clippy's configuration file: {}", error.message),
);
} else {
sess.err(format!(
"error reading Clippy's configuration file `{}`: {}",
file_name.display(),
error.message
));
}
}
for warning in warnings {
if let Some(span) = warning.span {
sess.span_warn(
span,
format!("error reading Clippy's configuration file: {}", warning.message),
);
} else {
sess.warn(format!(
"error reading Clippy's configuration file `{}`: {}",
file_name.display(),
warning.message
));
}
}
conf
}
#[derive(Default)]
struct RegistrationGroups {
all: Vec<LintId>,
@ -558,7 +502,7 @@ fn register_categories(store: &mut rustc_lint::LintStore) {
///
/// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)]
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &'static Conf) {
register_removed_non_tool_lints(store);
register_categories(store);
@ -660,8 +604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
let msrv = Msrv::read(&conf.msrv, sess);
let msrv = move || msrv.clone();
let msrv = || conf.msrv.clone();
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
let allow_expect_in_tests = conf.allow_expect_in_tests;
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
@ -762,6 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
too_many_arguments_threshold,
too_many_lines_threshold,
large_error_threshold,
avoid_breaking_exported_api,
))
});
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
@ -806,7 +750,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
suppress_restriction_lint_in_const,
))
});
store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
@ -851,10 +796,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
))
});
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
let struct_field_name_threshold = conf.struct_field_name_threshold;
let allow_private_module_inception = conf.allow_private_module_inception;
store.register_late_pass(move |_| {
Box::new(enum_variants::EnumVariantNames::new(
Box::new(item_name_repetitions::ItemNameRepetitions::new(
enum_variant_name_threshold,
struct_field_name_threshold,
avoid_breaking_exported_api,
allow_private_module_inception,
))

View File

@ -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";

View File

@ -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};

View File

@ -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);

View File

@ -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,
}

View File

@ -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,
}
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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(

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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"
}

Some files were not shown because too many files have changed in this diff Show More