mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-22 20:03:37 +00:00
Auto merge of #3133 - rust-lang:rustup-2023-10-22, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
f6be93fc61
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
34
Cargo.lock
34
Cargo.lock
@ -564,7 +564,7 @@ dependencies = [
|
||||
"tester",
|
||||
"tokio",
|
||||
"toml 0.7.5",
|
||||
"ui_test 0.20.0",
|
||||
"ui_test",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -614,6 +614,7 @@ dependencies = [
|
||||
"if_chain",
|
||||
"itertools",
|
||||
"rustc-semver",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2511,7 +2512,7 @@ dependencies = [
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"ui_test 0.21.2",
|
||||
"ui_test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3696,7 +3697,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"elsa",
|
||||
"ena",
|
||||
"indexmap 2.0.0",
|
||||
@ -4528,7 +4528,6 @@ dependencies = [
|
||||
name = "rustc_span"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"indexmap 2.0.0",
|
||||
"md-5",
|
||||
"rustc_arena",
|
||||
@ -5740,33 +5739,6 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
|
||||
|
||||
[[package]]
|
||||
name = "ui_test"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd8fb9b15c8332cf51bfc2dc4830063b2446a9c9d732421b56f2478024a3971"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"anyhow",
|
||||
"bstr",
|
||||
"cargo-platform",
|
||||
"cargo_metadata 0.15.4",
|
||||
"color-eyre",
|
||||
"colored",
|
||||
"comma",
|
||||
"crossbeam-channel",
|
||||
"indicatif",
|
||||
"lazy_static",
|
||||
"levenshtein",
|
||||
"prettydiff",
|
||||
"regex",
|
||||
"rustc_version",
|
||||
"rustfix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ui_test"
|
||||
version = "0.21.2"
|
||||
|
14
README.md
14
README.md
@ -11,6 +11,20 @@ standard library, and documentation.
|
||||
If you wish to _contribute_ to the compiler, you should read
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) instead.
|
||||
|
||||
<details>
|
||||
<summary>Table of content</summary>
|
||||
|
||||
- [Quick Start](#quick-start)
|
||||
- [Installing from Source](#installing-from-source)
|
||||
- [Building Documentation](#building-documentation)
|
||||
- [Notes](#notes)
|
||||
- [Getting Help](#getting-help)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
- [Trademark](#trademark)
|
||||
|
||||
</details>
|
||||
|
||||
## Quick Start
|
||||
|
||||
Read ["Installation"] from [The Book].
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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"] }
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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",
|
||||
);
|
||||
|
||||
|
@ -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()];
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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",
|
||||
]
|
@ -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",
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2023-10-09"
|
||||
channel = "nightly-2023-10-21"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools"]
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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<_>>()
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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`");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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" }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>() {}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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),),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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}`" }
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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 {
|
||||
|
@ -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![];
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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(());
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
61
compiler/rustc_smir/src/rustc_internal/internal.rs
Normal file
61
compiler/rustc_smir/src/rustc_internal/internal.rs
Normal 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!()
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
55
compiler/rustc_smir/src/rustc_smir/builder.rs
Normal file
55
compiler/rustc_smir/src/rustc_smir/builder.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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)]
|
||||
|
@ -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"
|
||||
|
@ -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>,
|
||||
|
@ -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;
|
||||
|
@ -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"],
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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>);
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 #
|
||||
|
29
src/ci/scripts/install-awscli.sh
Executable file
29
src/ci/scripts/install-awscli.sh
Executable 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
@ -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};
|
||||
|
@ -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};
|
||||
|
@ -1,50 +1,104 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_in_test_function;
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, HirId};
|
||||
use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use super::IMPL_TRAIT_IN_PARAMS;
|
||||
|
||||
fn report(
|
||||
cx: &LateContext<'_>,
|
||||
param: &GenericParam<'_>,
|
||||
ident: &Ident,
|
||||
generics: &Generics<'_>,
|
||||
first_param_span: Span,
|
||||
) {
|
||||
// No generics with nested generics, and no generics like FnMut(x)
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
param.span,
|
||||
"`impl Trait` used as a function parameter",
|
||||
|diag| {
|
||||
if let Some(gen_span) = generics.span_for_param_suggestion() {
|
||||
// If there's already a generic param with the same bound, do not lint **this** suggestion.
|
||||
diag.span_suggestion_with_style(
|
||||
gen_span,
|
||||
"add a type parameter",
|
||||
format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]),
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
rustc_errors::SuggestionStyle::ShowAlways,
|
||||
);
|
||||
} else {
|
||||
diag.span_suggestion_with_style(
|
||||
Span::new(
|
||||
first_param_span.lo() - rustc_span::BytePos(1),
|
||||
ident.span.hi(),
|
||||
ident.span.ctxt(),
|
||||
ident.span.parent(),
|
||||
),
|
||||
"add a type parameter",
|
||||
format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]),
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
rustc_errors::SuggestionStyle::ShowAlways,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
|
||||
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() && !is_in_test_function(cx.tcx, hir_id)
|
||||
{
|
||||
if let FnKind::ItemFn(ident, generics, _) = kind {
|
||||
if_chain! {
|
||||
if let FnKind::ItemFn(ident, generics, _) = kind;
|
||||
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public();
|
||||
if !is_in_test_function(cx.tcx, hir_id);
|
||||
then {
|
||||
for param in generics.params {
|
||||
if param.is_impl_trait() {
|
||||
// No generics with nested generics, and no generics like FnMut(x)
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
param.span,
|
||||
"'`impl Trait` used as a function parameter'",
|
||||
|diag| {
|
||||
if let Some(gen_span) = generics.span_for_param_suggestion() {
|
||||
diag.span_suggestion_with_style(
|
||||
gen_span,
|
||||
"add a type parameter",
|
||||
format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]),
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
rustc_errors::SuggestionStyle::ShowAlways,
|
||||
);
|
||||
} else {
|
||||
diag.span_suggestion_with_style(
|
||||
Span::new(
|
||||
body.params[0].span.lo() - rustc_span::BytePos(1),
|
||||
ident.span.hi(),
|
||||
ident.span.ctxt(),
|
||||
ident.span.parent(),
|
||||
),
|
||||
"add a type parameter",
|
||||
format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]),
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
rustc_errors::SuggestionStyle::ShowAlways,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
report(cx, param, ident, generics, body.params[0].span);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
|
||||
if_chain! {
|
||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
||||
if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id());
|
||||
if let hir::ItemKind::Impl(impl_) = item.kind;
|
||||
if let hir::Impl { of_trait, .. } = *impl_;
|
||||
if of_trait.is_none();
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public();
|
||||
if !is_in_test_function(cx.tcx, impl_item.hir_id());
|
||||
then {
|
||||
for param in impl_item.generics.params {
|
||||
if param.is_impl_trait() {
|
||||
report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) {
|
||||
if_chain! {
|
||||
if !avoid_breaking_exported_api;
|
||||
if let TraitItemKind::Fn(_, _) = trait_item.kind;
|
||||
if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id());
|
||||
// ^^ (Will always be a trait)
|
||||
if !item.vis_span.is_empty(); // Is public
|
||||
if !is_in_test_function(cx.tcx, trait_item.hir_id());
|
||||
then {
|
||||
for param in trait_item.generics.params {
|
||||
if param.is_impl_trait() {
|
||||
let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1));
|
||||
report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
@ -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)
|
||||
|
@ -50,9 +50,6 @@ extern crate clippy_utils;
|
||||
#[macro_use]
|
||||
extern crate declare_clippy_lint;
|
||||
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::{Lint, LintId};
|
||||
@ -121,7 +118,6 @@ mod empty_structs_with_brackets;
|
||||
mod endian_bytes;
|
||||
mod entry;
|
||||
mod enum_clike;
|
||||
mod enum_variants;
|
||||
mod equatable_if_let;
|
||||
mod error_impl_error;
|
||||
mod escape;
|
||||
@ -166,6 +162,7 @@ mod inline_fn_without_body;
|
||||
mod instant_subtraction;
|
||||
mod int_plus_one;
|
||||
mod invalid_upcast_comparisons;
|
||||
mod item_name_repetitions;
|
||||
mod items_after_statements;
|
||||
mod items_after_test_module;
|
||||
mod iter_not_returning_iterator;
|
||||
@ -362,7 +359,6 @@ mod zero_sized_map_values;
|
||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
use crate::utils::conf::metadata::get_configuration_metadata;
|
||||
use crate::utils::conf::TryConf;
|
||||
pub use crate::utils::conf::{lookup_conf_file, Conf};
|
||||
use crate::utils::FindAll;
|
||||
|
||||
@ -374,65 +370,13 @@ use crate::utils::FindAll;
|
||||
/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
|
||||
///
|
||||
/// Used in `./src/driver.rs`.
|
||||
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
|
||||
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
|
||||
let msrv = Msrv::read(&conf.msrv, sess);
|
||||
let msrv = move || msrv.clone();
|
||||
let msrv = || conf.msrv.clone();
|
||||
|
||||
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
|
||||
if let Ok((_, warnings)) = path {
|
||||
for warning in warnings {
|
||||
sess.warn(warning.clone());
|
||||
}
|
||||
}
|
||||
let file_name = match path {
|
||||
Ok((Some(path), _)) => path,
|
||||
Ok((None, _)) => return Conf::default(),
|
||||
Err(error) => {
|
||||
sess.err(format!("error finding Clippy's configuration file: {error}"));
|
||||
return Conf::default();
|
||||
},
|
||||
};
|
||||
|
||||
let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name);
|
||||
// all conf errors are non-fatal, we just use the default conf in case of error
|
||||
for error in errors {
|
||||
if let Some(span) = error.span {
|
||||
sess.span_err(
|
||||
span,
|
||||
format!("error reading Clippy's configuration file: {}", error.message),
|
||||
);
|
||||
} else {
|
||||
sess.err(format!(
|
||||
"error reading Clippy's configuration file `{}`: {}",
|
||||
file_name.display(),
|
||||
error.message
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for warning in warnings {
|
||||
if let Some(span) = warning.span {
|
||||
sess.span_warn(
|
||||
span,
|
||||
format!("error reading Clippy's configuration file: {}", warning.message),
|
||||
);
|
||||
} else {
|
||||
sess.warn(format!(
|
||||
"error reading Clippy's configuration file `{}`: {}",
|
||||
file_name.display(),
|
||||
warning.message
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
conf
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RegistrationGroups {
|
||||
all: Vec<LintId>,
|
||||
@ -558,7 +502,7 @@ fn register_categories(store: &mut rustc_lint::LintStore) {
|
||||
///
|
||||
/// Used in `./src/driver.rs`.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
|
||||
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &'static Conf) {
|
||||
register_removed_non_tool_lints(store);
|
||||
register_categories(store);
|
||||
|
||||
@ -660,8 +604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
||||
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
||||
|
||||
let msrv = Msrv::read(&conf.msrv, sess);
|
||||
let msrv = move || msrv.clone();
|
||||
let msrv = || conf.msrv.clone();
|
||||
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
|
||||
let allow_expect_in_tests = conf.allow_expect_in_tests;
|
||||
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
|
||||
@ -762,6 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
too_many_arguments_threshold,
|
||||
too_many_lines_threshold,
|
||||
large_error_threshold,
|
||||
avoid_breaking_exported_api,
|
||||
))
|
||||
});
|
||||
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
|
||||
@ -806,7 +750,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
suppress_restriction_lint_in_const,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
|
||||
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
|
||||
store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone())));
|
||||
store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
|
||||
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
|
||||
store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
|
||||
@ -851,10 +796,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
))
|
||||
});
|
||||
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
|
||||
let struct_field_name_threshold = conf.struct_field_name_threshold;
|
||||
let allow_private_module_inception = conf.allow_private_module_inception;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(enum_variants::EnumVariantNames::new(
|
||||
Box::new(item_name_repetitions::ItemNameRepetitions::new(
|
||||
enum_variant_name_threshold,
|
||||
struct_field_name_threshold,
|
||||
avoid_breaking_exported_api,
|
||||
allow_private_module_inception,
|
||||
))
|
||||
|
@ -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";
|
||||
|
@ -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};
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user