diff options
715 files changed, 17822 insertions, 8438 deletions
diff --git a/.gitignore b/.gitignore index aabec0988a9..f2cdd8762f2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ Session.vim *.iml .vscode .project +.vim/ .favorites.json .settings/ .vs/ diff --git a/Cargo.lock b/Cargo.lock index a4b4e49f82c..4bd99b7fbe7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,11 +205,11 @@ dependencies = [ [[package]] name = "ar_archive_writer" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8412a2d690663356cba5a2532f3ed55d1e8090743bc6695b88403b27df67733" +checksum = "3f2bcb7cf51decfbbfc7ef476e28b0775b13e5eb1190f8b7df145cd53d4f4374" dependencies = [ - "object 0.35.0", + "object 0.36.2", ] [[package]] @@ -564,7 +564,7 @@ dependencies = [ "termize", "tokio", "toml 0.7.8", - "ui_test 0.24.0", + "ui_test 0.25.0", "walkdir", ] @@ -572,6 +572,7 @@ dependencies = [ name = "clippy_config" version = "0.1.82" dependencies = [ + "itertools", "rustc-semver", "serde", "toml 0.7.8", @@ -1406,8 +1407,11 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", + "cargo_metadata 0.18.1", + "rinja", "serde", "serde_json", + "thiserror", ] [[package]] @@ -2456,15 +2460,6 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", -] - -[[package]] -name = "object" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" @@ -3103,7 +3098,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d3762e3740cdbf2fd2be465cc2c26d643ad17353cc2e0223d211c1b096118bd" dependencies = [ + "humansize", "itoa", + "num-traits", + "percent-encoding", "rinja_derive", ] @@ -4939,9 +4937,9 @@ dependencies = [ [[package]] name = "spanned" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed14ba8b4b82241bd5daba2c49185d4a0581a0058355fe96537338f002b8605d" +checksum = "86af297923fbcfd107c20a189a6e9c872160df71a7190ae4a7a6c5dce4b2feb6" dependencies = [ "bstr", "color-eyre", @@ -5565,9 +5563,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1c6c78d55482388711c8d417b8e547263046a607512278fed274c54633bbe4" +checksum = "f7e4f339f62edc873975c47115f9e71c5454ddaa37c1142b42fc0b2672c8dacb" dependencies = [ "annotate-snippets 0.11.4", "anyhow", diff --git a/REUSE.toml b/REUSE.toml index 1a30d8016c9..efd70555247 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -163,7 +163,7 @@ SPDX-License-Identifier = "MIT OR Apache-2.0" path = "src/llvm-project/**" precedence = "override" SPDX-FileCopyrightText = [ - "2003-2019 by the contributors listed in [CREDITS.TXT](https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", + "2003-2019 by the contributors listed in CREDITS.TXT (https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", "2010 Apple Inc", "2003-2019 University of Illinois at Urbana-Champaign.", ] diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 1456890a0a2..7af3945d3f9 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1668,7 +1668,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }), )), )), - // FIXME(effects) we might not need a default. default: Some(default_ct), is_host_effect: true, synthetic: true, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7a925705806..a58c7c43246 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1124,8 +1124,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { err.multipart_suggestion( "consider moving the expression out of the loop so it is only moved once", vec![ - (parent.span, "value".to_string()), (span.shrink_to_lo(), format!("let mut value = {value};{indent}")), + (parent.span, "value".to_string()), ], Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 8c9de5210cd..1073ea40694 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -225,21 +225,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Find something that we can name let upper_bound = self.approx_universal_upper_bound(vid); - let upper_bound = &self.definitions[upper_bound]; - match upper_bound.external_name { - Some(reg) => reg, - None => { - // Nothing exact found, so we pick the first one that we find. - let scc = self.constraint_sccs.scc(vid); - for vid in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { - match self.definitions[vid].external_name { - None => {} - Some(region) if region.is_static() => {} - Some(region) => return region, - } - } - region - } + if let Some(universal_region) = self.definitions[upper_bound].external_name { + return universal_region; + } + + // Nothing exact found, so we pick a named upper bound, if there's only one. + // If there's >1 universal region, then we probably are dealing w/ an intersection + // region which cannot be mapped back to a universal. + // FIXME: We could probably compute the LUB if there is one. + let scc = self.constraint_sccs.scc(vid); + let upper_bounds: Vec<_> = self + .rev_scc_graph + .as_ref() + .unwrap() + .upper_bounds(scc) + .filter_map(|vid| self.definitions[vid].external_name) + .filter(|r| !r.is_static()) + .collect(); + match &upper_bounds[..] { + [universal_region] => *universal_region, + _ => region, } } _ => region, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 9c70f7ede8c..ff82f0f2da7 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -555,7 +555,7 @@ fn make_format_args( }; let arg_name = args.explicit_args()[index].kind.ident().unwrap(); ecx.buffered_early_lint.push(BufferedEarlyLint { - span: arg_name.span.into(), + span: Some(arg_name.span.into()), node_id: rustc_ast::CRATE_NODE_ID, lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY), diagnostic: BuiltinLintDiag::NamedArgumentUsedPositionally { diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index 1ed6f8fc359..30dc5cb1615 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -2,13 +2,14 @@ name: Abi-cafe on: - push + - pull_request permissions: {} jobs: abi_cafe: runs-on: ${{ matrix.os }} - timeout-minutes: 60 + timeout-minutes: 30 concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.env.TARGET_TRIPLE }} cancel-in-progress: true @@ -27,12 +28,16 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: x86_64-apple-darwin - - os: windows-latest + - os: macos-latest env: - TARGET_TRIPLE: x86_64-pc-windows-msvc + TARGET_TRIPLE: aarch64-apple-darwin - os: windows-latest env: - TARGET_TRIPLE: x86_64-pc-windows-gnu + TARGET_TRIPLE: x86_64-pc-windows-msvc + # FIXME Currently hangs. Re-enable once this is fixed or abi-cafe adds a timeout. + #- os: windows-latest + # env: + # TARGET_TRIPLE: x86_64-pc-windows-gnu steps: - uses: actions/checkout@v4 diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml b/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml index b4f8ce0f532..27c95572ef8 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml @@ -13,7 +13,6 @@ jobs: - uses: actions/checkout@v4 - run: | sed -i 's/components.*/components = []/' rust-toolchain - echo 'profile = "minimal"' >> rust-toolchain - uses: rustsec/audit-check@v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index a2ae3d63fb9..896a5c34c3e 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -29,7 +29,6 @@ jobs: - name: Avoid installing rustc-dev run: | sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain - echo 'profile = "minimal"' >> rust-toolchain rustfmt -v - name: Rustfmt diff --git a/compiler/rustc_codegen_cranelift/.zed/settings.json b/compiler/rustc_codegen_cranelift/.zed/settings.json new file mode 100644 index 00000000000..e93bed36949 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.zed/settings.json @@ -0,0 +1,68 @@ +{ + "format_on_save": "on", + "lsp": { + "rust-analyzer": { + "initialization_options": { + "diagnostics": { + // in case rustc.source is disabled for performance reasons; disable the errors about this + "disabled": ["unresolved-extern-crate", "unresolved-macro-call"] + }, + "rustc": { + "source": "discover" + }, + "imports": { + "granularity": { + "enforce": true, + "group": "module" + }, + "prefix": "crate" + }, + "cargo": { + "features": ["unstable-features"] + }, + "linkedProjects": [ + "./Cargo.toml", + "./build_system/Cargo.toml", + { + "crates": [ + { + "root_module": "./example/mini_core.rs", + "edition": "2018", + "deps": [], + "cfg": [] + }, + { + "root_module": "./example/mini_core_hello_world.rs", + "edition": "2018", + "deps": [ + { + "crate": 0, + "name": "mini_core" + } + ], + "cfg": [] + }, + { + "root_module": "./example/mod_bench.rs", + "edition": "2018", + "deps": [], + "cfg": [] + } + ] + }, + { + "sysroot_src": "./build/stdlib/library", + "crates": [ + { + "root_module": "./example/std_example.rs", + "edition": "2015", + "deps": [], + "cfg": [] + } + ] + } + ] + } + } + } +} diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index efec5db836b..02d4d98dc42 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -46,21 +46,28 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6b33d7e757a887989eb18b35712b2a67d96171ec3149d1bfb657b29b7b367c" +checksum = "effa84ab2023f7138045ece6b326588c17447ca22e66db71ec15cb0a6c0c4ad2" dependencies = [ "cranelift-entity", ] [[package]] +name = "cranelift-bitset" +version = "0.110.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a1dfc50dca188a15d938867c4400589530bcb0138f7022aae6d059d1d8c309" + +[[package]] name = "cranelift-codegen" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9acf15cb22be42d07c3b57d7856329cb228b7315d385346149df2566ad5e4aa" +checksum = "821c20c639350158ecca928dc2a244d0d1c9cef2377a378fc62a445a286eb1ca" dependencies = [ "bumpalo", "cranelift-bforest", + "cranelift-bitset", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-control", @@ -77,39 +84,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e934d301392b73b3f8b0540391fb82465a0f179a3cee7c726482ac4727efcc97" +checksum = "064473f2fd59b44fa2c9aaa60de1f9c44db5e13521e28bc85d2b92ee535ef625" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb2a2566b3d54b854dfb288b3b187f6d3d17d6f762c92898207eba302931da" +checksum = "d0f39b9ebfd2febdc2acfb9a0fca110665bcd5a6839502576307735ed07b2177" [[package]] name = "cranelift-control" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0100f33b704cdacd01ad66ff41f8c5030d57cbff078e2a4e49ab1822591299fa" +checksum = "94e125c189c3a1ca8dfe209fc6f46edba058a6d24e0b92aff69459a15f4711e7" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8cfdc315e5d18997093e040a8d234bea1ac1e118a716d3e30f40d449e78207b" +checksum = "ea62eb109baec2247e1a6fa7b74c0f584b1e76e289cfd7017385b4b031fc8450" +dependencies = [ + "cranelift-bitset", +] [[package]] name = "cranelift-frontend" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f74b84f16af2e982b0c0c72233503d9d55cbfe3865dbe807ca28dc6642a28b5" +checksum = "722b089357aacb6c7528b2e59a5fe00917d61ce63448b25a3e477a5b7819fac8" dependencies = [ "cranelift-codegen", "log", @@ -119,15 +129,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf306d3dde705fb94bd48082f01d38c4ededc74293a4c007805f610bf08bc6e" +checksum = "c4b5005a48288e7fc2a2991a377831c534e26929b063c379c018060727785a9b" [[package]] name = "cranelift-jit" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c5cfb8bbd3339cd25cca30e7516ff8fe5cb1feeddde6980cc4d5ef34df97bb" +checksum = "f843932baf8d1025c5f114b929eda172d74b7163d058e0de2597c308b567c7e9" dependencies = [ "anyhow", "cranelift-codegen", @@ -145,9 +155,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9b0d4269b36fd858e6d8f20cd4938941186fb831488c361888cb2d6b33a9a6" +checksum = "449819ef1c4af139cf1b9717916fcaea0e23248853d3e95135139773a842d3eb" dependencies = [ "anyhow", "cranelift-codegen", @@ -156,9 +166,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea0ebdef7aff4a79bcbc8b6495f31315f16b3bf311152f472eaa8d679352581" +checksum = "3ae2d48f38081a9e679ad795bd36bb29bedeb5552fc1c195185bf9885fa1b16e" dependencies = [ "cranelift-codegen", "libc", @@ -167,9 +177,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e33439ec20db058bc7cc3410f9748ab1ad90a35cef713d625c736f43e3820d" +checksum = "3a39ee2cfd0ec485eca76f6b4dc17701a280fa406bc05137bb43f1635ed12c9f" dependencies = [ "anyhow", "cranelift-codegen", @@ -279,9 +289,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "object" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "crc32fast", "hashbrown 0.14.5", @@ -411,9 +421,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "22.0.0" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5afe2f0499542f9a4bcfa1b55bfdda803b6ade4e7c93c6b99e0f39dba44b0a91" +checksum = "7fddf3e2980fb1d123d1fcac55189e417fdd3dba4f62139b5a0a1f9efe5669d5" dependencies = [ "anyhow", "cfg-if", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 2969a6cf6ec..a0df502dadc 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.109.0", default-features = false, features = ["std", "unwind", "all-arch"] } -cranelift-frontend = { version = "0.109.0" } -cranelift-module = { version = "0.109.0" } -cranelift-native = { version = "0.109.0" } -cranelift-jit = { version = "0.109.0", optional = true } -cranelift-object = { version = "0.109.0" } +cranelift-codegen = { version = "0.110.1", default-features = false, features = ["std", "unwind", "all-arch"] } +cranelift-frontend = { version = "0.110.1" } +cranelift-module = { version = "0.110.1" } +cranelift-native = { version = "0.110.1" } +cranelift-jit = { version = "0.110.1", optional = true } +cranelift-object = { version = "0.110.1" } target-lexicon = "0.12.0" gimli = { version = "0.28", default-features = false, features = ["write"]} object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 3b3c86a1bd1..6766e2f844d 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -16,7 +16,6 @@ $ rustup component add rustc-codegen-cranelift-preview --toolchain nightly Once it is installed, you can enable it with one of the following approaches: - `CARGO_PROFILE_DEV_CODEGEN_BACKEND=cranelift cargo +nightly build -Zcodegen-backend` -- `RUSTFLAGS="-Zcodegen-backend=cranelift" cargo +nightly build` - Add the following to `.cargo/config.toml`: ```toml [unstable] diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs index 75f9f233cb3..e3f1162445b 100644 --- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs +++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs @@ -1,4 +1,4 @@ -use crate::path::Dirs; +use crate::path::{Dirs, RelPath}; use crate::prepare::GitRepo; use crate::utils::{spawn_and_wait, CargoProject, Compiler}; use crate::{build_sysroot, CodegenBackend, SysrootKind}; @@ -6,8 +6,8 @@ use crate::{build_sysroot, CodegenBackend, SysrootKind}; static ABI_CAFE_REPO: GitRepo = GitRepo::github( "Gankra", "abi-cafe", - "4c6dc8c9c687e2b3a760ff2176ce236872b37212", - "588df6d66abbe105", + "f1220cfd13b57f5c0082c26529163865ee25e115", + "fe93a9acd461425d", "abi-cafe", ); @@ -21,6 +21,7 @@ pub(crate) fn run( rustup_toolchain_name: Option<&str>, bootstrap_host_compiler: &Compiler, ) { + RelPath::DOWNLOAD.ensure_exists(dirs); ABI_CAFE_REPO.fetch(dirs); ABI_CAFE_REPO.patch(dirs); @@ -38,17 +39,23 @@ pub(crate) fn run( eprintln!("Running abi-cafe"); let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"]; - - let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs); - cmd.arg("--"); - cmd.arg("--pairs"); - cmd.args( + let pairs = if cfg!(not(any(target_os = "macos", all(target_os = "windows", target_env = "msvc")))) { &pairs[..] } else { &pairs[..2] - }, - ); + }; + + let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs); + cmd.arg("--"); + + // stdcall, vectorcall and such don't work yet + cmd.arg("--conventions").arg("c").arg("--conventions").arg("rust"); + + for pair in pairs { + cmd.arg("--pairs").arg(pair); + } + cmd.arg("--add-rustc-codegen-backend"); match cg_clif_dylib { CodegenBackend::Local(path) => { @@ -58,6 +65,7 @@ pub(crate) fn run( cmd.arg(format!("cgclif:{name}")); } } + cmd.current_dir(ABI_CAFE.source_dir(dirs)); spawn_and_wait(cmd); diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index ed8b5b906d2..e41f6c5e709 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -312,7 +312,6 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> { let obj = RTSTARTUP_SYSROOT.to_path(dirs).join(format!("{file}.o")); let mut build_rtstartup_cmd = Command::new(&compiler.rustc); build_rtstartup_cmd - .arg("-Ainternal_features") // Missing #[allow(internal_features)] .arg("--target") .arg(&compiler.triple) .arg("--emit=obj") diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index 5525a5f63e9..be0bed0f4e6 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -22,36 +22,6 @@ pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) { assert!(sysroot_src_orig.exists()); apply_patches(dirs, "stdlib", &sysroot_src_orig, &STDLIB_SRC.to_path(dirs)); - - std::fs::write( - STDLIB_SRC.to_path(dirs).join("Cargo.toml"), - r#" -[workspace] -resolver = "1" -members = ["./library/sysroot"] - -[patch.crates-io] -rustc-std-workspace-core = { path = "./library/rustc-std-workspace-core" } -rustc-std-workspace-alloc = { path = "./library/rustc-std-workspace-alloc" } -rustc-std-workspace-std = { path = "./library/rustc-std-workspace-std" } - -# Mandatory for correctly compiling compiler-builtins -[profile.dev.package.compiler_builtins] -debug-assertions = false -overflow-checks = false -codegen-units = 10000 - -[profile.release.package.compiler_builtins] -debug-assertions = false -overflow-checks = false -codegen-units = 10000 -"#, - ) - .unwrap(); - - let source_lockfile = RelPath::PATCHES.to_path(dirs).join("stdlib-lock.toml"); - let target_lockfile = STDLIB_SRC.to_path(dirs).join("Cargo.lock"); - fs::copy(source_lockfile, target_lockfile).unwrap(); } pub(crate) struct GitRepo { diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index afc8a923863..38c3786a94a 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -106,6 +106,7 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ ]); runner.run_out_command("gen_block_iterate", &[]); }), + TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), ]; pub(crate) static RAND_REPO: GitRepo = GitRepo::github( @@ -437,7 +438,7 @@ impl<'a> TestRunner<'a> { cmd.arg("-L"); cmd.arg(format!("crate={}", BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs).display())); cmd.arg("--out-dir"); - cmd.arg(format!("{}", BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs).display())); + cmd.arg(BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs)); cmd.arg("-Cdebuginfo=2"); cmd.arg("--target"); cmd.arg(&self.target_compiler.triple); diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 0b7cac18837..527ec5303b6 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -45,6 +45,7 @@ aot.issue-59326 aot.polymorphize_coroutine aot.neon aot.gen_block_iterate +aot.raw-dylib testsuite.extended_sysroot test.rust-random/rand diff --git a/compiler/rustc_codegen_cranelift/example/raw-dylib.rs b/compiler/rustc_codegen_cranelift/example/raw-dylib.rs new file mode 100644 index 00000000000..4711884f76a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/raw-dylib.rs @@ -0,0 +1,31 @@ +// Tests the raw-dylib feature for Windows. +// https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute + +fn main() { + #[cfg(windows)] + { + #[link(name = "kernel32", kind = "raw-dylib")] + extern "C" { + fn GetModuleFileNameA( + module: *mut std::ffi::c_void, + filename: *mut u8, + size: u32, + ) -> u32; + } + + // Get the filename of the current executable.... + let mut buffer = [0u8; 1024]; + let size = unsafe { + GetModuleFileNameA(core::ptr::null_mut(), buffer.as_mut_ptr(), buffer.len() as u32) + }; + if size == 0 { + eprintln!("failed to get module file name: {}", std::io::Error::last_os_error()); + return; + } else { + // ...and make sure that it matches the test name. + let filename = + std::ffi::CStr::from_bytes_with_nul(&buffer[..size as usize + 1]).unwrap(); + assert!(filename.to_str().unwrap().ends_with("raw-dylib.exe")); + } + } +} diff --git a/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch b/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch deleted file mode 100644 index 77716c51399..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 2b15fee2bb5fd14e34c7e17e44d99cb34f4c555d Mon Sep 17 00:00:00 2001 -From: Afonso Bordado <afonsobordado@az8.co> -Date: Tue, 27 Sep 2022 07:55:17 +0100 -Subject: [PATCH] Disable some test on x86_64-pc-windows-gnu - ---- - src/report.rs | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/src/report.rs b/src/report.rs -index eeec614..f582867 100644 ---- a/src/report.rs -+++ b/src/report.rs -@@ -48,6 +48,15 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl - // - // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES - -+ // x86_64-pc-windows-gnu has some broken i128 tests that aren't disabled by default -+ if cfg!(all(target_os = "windows", target_env = "gnu")) && test.test_name == "ui128" { -+ result.run = Link; -+ result.check = Pass(Link); -+ } else if test.test_name == "ui128" { -+ result.run == Check; -+ result.check = Pass(Check); -+ } -+ - // END OF VENDOR RESERVED AREA - // - // --- -2.30.1.windows.1 - diff --git a/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch b/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch new file mode 100644 index 00000000000..8a2565f1668 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch @@ -0,0 +1,75 @@ +From 236df390f3bc4ed69c26f4d51d584bea246da886 Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Tue, 9 Jul 2024 11:25:14 +0000 +Subject: [PATCH] Disable broken tests + +--- + src/report.rs | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/src/report.rs b/src/report.rs +index 958ab43..dcf1044 100644 +--- a/src/report.rs ++++ b/src/report.rs +@@ -48,6 +48,58 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn Toolchain, callee: &dyn Toolc + // + // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES + ++ if cfg!(all(target_arch = "aarch64", target_os = "linux")) { ++ if test.test == "F32Array" && test.options.convention == CallingConvention::C { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C { ++ result.check = Busted(Check); ++ } ++ } ++ ++ if cfg!(all(target_arch = "aarch64", target_os = "macos")) { ++ if test.test == "SingleVariantUnion" && test.options.convention == CallingConvention::C && test.options.repr == LangRepr::C { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "OptionU128" && test.caller == "rustc" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C { ++ result.check = Busted(Run); ++ } ++ ++ if test.test == "OptionU128" && test.caller == "cgclif" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C { ++ result.check = Busted(Check); ++ } ++ } ++ ++ if cfg!(all(target_arch = "x86_64", unix)) { ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::Rust { ++ result.check = Busted(Run); ++ } ++ } ++ ++ if cfg!(all(target_arch = "x86_64", windows)) { ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust && (test.caller == "rustc" || test.options.repr == LangRepr::Rust) { ++ result.check = Busted(Run); ++ } ++ ++ if test.test == "simple" && test.options.convention == CallingConvention::Rust { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "simple" && test.options.convention == CallingConvention::Rust && test.caller == "rustc" { ++ result.check = Busted(Run); ++ } ++ } ++ ++ if test.test == "f16" || test.test == "f128" { ++ result.run = Skip; ++ } ++ + // END OF VENDOR RESERVED AREA + // + // +-- +2.34.1 + diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch index 7cf7f86700e..8c404956bcc 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch @@ -37,8 +37,8 @@ index 42a26ae..5ac1042 100644 +++ b/lib.rs @@ -1,3 +1,4 @@ +#![cfg(test)] - #![feature(alloc_layout_extra)] - #![feature(array_chunks)] - #![feature(array_ptr_get)] + // tidy-alphabetical-start + #![cfg_attr(bootstrap, feature(offset_of_nested))] + #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -- 2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 271ca12eabb..d579c9588f0 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -11,17 +11,17 @@ Cranelift doesn't support them yet 4 files changed, 4 insertions(+), 50 deletions(-) diff --git a/lib.rs b/lib.rs -index 897a5e9..331f66f 100644 +index 1e336bf..35e6f54 100644 --- a/lib.rs +++ b/lib.rs -@@ -93,7 +93,6 @@ - #![feature(const_option)] - #![feature(const_option_ext)] - #![feature(const_result)] +@@ -1,7 +1,6 @@ + #![cfg(test)] + // tidy-alphabetical-start + #![cfg_attr(bootstrap, feature(offset_of_nested))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] - #![feature(int_roundings)] - #![feature(split_array)] + #![feature(alloc_layout_extra)] + #![feature(array_chunks)] diff --git a/atomic.rs b/atomic.rs index b735957..ea728b6 100644 --- a/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch new file mode 100644 index 00000000000..ada35145e29 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -0,0 +1,25 @@ +From 175d52c5e1779764b66777db1e6f172c2dc365ff Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Fri, 9 Aug 2024 15:44:51 +0000 +Subject: [PATCH] Disable f16 and f128 in compiler-builtins + +--- + library/sysroot/Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml +index 7165c3e48af..968552ad435 100644 +--- a/library/sysroot/Cargo.toml ++++ b/library/sysroot/Cargo.toml +@@ -11,7 +11,7 @@ test = { path = "../test" } + + # Forward features to the `std` crate as necessary + [features] +-default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] ++default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind", "compiler-builtins-no-f16-f128"] + backtrace = ["std/backtrace"] + compiler-builtins-c = ["std/compiler-builtins-c"] + compiler-builtins-mem = ["std/compiler-builtins-mem"] +-- +2.34.1 + diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch deleted file mode 100644 index 584dbdb647f..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 9f65e742ba3e41474e6126c6c4469c48eaa6ca7e Mon Sep 17 00:00:00 2001 -From: Chris Denton <chris@chrisdenton.dev> -Date: Tue, 20 Feb 2024 16:01:40 -0300 -Subject: [PATCH] Don't use raw-dylib in std - ---- - library/std/src/sys/pal/windows/c.rs | 2 +- - library/std/src/sys/pal/windows/rand.rs | 3 +-- - 2 files changed, 2 insertions(+), 3 deletions(-) - -diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs -index ad8e01bfa9b..9ca8e4c16ce 100644 ---- a/library/std/src/sys/pal/windows/c.rs -+++ b/library/std/src/sys/pal/windows/c.rs -@@ -312,7 +312,7 @@ pub unsafe fn NtWriteFile( - - // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. - cfg_if::cfg_if! { --if #[cfg(not(target_vendor = "win7"))] { -+if #[cfg(any())] { - #[cfg(target_arch = "x86")] - #[link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated")] - extern "system" { -diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs -index e427546222a..f2fe42a4d51 100644 ---- a/library/std/src/sys/pal/windows/rand.rs -+++ b/library/std/src/sys/pal/windows/rand.rs -@@ -2,7 +2,7 @@ - - use crate::sys::c; - --#[cfg(not(target_vendor = "win7"))] -+#[cfg(any())] - #[inline] - pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); -@@ -13,7 +13,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { - v - } - --#[cfg(target_vendor = "win7")] - pub fn hashmap_random_keys() -> (u64, u64) { - use crate::ffi::c_void; - use crate::io; --- -2.42.0.windows.2 - diff --git a/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch b/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch deleted file mode 100644 index 21f5ee9cc6e..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0d741cf82c3c908616abd39dc84ebf7d8702e0c3 Mon Sep 17 00:00:00 2001 -From: Chris Denton <chris@chrisdenton.dev> -Date: Tue, 16 Apr 2024 15:51:34 +0000 -Subject: [PATCH] Revert use raw-dylib for Windows futex APIs - ---- - library/std/src/sys/pal/windows/c.rs | 14 +------------- - 1 file changed, 1 insertion(+), 13 deletions(-) - -diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs -index 9d58ce05f01..1c828bac4b6 100644 ---- a/library/std/src/sys/pal/windows/c.rs -+++ b/library/std/src/sys/pal/windows/c.rs -@@ -357,19 +357,7 @@ pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 { - } - - #[cfg(not(target_vendor = "win7"))] --// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library. --#[cfg_attr( -- target_arch = "x86", -- link( -- name = "api-ms-win-core-synch-l1-2-0", -- kind = "raw-dylib", -- import_name_type = "undecorated" -- ) --)] --#[cfg_attr( -- not(target_arch = "x86"), -- link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib") --)] -+#[link(name = "synchronization")] - extern "system" { - pub fn WaitOnAddress( - address: *const c_void, --- -2.42.0.windows.2 - diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml deleted file mode 100644 index 9ea53e8f848..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml +++ /dev/null @@ -1,455 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "compiler_builtins", - "gimli 0.29.0", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "alloc" -version = "0.0.0" -dependencies = [ - "compiler_builtins", - "core", - "rand", - "rand_xorshift", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "cc" -version = "1.0.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "compiler_builtins" -version = "0.1.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ab134a739bafec76aa91ccb15d519a54e569350644a1fea6528d5a0d407e22" -dependencies = [ - "cc", - "rustc-std-workspace-core", -] - -[[package]] -name = "core" -version = "0.0.0" -dependencies = [ - "rand", - "rand_xorshift", -] - -[[package]] -name = "cupid" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bad352a84b567cc38a5854e3aa8ee903cb8519a25d0b799b739bafffd1f91a1" -dependencies = [ - "gcc", - "rustc_version", -] - -[[package]] -name = "dlmalloc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" -dependencies = [ - "compiler_builtins", - "libc", - "rustc-std-workspace-core", -] - -[[package]] -name = "fortanix-sgx-abi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "rustc-std-workspace-core", - "rustc-std-workspace-std", - "unicode-width", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "allocator-api2", - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -dependencies = [ - "rustc-std-workspace-core", -] - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "object" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" -dependencies = [ - "compiler_builtins", - "memchr", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "panic_abort" -version = "0.0.0" -dependencies = [ - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "libc", -] - -[[package]] -name = "panic_unwind" -version = "0.0.0" -dependencies = [ - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "libc", - "unwind", -] - -[[package]] -name = "proc_macro" -version = "0.0.0" -dependencies = [ - "core", - "std", -] - -[[package]] -name = "profiler_builtins" -version = "0.0.0" -dependencies = [ - "cc", - "compiler_builtins", - "core", -] - -[[package]] -name = "r-efi" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e244f96e03a3067f9e521d3167bd42657594cb8588c8d3a2db01545dc1af2e0" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "r-efi-alloc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" -dependencies = [ - "compiler_builtins", - "r-efi", - "rustc-std-workspace-core", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "rustc-std-workspace-alloc" -version = "1.99.0" -dependencies = [ - "alloc", -] - -[[package]] -name = "rustc-std-workspace-core" -version = "1.99.0" -dependencies = [ - "core", -] - -[[package]] -name = "rustc-std-workspace-std" -version = "1.99.0" -dependencies = [ - "std", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "std" -version = "0.0.0" -dependencies = [ - "addr2line", - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "dlmalloc", - "fortanix-sgx-abi", - "hashbrown", - "hermit-abi", - "libc", - "miniz_oxide", - "object", - "panic_abort", - "panic_unwind", - "profiler_builtins", - "r-efi", - "r-efi-alloc", - "rand", - "rand_xorshift", - "rustc-demangle", - "std_detect", - "unwind", - "wasi", -] - -[[package]] -name = "std_detect" -version = "0.1.5" -dependencies = [ - "cfg-if", - "compiler_builtins", - "cupid", - "libc", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "sysroot" -version = "0.0.0" -dependencies = [ - "proc_macro", - "std", - "test", -] - -[[package]] -name = "test" -version = "0.0.0" -dependencies = [ - "core", - "getopts", - "libc", - "std", -] - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", - "rustc-std-workspace-std", -] - -[[package]] -name = "unwind" -version = "0.0.0" -dependencies = [ - "cfg-if", - "compiler_builtins", - "core", - "libc", - "unwinding", -] - -[[package]] -name = "unwinding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" -dependencies = [ - "compiler_builtins", - "gimli 0.28.1", - "rustc-std-workspace-core", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index db9b551bd2a..96c467e091c 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,4 @@ [toolchain] -channel = "nightly-2024-07-13" +channel = "nightly-2024-08-09" components = ["rust-src", "rustc-dev", "llvm-tools"] +profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index f0550c23b17..bb5af9127b9 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -39,6 +39,7 @@ rm tests/ui/simd/dont-invalid-bitcast-x86_64.rs # unimplemented llvm.x86.sse41.r # exotic linkages rm tests/incremental/hashes/function_interfaces.rs rm tests/incremental/hashes/statics.rs +rm -r tests/run-make/naked-symbol-visibility # variadic arguments rm tests/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs @@ -118,6 +119,7 @@ rm tests/ui/mir/mir_misc_casts.rs # depends on deduplication of constants rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same +rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended @@ -134,6 +136,8 @@ rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation war # bugs in the test suite # ====================== rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue +rm tests/ui/backtrace/synchronized-panic-handler.rs # missing needs-unwind annotation +rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd @@ -157,8 +161,8 @@ index ea06b620c4c..b969d0009c6 100644 RUSTDOC := \$(RUSTDOC) -Clinker='\$(RUSTC_LINKER)' diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index 9607ff02f96..b7d97caf9a2 100644 ---- a/src/tools/run-make-support/src/rustdoc.rs -+++ b/src/tools/run-make-support/src/rustdoc.rs +--- a/src/tools/run-make-support/src/external_deps/rustdoc.rs ++++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs @@ -34,8 +34,6 @@ pub fn bare() -> Self { #[track_caller] pub fn new() -> Self { diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 1935005a08c..5eedab4f2cb 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -1,8 +1,13 @@ -use std::path::{Path, PathBuf}; +use std::borrow::Borrow; +use std::fs; +use std::path::Path; +use ar_archive_writer::{COFFShortExport, MachineTypes}; use rustc_codegen_ssa::back::archive::{ - ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, + create_mingw_dll_import_lib, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, + DEFAULT_OBJECT_READER, }; +use rustc_codegen_ssa::common::is_mingw_gnu_toolchain; use rustc_session::Session; pub(crate) struct ArArchiveBuilderBuilder; @@ -15,11 +20,74 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { fn create_dll_import_lib( &self, sess: &Session, - _lib_name: &str, - _dll_imports: &[rustc_session::cstore::DllImport], - _tmpdir: &Path, - _is_direct_dependency: bool, - ) -> PathBuf { - sess.dcx().fatal("raw-dylib is not yet supported by rustc_codegen_cranelift"); + lib_name: &str, + import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + output_path: &Path, + ) { + if is_mingw_gnu_toolchain(&sess.target) { + // The binutils linker used on -windows-gnu targets cannot read the import + // libraries generated by LLVM: in our attempts, the linker produced an .EXE + // that loaded but crashed with an AV upon calling one of the imported + // functions. Therefore, use binutils to create the import library instead, + // by writing a .DEF file to the temp dir and calling binutils's dlltool. + create_mingw_dll_import_lib( + sess, + lib_name, + import_name_and_ordinal_vector, + output_path, + ); + } else { + let mut file = + match fs::OpenOptions::new().write(true).create_new(true).open(&output_path) { + Ok(file) => file, + Err(error) => { + sess.dcx().fatal(format!( + "failed to create import library file `{path}`: {error}", + path = output_path.display(), + )); + } + }; + + let machine = match sess.target.arch.borrow() { + "x86" => MachineTypes::I386, + "x86_64" => MachineTypes::AMD64, + "arm" => MachineTypes::ARMNT, + "aarch64" => MachineTypes::ARM64, + _ => { + sess.dcx().fatal(format!( + "unsupported target architecture `{arch}`", + arch = sess.target.arch, + )); + } + }; + + let exports = import_name_and_ordinal_vector + .iter() + .map(|(name, ordinal)| COFFShortExport { + name: name.to_string(), + ext_name: None, + symbol_name: None, + alias_target: None, + ordinal: ordinal.unwrap_or(0), + noname: ordinal.is_some(), + data: false, + private: false, + constant: false, + }) + .collect::<Vec<_>>(); + + if let Err(error) = ar_archive_writer::write_import_library( + &mut file, + lib_name, + &exports, + machine, + !sess.target.is_like_msvc, + ) { + sess.dcx().fatal(format!( + "failed to create import library `{path}`: `{error}`", + path = output_path.display(), + )); + } + } } } diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index e16b77648d1..b6a4769e031 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -23,19 +23,7 @@ pub(crate) fn maybe_codegen<'tcx>( match bin_op { BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None, BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None, - BinOp::Mul | BinOp::MulUnchecked => { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret_val = fx.lib_call( - "__multi3", - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I128)], - &args, - )[0]; - Some(CValue::by_val( - ret_val, - fx.layout_of(if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }), - )) - } + BinOp::Mul | BinOp::MulUnchecked => None, BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div | BinOp::Rem => { let name = match (bin_op, is_signed) { @@ -92,6 +80,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( match bin_op { BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(), + BinOp::Add | BinOp::Sub => None, BinOp::Mul if is_signed => { let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32)); @@ -112,7 +101,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( let oflow = fx.bcx.ins().ireduce(types::I8, oflow); Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty))) } - BinOp::Add | BinOp::Sub | BinOp::Mul => { + BinOp::Mul => { let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); let param_types = vec![ @@ -121,15 +110,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( AbiParam::new(types::I128), ]; let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let name = match (bin_op, is_signed) { - (BinOp::Add, false) => "__rust_u128_addo", - (BinOp::Add, true) => "__rust_i128_addo", - (BinOp::Sub, false) => "__rust_u128_subo", - (BinOp::Sub, true) => "__rust_i128_subo", - (BinOp::Mul, false) => "__rust_u128_mulo", - _ => unreachable!(), - }; - fx.lib_call(name, param_types, vec![], &args); + fx.lib_call("__rust_u128_mulo", param_types, vec![], &args); Some(out_place.to_cvalue(fx)) } BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 09317139936..a1b29a42225 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -107,7 +107,7 @@ pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { return false; } - let tail = tcx.struct_tail_erasing_lifetimes(ty, ParamEnv::reveal_all()); + let tail = tcx.struct_tail_for_codegen(ty, ParamEnv::reveal_all()); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index f3b963200a0..4154a62234c 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -38,18 +38,12 @@ builtin_functions! { register_functions_for_jit; // integers - fn __multi3(a: i128, b: i128) -> i128; fn __muloti4(n: i128, d: i128, oflow: &mut i32) -> i128; fn __udivti3(n: u128, d: u128) -> u128; fn __divti3(n: i128, d: i128) -> i128; fn __umodti3(n: u128, d: u128) -> u128; fn __modti3(n: i128, d: i128) -> i128; - fn __rust_u128_addo(a: u128, b: u128) -> (u128, bool); - fn __rust_i128_addo(a: i128, b: i128) -> (i128, bool); - fn __rust_u128_subo(a: u128, b: u128) -> (u128, bool); - fn __rust_i128_subo(a: i128, b: i128) -> (i128, bool); fn __rust_u128_mulo(a: u128, b: u128) -> (u128, bool); - fn __rust_i128_mulo(a: i128, b: i128) -> (i128, bool); // floats fn __floattisf(i: i128) -> f32; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index a20faa2cad3..cb003037c26 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -169,39 +169,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( } } - "llvm.x86.sse.add.ss" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_ss&ig_expand=171 - intrinsic_args!(fx, args => (a, b); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), ret.layout()); - let layout = a.layout(); - - let (_, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - let ret_lane_layout = fx.layout_of(lane_ty); - - ret.write_cvalue(fx, a); - - let a_lane = a.value_lane(fx, 0).load_scalar(fx); - let b_lane = b.value_lane(fx, 0).load_scalar(fx); - - let res = fx.bcx.ins().fadd(a_lane, b_lane); - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, 0).write_cvalue(fx, res_lane); - } - - "llvm.x86.sse.sqrt.ps" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_ps&ig_expand=6245 - intrinsic_args!(fx, args => (a); intrinsic); - - // FIXME use vector instructions when possible - simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { - fx.bcx.ins().sqrt(lane) - }); - } - "llvm.x86.sse.max.ps" => { // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_ps&ig_expand=4357 intrinsic_args!(fx, args => (a, b); intrinsic); @@ -744,117 +711,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( pack_instruction(fx, a, b, ret, PackSize::S16, PackWidth::Avx); } - "llvm.x86.fma.vfmaddsub.ps" - | "llvm.x86.fma.vfmaddsub.pd" - | "llvm.x86.fma.vfmaddsub.ps.256" - | "llvm.x86.fma.vfmaddsub.pd.256" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmaddsub_ps&ig_expand=3205 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmaddsub_pd&ig_expand=3181 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmaddsub_ps&ig_expand=3209 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmaddsub_pd&ig_expand=3185 - intrinsic_args!(fx, args => (a, b, c); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), c.layout()); - let layout = a.layout(); - - let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - assert!(ret_lane_ty.is_floating_point()); - assert_eq!(lane_count, ret_lane_count); - let ret_lane_layout = fx.layout_of(ret_lane_ty); - - for idx in 0..lane_count { - let a_lane = a.value_lane(fx, idx).load_scalar(fx); - let b_lane = b.value_lane(fx, idx).load_scalar(fx); - let c_lane = c.value_lane(fx, idx).load_scalar(fx); - - let mul = fx.bcx.ins().fmul(a_lane, b_lane); - let res = if idx & 1 == 0 { - fx.bcx.ins().fsub(mul, c_lane) - } else { - fx.bcx.ins().fadd(mul, c_lane) - }; - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, idx).write_cvalue(fx, res_lane); - } - } - - "llvm.x86.fma.vfmsubadd.ps" - | "llvm.x86.fma.vfmsubadd.pd" - | "llvm.x86.fma.vfmsubadd.ps.256" - | "llvm.x86.fma.vfmsubadd.pd.256" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmsubadd_ps&ig_expand=3325 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmsubadd_pd&ig_expand=3301 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmsubadd_ps&ig_expand=3329 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmsubadd_pd&ig_expand=3305 - intrinsic_args!(fx, args => (a, b, c); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), c.layout()); - let layout = a.layout(); - - let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - assert!(ret_lane_ty.is_floating_point()); - assert_eq!(lane_count, ret_lane_count); - let ret_lane_layout = fx.layout_of(ret_lane_ty); - - for idx in 0..lane_count { - let a_lane = a.value_lane(fx, idx).load_scalar(fx); - let b_lane = b.value_lane(fx, idx).load_scalar(fx); - let c_lane = c.value_lane(fx, idx).load_scalar(fx); - - let mul = fx.bcx.ins().fmul(a_lane, b_lane); - let res = if idx & 1 == 0 { - fx.bcx.ins().fadd(mul, c_lane) - } else { - fx.bcx.ins().fsub(mul, c_lane) - }; - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, idx).write_cvalue(fx, res_lane); - } - } - - "llvm.x86.fma.vfnmadd.ps" - | "llvm.x86.fma.vfnmadd.pd" - | "llvm.x86.fma.vfnmadd.ps.256" - | "llvm.x86.fma.vfnmadd.pd.256" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fnmadd_ps&ig_expand=3391 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fnmadd_pd&ig_expand=3367 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fnmadd_ps&ig_expand=3395 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fnmadd_pd&ig_expand=3371 - intrinsic_args!(fx, args => (a, b, c); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), c.layout()); - let layout = a.layout(); - - let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - assert!(ret_lane_ty.is_floating_point()); - assert_eq!(lane_count, ret_lane_count); - let ret_lane_layout = fx.layout_of(ret_lane_ty); - - for idx in 0..lane_count { - let a_lane = a.value_lane(fx, idx).load_scalar(fx); - let b_lane = b.value_lane(fx, idx).load_scalar(fx); - let c_lane = c.value_lane(fx, idx).load_scalar(fx); - - let mul = fx.bcx.ins().fmul(a_lane, b_lane); - let neg_mul = fx.bcx.ins().fneg(mul); - let res = fx.bcx.ins().fadd(neg_mul, c_lane); - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, idx).write_cvalue(fx, res_lane); - } - } - "llvm.x86.sse42.crc32.32.8" | "llvm.x86.sse42.crc32.32.16" | "llvm.x86.sse42.crc32.32.32" diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 9d46d8d6dac..21930fa2ddb 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -12,6 +12,7 @@ #![warn(unused_lifetimes)] // tidy-alphabetical-end +extern crate ar_archive_writer; extern crate jobserver; #[macro_use] extern crate rustc_middle; @@ -190,9 +191,20 @@ impl CodegenBackend for CraneliftCodegenBackend { if sess.target.arch == "x86_64" && sess.target.os != "none" { // x86_64 mandates SSE2 support vec![Symbol::intern("fxsr"), sym::sse, Symbol::intern("sse2")] - } else if sess.target.arch == "aarch64" && sess.target.os != "none" { - // AArch64 mandates Neon support - vec![sym::neon] + } else if sess.target.arch == "aarch64" { + match &*sess.target.os { + "none" => vec![], + // On macOS the aes, sha2 and sha3 features are enabled by default and ring + // fails to compile on macOS when they are not present. + "macos" => vec![ + sym::neon, + Symbol::intern("aes"), + Symbol::intern("sha2"), + Symbol::intern("sha3"), + ], + // AArch64 mandates Neon support + _ => vec![sym::neon], + } } else { vec![] } diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 967aa53abbd..e09cd16e89a 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -22,7 +22,7 @@ pub(crate) fn unsized_info<'tcx>( old_info: Option<Value>, ) -> Value { let (source, target) = - fx.tcx.struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all()); + fx.tcx.struct_lockstep_tails_for_codegen(source, target, ParamEnv::reveal_all()); match (&source.kind(), &target.kind()) { (&ty::Array(_, len), &ty::Slice(_)) => fx .bcx diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 1aa28daeafc..8eb2095e523 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -677,8 +677,10 @@ impl<'tcx> CPlace<'tcx> { let to_addr = to_ptr.get_addr(fx); let src_layout = from.1; let size = dst_layout.size.bytes(); - let src_align = src_layout.align.abi.bytes() as u8; - let dst_align = dst_layout.align.abi.bytes() as u8; + // `emit_small_memory_copy` uses `u8` for alignments, just use the maximum + // alignment that fits in a `u8` if the actual alignment is larger. + let src_align = src_layout.align.abi.bytes().try_into().unwrap_or(128); + let dst_align = dst_layout.align.abi.bytes().try_into().unwrap_or(128); fx.bcx.emit_small_memory_copy( fx.target_config, to_addr, diff --git a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock index d6ec1f87d01..771f2f18dce 100644 --- a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock @@ -50,7 +50,7 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.109" +version = "0.1.118" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f11973008a8cf741fe6d22f339eba21fd0ca81e2760a769ba8243ed6c21edd7e" dependencies = [ diff --git a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml index e4669923623..05503128f2a 100644 --- a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml +++ b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml @@ -6,9 +6,7 @@ resolver = "2" [dependencies] core = { path = "./sysroot_src/library/core" } -# TODO: after the sync, revert to using version 0.1. -# compiler_builtins = "0.1" -compiler_builtins = "=0.1.109" +compiler_builtins = "0.1" alloc = { path = "./sysroot_src/library/alloc" } std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] } test = { path = "./sysroot_src/library/test" } diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 36f5e0f8094..0cee05f1cea 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -1,9 +1,8 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use rustc_codegen_ssa::back::archive::{ ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, }; -use rustc_session::cstore::DllImport; use rustc_session::Session; pub(crate) struct ArArchiveBuilderBuilder; @@ -17,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { &self, _sess: &Session, _lib_name: &str, - _dll_imports: &[DllImport], - _tmpdir: &Path, - _is_direct_dependency: bool, - ) -> PathBuf { + _import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + _output_path: &Path, + ) { unimplemented!("creating dll imports is not yet supported"); } } diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index e521551304e..5fdf2680aac 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -75,7 +75,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let function_features = codegen_fn_attrs .target_features .iter() - .map(|features| features.as_str()) + .map(|features| features.name.as_str()) .collect::<Vec<&str>>(); if let Some(features) = check_tied_features( diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 8bb90efe6fb..5308ccdb614 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -65,8 +65,8 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri let feature = backend_feature_name(s)?; // Warn against use of GCC specific feature names on the CLI. - if diagnostics && !supported_features.iter().any(|&(v, _)| v == feature) { - let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| { + if diagnostics && !supported_features.iter().any(|&(v, _, _)| v == feature) { + let rust_feature = supported_features.iter().find_map(|&(rust_feature, _, _)| { let gcc_features = to_gcc_features(sess, rust_feature); if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) { Some(rust_feature) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index cd63a405b67..94f016234f9 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -486,7 +486,7 @@ pub fn target_features( sess.target .supported_target_features() .iter() - .filter_map(|&(feature, gate)| { + .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 1c126e79762..267da9325c3 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -1,23 +1,13 @@ codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} -codegen_llvm_dlltool_fail_import_library = - Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: - {$stdout} - {$stderr} - codegen_llvm_dynamic_linking_with_lto = cannot prefer dynamic linking when performing LTO .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO -codegen_llvm_error_calling_dlltool = - Error calling dlltool '{$dlltool_path}': {$error} codegen_llvm_error_creating_import_library = Error creating import library for {$lib_name}: {$error} -codegen_llvm_error_writing_def_file = - Error writing .DEF file: {$error} - codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_from_llvm_diag = {$message} diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index b8f42031263..1277b7898c2 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -5,10 +5,10 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::MemFlags; -use rustc_middle::bug; use rustc_middle::ty::layout::LayoutOf; pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::Ty; +use rustc_middle::{bug, ty}; use rustc_session::config; pub use rustc_target::abi::call::*; use rustc_target::abi::{self, HasDataLayout, Int, Size}; @@ -16,6 +16,7 @@ pub use rustc_target::spec::abi::Abi; use rustc_target::spec::SanitizerSet; use smallvec::SmallVec; +use crate::attributes::llfn_attrs_from_instance; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Attribute, AttributePlace}; @@ -310,7 +311,16 @@ pub trait FnAbiLlvmExt<'ll, 'tcx> { fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn llvm_cconv(&self) -> llvm::CallConv; - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value); + + /// Apply attributes to a function declaration/definition. + fn apply_attrs_llfn( + &self, + cx: &CodegenCx<'ll, 'tcx>, + llfn: &'ll Value, + instance: Option<ty::Instance<'tcx>>, + ); + + /// Apply attributes to a function call. fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value); } @@ -396,7 +406,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { self.conv.into() } - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { + fn apply_attrs_llfn( + &self, + cx: &CodegenCx<'ll, 'tcx>, + llfn: &'ll Value, + instance: Option<ty::Instance<'tcx>>, + ) { let mut func_attrs = SmallVec::<[_; 3]>::new(); if self.ret.layout.abi.is_uninhabited() { func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx)); @@ -477,6 +492,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } } + + // If the declaration has an associated instance, compute extra attributes based on that. + if let Some(instance) = instance { + llfn_attrs_from_instance(cx, llfn, instance); + } } fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index ad38814a68b..fde95104093 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -324,9 +324,10 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") } +/// Helper for `FnAbi::apply_attrs_llfn`: /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. -pub fn from_fn_attrs<'ll, 'tcx>( +pub fn llfn_attrs_from_instance<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>, @@ -496,7 +497,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.extend(tune_cpu_attr(cx)); let function_features = - codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::<Vec<&str>>(); + codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>(); if let Some(f) = llvm_util::check_tied_features( cx.tcx.sess, diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2f5a864d70e..a2ab19ac800 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,21 +1,19 @@ //! A helper class for dealing with static archives -use std::ffi::{c_char, c_void, CStr, CString, OsString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::path::{Path, PathBuf}; -use std::{env, io, mem, ptr, str}; +use std::{io, mem, ptr, str}; use rustc_codegen_ssa::back::archive::{ - try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, - ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER, + create_mingw_dll_import_lib, try_extract_macho_fat_archive, ArArchiveBuilder, + ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, + DEFAULT_OBJECT_READER, }; -use rustc_session::cstore::DllImport; +use rustc_codegen_ssa::common; use rustc_session::Session; use tracing::trace; -use crate::common; -use crate::errors::{ - DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile, -}; +use crate::errors::ErrorCreatingImportLibrary; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; @@ -121,116 +119,21 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf { - let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; - let output_path = tmpdir.join(format!("{lib_name}{name_suffix}.lib")); - - let target = &sess.target; - let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target); - - let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports - .iter() - .map(|import: &DllImport| { - if sess.target.arch == "x86" { - ( - common::i686_decorated_name(import, mingw_gnu_toolchain, false), - import.ordinal(), - ) - } else { - (import.name.to_string(), import.ordinal()) - } - }) - .collect(); - - if mingw_gnu_toolchain { + import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + output_path: &Path, + ) { + if common::is_mingw_gnu_toolchain(&sess.target) { // The binutils linker used on -windows-gnu targets cannot read the import // libraries generated by LLVM: in our attempts, the linker produced an .EXE // that loaded but crashed with an AV upon calling one of the imported // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. - let def_file_path = tmpdir.join(format!("{lib_name}{name_suffix}.def")); - - let def_file_content = format!( - "EXPORTS\n{}", - import_name_and_ordinal_vector - .into_iter() - .map(|(name, ordinal)| { - match ordinal { - Some(n) => format!("{name} @{n} NONAME"), - None => name, - } - }) - .collect::<Vec<String>>() - .join("\n") + create_mingw_dll_import_lib( + sess, + lib_name, + import_name_and_ordinal_vector, + output_path, ); - - match std::fs::write(&def_file_path, def_file_content) { - Ok(_) => {} - Err(e) => { - sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); - } - }; - - // --no-leading-underscore: For the `import_name_type` feature to work, we need to be - // able to control the *exact* spelling of each of the symbols that are being imported: - // hence we don't want `dlltool` adding leading underscores automatically. - let dlltool = find_binutils_dlltool(sess); - let temp_prefix = { - let mut path = PathBuf::from(&output_path); - path.pop(); - path.push(lib_name); - path - }; - // dlltool target architecture args from: - // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 - let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { - "x86_64" => ("i386:x86-64", "--64"), - "x86" => ("i386", "--32"), - "aarch64" => ("arm64", "--64"), - "arm" => ("arm", "--32"), - _ => panic!("unsupported arch {}", sess.target.arch), - }; - let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd - .arg("-d") - .arg(def_file_path) - .arg("-D") - .arg(lib_name) - .arg("-l") - .arg(&output_path) - .arg("-m") - .arg(dlltool_target_arch) - .arg("-f") - .arg(dlltool_target_bitness) - .arg("--no-leading-underscore") - .arg("--temp-prefix") - .arg(temp_prefix); - - match dlltool_cmd.output() { - Err(e) => { - sess.dcx().emit_fatal(ErrorCallingDllTool { - dlltool_path: dlltool.to_string_lossy(), - error: e, - }); - } - // dlltool returns '0' on failure, so check for error output instead. - Ok(output) if !output.stderr.is_empty() => { - sess.dcx().emit_fatal(DlltoolFailImportLibrary { - dlltool_path: dlltool.to_string_lossy(), - dlltool_args: dlltool_cmd - .get_args() - .map(|arg| arg.to_string_lossy()) - .collect::<Vec<_>>() - .join(" "), - stdout: String::from_utf8_lossy(&output.stdout), - stderr: String::from_utf8_lossy(&output.stderr), - }) - } - _ => {} - } } else { // we've checked for \0 characters in the library name already let dll_name_z = CString::new(lib_name).unwrap(); @@ -242,9 +145,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { trace!(" output_path {}", output_path.display()); trace!( " import names: {}", - dll_imports + import_name_and_ordinal_vector .iter() - .map(|import| import.name.to_string()) + .map(|(name, _ordinal)| name.clone()) .collect::<Vec<_>>() .join(", "), ); @@ -281,9 +184,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { error: llvm::last_error().unwrap_or("unknown LLVM error".to_string()), }); } - }; - - output_path + } } } @@ -457,39 +358,3 @@ impl<'a> LlvmArchiveBuilder<'a> { fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) } - -fn find_binutils_dlltool(sess: &Session) -> OsString { - assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); - if let Some(dlltool_path) = &sess.opts.cg.dlltool { - return dlltool_path.clone().into_os_string(); - } - - let tool_name: OsString = if sess.host.options.is_like_windows { - // If we're compiling on Windows, always use "dlltool.exe". - "dlltool.exe" - } else { - // On other platforms, use the architecture-specific name. - match sess.target.arch.as_ref() { - "x86_64" => "x86_64-w64-mingw32-dlltool", - "x86" => "i686-w64-mingw32-dlltool", - "aarch64" => "aarch64-w64-mingw32-dlltool", - - // For non-standard architectures (e.g., aarch32) fallback to "dlltool". - _ => "dlltool", - } - } - .into(); - - // NOTE: it's not clear how useful it is to explicitly search PATH. - for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { - let full_path = dir.join(&tool_name); - if full_path.is_file() { - return full_path.into_os_string(); - } - } - - // The user didn't specify the location of the dlltool binary, and we weren't able - // to find the appropriate one on the PATH. Just return the name of the tool - // and let the invocation fail with a hopefully useful error message. - tool_name -} diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 5a7909d1511..a1f2433ab6f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -95,11 +95,14 @@ pub fn write_output_file<'ll>( } } -pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { +pub fn create_informational_target_machine( + sess: &Session, + only_base_features: bool, +) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, false); + let features = llvm_util::global_llvm_features(sess, false, only_base_features); target_machine_factory(sess, config::OptLevel::No, &features)(config) .unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise()) } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 77e51f94150..0f44c5dba5e 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -12,7 +12,8 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, LayoutError, LayoutOfHelpers, + TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_sanitizers::{cfi, kcfi}; @@ -531,7 +532,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { #[instrument(level = "trace", skip(self))] fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> { if place.layout.is_unsized() { - let tail = self.tcx.struct_tail_with_normalize(place.layout.ty, |ty| ty, || {}); + let tail = self.tcx.struct_tail_for_codegen(place.layout.ty, self.param_env()); if matches!(tail.kind(), ty::Foreign(..)) { // Unsized locals and, at least conceptually, even unsized arguments must be copied // around, which requires dynamically determining their size. Therefore, we cannot diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index c913bdebaaa..663c5be46e5 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -4,13 +4,14 @@ //! and methods are represented as just a fn ptr and not a full //! closure. +use rustc_codegen_ssa::common; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use tracing::debug; use crate::context::CodegenCx; +use crate::llvm; use crate::value::Value; -use crate::{attributes, common, llvm}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -46,7 +47,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> } else { let instance_def_id = instance.def_id(); let llfn = if tcx.sess.target.arch == "x86" - && let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym) + && let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym) { // Fix for https://github.com/rust-lang/rust/issues/104453 // On x86 Windows, LLVM uses 'L' as the prefix for any private @@ -77,8 +78,6 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> }; debug!("get_fn: not casting pointer!"); - attributes::from_fn_attrs(cx, llfn, instance); - // Apply an appropriate linkage/visibility value to our item that we // just declared. // diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 197bbb9ddbf..65f974c5689 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -1,7 +1,5 @@ //! Code that is useful in various codegen modules. -use std::fmt::Write; - use libc::{c_char, c_uint}; use rustc_ast::Mutability; use rustc_codegen_ssa::traits::*; @@ -10,9 +8,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::TyCtxt; -use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; +use rustc_session::cstore::DllImport; use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer}; -use rustc_target::spec::Target; use tracing::debug; use crate::consts::const_alloc_to_llvm; @@ -379,64 +376,3 @@ pub(crate) fn get_dllimport<'tcx>( tcx.native_library(id) .and_then(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name)) } - -pub(crate) fn is_mingw_gnu_toolchain(target: &Target) -> bool { - target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() -} - -pub(crate) fn i686_decorated_name( - dll_import: &DllImport, - mingw: bool, - disable_name_mangling: bool, -) -> String { - let name = dll_import.name.as_str(); - - let (add_prefix, add_suffix) = match dll_import.import_name_type { - Some(PeImportNameType::NoPrefix) => (false, true), - Some(PeImportNameType::Undecorated) => (false, false), - _ => (true, true), - }; - - // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). - let mut decorated_name = String::with_capacity(name.len() + 6); - - if disable_name_mangling { - // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. - decorated_name.push('\x01'); - } - - let prefix = if add_prefix && dll_import.is_fn { - match dll_import.calling_convention { - DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, - DllCallingConvention::Stdcall(_) => (!mingw - || dll_import.import_name_type == Some(PeImportNameType::Decorated)) - .then_some('_'), - DllCallingConvention::Fastcall(_) => Some('@'), - } - } else if !dll_import.is_fn && !mingw { - // For static variables, prefix with '_' on MSVC. - Some('_') - } else { - None - }; - if let Some(prefix) = prefix { - decorated_name.push(prefix); - } - - decorated_name.push_str(name); - - if add_suffix && dll_import.is_fn { - match dll_import.calling_convention { - DllCallingConvention::C => {} - DllCallingConvention::Stdcall(arg_list_size) - | DllCallingConvention::Fastcall(arg_list_size) => { - write!(&mut decorated_name, "@{arg_list_size}").unwrap(); - } - DllCallingConvention::Vectorcall(arg_list_size) => { - write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); - } - } - } - - decorated_name -} diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index c3ea4a18a71..75b298f14ca 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -18,7 +19,7 @@ use rustc_target::abi::{ }; use tracing::{debug, instrument, trace}; -use crate::common::{self, CodegenCx}; +use crate::common::CodegenCx; use crate::errors::{ InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined, }; @@ -195,7 +196,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( g2 } } else if cx.tcx.sess.target.arch == "x86" - && let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym) + && let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym) { cx.declare_global( &common::i686_decorated_name( diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ea930421b58..dd3f39ecead 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -149,7 +149,7 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(tcx.sess); + let tm = crate::back::write::create_informational_target_machine(tcx.sess, false); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); } @@ -775,10 +775,10 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> ptr); - ifn!("llvm.powi.f16", fn(t_f16, t_i32) -> t_f16); - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.powi.f128", fn(t_f128, t_i32) -> t_f128); + ifn!("llvm.powi.f16.i32", fn(t_f16, t_i32) -> t_f16); + ifn!("llvm.powi.f32.i32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64.i32", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.powi.f128.i32", fn(t_f128, t_i32) -> t_f128); ifn!("llvm.pow.f16", fn(t_f16, t_f16) -> t_f16); ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index d51e15d12e2..e542aa96e8a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -62,7 +62,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, pointee_ty: Ty<'tcx>, ) -> Option<FatPtrKind> { - let pointee_tail_ty = cx.tcx.struct_tail_erasing_lifetimes(pointee_ty, cx.param_env()); + let pointee_tail_ty = cx.tcx.struct_tail_for_codegen(pointee_ty, cx.param_env()); let layout = cx.layout_of(pointee_tail_ty); trace!( "fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})", diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index c4887464e19..2aa349b2782 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -137,7 +137,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::Visibility::Default, fn_abi.llvm_type(self), ); - fn_abi.apply_attrs_llfn(self, llfn); + fn_abi.apply_attrs_llfn(self, llfn, instance); if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index a3957bc52a5..7e53d32ce8c 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::ffi::CString; use std::path::Path; @@ -72,28 +71,6 @@ pub(crate) struct InvalidMinimumAlignmentTooLarge { pub(crate) struct SanitizerMemtagRequiresMte; #[derive(Diagnostic)] -#[diag(codegen_llvm_error_writing_def_file)] -pub(crate) struct ErrorWritingDEFFile { - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_error_calling_dlltool)] -pub(crate) struct ErrorCallingDllTool<'a> { - pub dlltool_path: Cow<'a, str>, - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_dlltool_fail_import_library)] -pub(crate) struct DlltoolFailImportLibrary<'a> { - pub dlltool_path: Cow<'a, str>, - pub dlltool_args: String, - pub stdout: Cow<'a, str>, - pub stderr: Cow<'a, str>, -} - -#[derive(Diagnostic)] #[diag(codegen_llvm_dynamic_linking_with_lto)] #[note] pub(crate) struct DynamicLinkingWithLTO; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 040de1c7dd7..57d5f6fdf50 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -35,10 +35,10 @@ fn get_simple_intrinsic<'ll>( sym::sqrtf64 => "llvm.sqrt.f64", sym::sqrtf128 => "llvm.sqrt.f128", - sym::powif16 => "llvm.powi.f16", - sym::powif32 => "llvm.powi.f32", - sym::powif64 => "llvm.powi.f64", - sym::powif128 => "llvm.powi.f128", + sym::powif16 => "llvm.powi.f16.i32", + sym::powif32 => "llvm.powi.f32.i32", + sym::powif64 => "llvm.powi.f64.i32", + sym::powif128 => "llvm.powi.f128.i32", sym::sinf16 => "llvm.sin.f16", sym::sinf32 => "llvm.sin.f32", diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 41e9cfd1066..518a86e0cb0 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -269,7 +269,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn provide(&self, providers: &mut Providers) { providers.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) + |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true, false) } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { @@ -434,7 +434,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index af8a9be1ccb..9fd8ca43789 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,6 +8,7 @@ use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_data_structures::unord::UnordSet; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::config::{PrintKind, PrintRequest}; @@ -239,40 +240,8 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> { } // In LLVM neon implicitly enables fp, but we manually enable // neon when a feature only implicitly enables fp - ("aarch64", "f32mm") => { - LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "f64mm") => { - LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fhm") => { - LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fp16") => { - LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "jsconv") => { - LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve") => { - LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2") => { - LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-aes") => { - LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sm4") => { - LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sha3") => { - LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency( - "sve2-bitperm", - TargetFeatureFoldStrength::EnableOnly("neon"), - ), + ("aarch64", "fhm") => LLVMFeature::new("fp16fml"), + ("aarch64", "fp16") => LLVMFeature::new("fullfp16"), // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called // `fast-unaligned-access`. In LLVM 19, it was split back out. ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { @@ -308,11 +277,53 @@ pub fn check_tied_features( /// Used to generate cfg variables and apply features /// Must express features in the way Rust understands them pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { - let target_machine = create_informational_target_machine(sess); + let mut features = vec![]; + + // Add base features for the target + let target_machine = create_informational_target_machine(sess, true); + features.extend( + sess.target + .supported_target_features() + .iter() + .filter(|(feature, _, _)| { + // skip checking special features, as LLVM may not understands them + if RUSTC_SPECIAL_FEATURES.contains(feature) { + return true; + } + // check that all features in a given smallvec are enabled + for llvm_feature in to_llvm_features(sess, feature) { + let cstr = SmallCStr::new(llvm_feature); + if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { + return false; + } + } + true + }) + .map(|(feature, _, _)| Symbol::intern(feature)), + ); + + // Add enabled features + for (enabled, feature) in + sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() { + Some('+') => Some((true, Symbol::intern(&s[1..]))), + Some('-') => Some((false, Symbol::intern(&s[1..]))), + _ => None, + }) + { + if enabled { + features.extend(sess.target.implied_target_features(std::iter::once(feature))); + } else { + features.retain(|f| { + !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) + }); + } + } + + // Filter enabled features based on feature gates sess.target .supported_target_features() .iter() - .filter_map(|&(feature, gate)| { + .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { @@ -320,18 +331,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { } }) .filter(|feature| { - // skip checking special features, as LLVM may not understands them - if RUSTC_SPECIAL_FEATURES.contains(feature) { - return true; - } - // check that all features in a given smallvec are enabled - for llvm_feature in to_llvm_features(sess, feature) { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { - return false; - } - } - true + RUSTC_SPECIAL_FEATURES.contains(feature) || features.contains(&Symbol::intern(feature)) }) .map(|feature| Symbol::intern(feature)) .collect() @@ -386,7 +386,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach .target .supported_target_features() .iter() - .map(|(feature, _gate)| { + .map(|(feature, _gate, _implied)| { // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name; let desc = @@ -440,7 +440,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess); + let tm = create_informational_target_machine(sess, false); match req.kind { PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass @@ -488,7 +488,11 @@ pub fn target_cpu(sess: &Session) -> &str { /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<String> { +pub(crate) fn global_llvm_features( + sess: &Session, + diagnostics: bool, + only_base_features: bool, +) -> Vec<String> { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -548,94 +552,124 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str } // -Ctarget-features - let supported_features = sess.target.supported_target_features(); - let (llvm_major, _, _) = get_version(); - let mut featsmap = FxHashMap::default(); - let feats = sess - .opts - .cg - .target_feature - .split(',') - .filter_map(|s| { - let enable_disable = match s.chars().next() { - None => return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + if !only_base_features { + let supported_features = sess.target.supported_target_features(); + let (llvm_major, _, _) = get_version(); + let mut featsmap = FxHashMap::default(); + + // insert implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + match feature.strip_prefix('+') { + Some(feature) => all_rust_features.extend( + UnordSet::from( + sess.target + .implied_target_features(std::iter::once(Symbol::intern(feature))), + ) + .to_sorted_stable_ord() + .iter() + .map(|s| format!("+{}", s.as_str())), + ), + _ => all_rust_features.push(feature.to_string()), + } + } + + let feats = all_rust_features + .iter() + .filter_map(|s| { + let enable_disable = match s.chars().next() { + None => return None, + Some(c @ ('+' | '-')) => c, + Some(_) => { + if diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + } + return None; } - return None; - } - }; + }; - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - if diagnostics { - let feature_state = supported_features.iter().find(|&&(v, _)| v == feature); - if feature_state.is_none() { - let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| { - let llvm_features = to_llvm_features(sess, rust_feature); - if llvm_features.contains(feature) && !llvm_features.contains(rust_feature) - { - Some(rust_feature) + let feature = backend_feature_name(sess, s)?; + // Warn against use of LLVM specific feature names and unstable features on the CLI. + if diagnostics { + let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); + if feature_state.is_none() { + let rust_feature = + supported_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature); + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } else if feature_state - .is_some_and(|(_name, feature_gate)| !feature_gate.is_stable()) - { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } else if feature_state + .is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) + { + // An unstable feature. Warn about using it. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } } - } - if diagnostics { - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); - } + if diagnostics { + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable_disable == '+'); + } - // rustc-specific features do not get passed down to LLVM… - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } + // rustc-specific features do not get passed down to LLVM… + if RUSTC_SPECIFIC_FEATURES.contains(&feature) { + return None; + } - // if the target-feature is "backchain" and LLVM version is greater than 18 - // then we also need to add "+backchain" to the target-features attribute. - // otherwise, we will only add the naked `backchain` attribute to the attribute-group. - if feature == "backchain" && llvm_major < 18 { - return None; - } - // ... otherwise though we run through `to_llvm_features` when - // passing requests down to LLVM. This means that all in-language - // features also work on the command line instead of having two - // different names when the LLVM name and the Rust name differ. - let llvm_feature = to_llvm_features(sess, feature); - - Some( - std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)) - .chain(llvm_feature.dependency.into_iter().filter_map(move |feat| { - match (enable_disable, feat) { + // if the target-feature is "backchain" and LLVM version is greater than 18 + // then we also need to add "+backchain" to the target-features attribute. + // otherwise, we will only add the naked `backchain` attribute to the attribute-group. + if feature == "backchain" && llvm_major < 18 { + return None; + } + // ... otherwise though we run through `to_llvm_features` when + // passing requests down to LLVM. This means that all in-language + // features also work on the command line instead of having two + // different names when the LLVM name and the Rust name differ. + let llvm_feature = to_llvm_features(sess, feature); + + Some( + std::iter::once(format!( + "{}{}", + enable_disable, llvm_feature.llvm_feature_name + )) + .chain(llvm_feature.dependency.into_iter().filter_map( + move |feat| match (enable_disable, feat) { ('-' | '+', TargetFeatureFoldStrength::Both(f)) | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, - } - })), - ) - }) - .flatten(); - features.extend(feats); + }, + )), + ) + }) + .flatten(); + features.extend(feats); + + if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { + sess.dcx().emit_err(TargetFeatureDisableOrEnable { + features: f, + span: None, + missing_features: None, + }); + } + } // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { @@ -646,30 +680,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str } } - // This is a workaround for a LLVM bug that doesn't implicitly enable - // `simd128` when `relaxed-simd` is. - // See <https://github.com/llvm/llvm-project/pull/99803>, which didn't make - // it into a released version of LLVM yet. - // - // This doesn't use the "implicit target feature" system because it is only - // used for function attributes in other targets, which fixes this bug as - // well on the function attribute level. - if sess.target.families.contains(&"wasm".into()) { - if features.iter().any(|f| f == "+relaxed-simd") - && !features.iter().any(|f| f == "+simd128") - { - features.push("+simd128".into()); - } - } - - if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { - sess.dcx().emit_err(TargetFeatureDisableOrEnable { - features: f, - span: None, - missing_features: None, - }); - } - features } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index d59eaae1ba9..f1ef359594b 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -12,7 +12,7 @@ use tracing::debug; use crate::context::CodegenCx; use crate::errors::SymbolAlreadyDefined; use crate::type_of::LayoutLlvmExt; -use crate::{attributes, base, llvm}; +use crate::{base, llvm}; impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( @@ -87,8 +87,6 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { debug!("predefine_fn: instance = {:?}", instance); - attributes::from_fn_attrs(self, lldecl, instance); - unsafe { if self.should_assume_dso_local(lldecl, false) { llvm::LLVMRustSetDSOLocal(lldecl, true); diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 2767ad5ec9c..6e1c323cbd0 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -ar_archive_writer = "0.3.0" +ar_archive_writer = "0.3.3" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" cc = "1.0.90" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 57d789aef80..80f25d42a08 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -24,8 +24,19 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} +codegen_ssa_dlltool_fail_import_library = + Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: + {$stdout} + {$stderr} + +codegen_ssa_error_calling_dlltool = + Error calling dlltool '{$dlltool_path}': {$error} + codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error} +codegen_ssa_error_writing_def_file = + Error writing .DEF file: {$error} + codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 4f93b7b23c6..ce55d99f506 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,4 +1,6 @@ +use std::env; use std::error::Error; +use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -9,7 +11,6 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; -use rustc_session::cstore::DllImport; use rustc_session::Session; use rustc_span::symbol::Symbol; use tempfile::Builder as TempFileBuilder; @@ -17,6 +18,7 @@ use tempfile::Builder as TempFileBuilder; use super::metadata::search_for_section; // Re-exporting for rustc_codegen_llvm::back::archive pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; +use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile}; pub trait ArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a>; @@ -30,10 +32,9 @@ pub trait ArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf; + import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + output_path: &Path, + ); fn extract_bundled_libs<'a>( &'a self, @@ -72,6 +73,130 @@ pub trait ArchiveBuilderBuilder { } } +pub fn create_mingw_dll_import_lib( + sess: &Session, + lib_name: &str, + import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, + output_path: &Path, +) { + let def_file_path = output_path.with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{name} @{n} NONAME"), + None => name, + } + }) + .collect::<Vec<String>>() + .join("\n") + ); + + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); + } + }; + + // --no-leading-underscore: For the `import_name_type` feature to work, we need to be + // able to control the *exact* spelling of each of the symbols that are being imported: + // hence we don't want `dlltool` adding leading underscores automatically. + let dlltool = find_binutils_dlltool(sess); + let temp_prefix = { + let mut path = PathBuf::from(&output_path); + path.pop(); + path.push(lib_name); + path + }; + // dlltool target architecture args from: + // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 + let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { + "x86_64" => ("i386:x86-64", "--64"), + "x86" => ("i386", "--32"), + "aarch64" => ("arm64", "--64"), + "arm" => ("arm", "--32"), + _ => panic!("unsupported arch {}", sess.target.arch), + }; + let mut dlltool_cmd = std::process::Command::new(&dlltool); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); + + match dlltool_cmd.output() { + Err(e) => { + sess.dcx().emit_fatal(ErrorCallingDllTool { + dlltool_path: dlltool.to_string_lossy(), + error: e, + }); + } + // dlltool returns '0' on failure, so check for error output instead. + Ok(output) if !output.stderr.is_empty() => { + sess.dcx().emit_fatal(DlltoolFailImportLibrary { + dlltool_path: dlltool.to_string_lossy(), + dlltool_args: dlltool_cmd + .get_args() + .map(|arg| arg.to_string_lossy()) + .collect::<Vec<_>>() + .join(" "), + stdout: String::from_utf8_lossy(&output.stdout), + stderr: String::from_utf8_lossy(&output.stderr), + }) + } + _ => {} + } +} + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.cg.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let tool_name: OsString = if sess.host.options.is_like_windows { + // If we're compiling on Windows, always use "dlltool.exe". + "dlltool.exe" + } else { + // On other platforms, use the architecture-specific name. + match sess.target.arch.as_ref() { + "x86_64" => "x86_64-w64-mingw32-dlltool", + "x86" => "i686-w64-mingw32-dlltool", + "aarch64" => "aarch64-w64-mingw32-dlltool", + + // For non-standard architectures (e.g., aarch32) fallback to "dlltool". + _ => "dlltool", + } + } + .into(); + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} + pub trait ArchiveBuilder { fn add_file(&mut self, path: &Path); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 653895380be..45a92fd03d5 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -51,7 +51,8 @@ use super::linker::{self, Linker}; use super::metadata::{create_wrapper_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; use crate::{ - errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, + common, errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, + NativeLib, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -390,17 +391,13 @@ fn link_rlib<'a>( } } - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - let output_path = archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir.as_ref(), - true, - ); - + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir.as_ref(), + true, + )? { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -488,6 +485,47 @@ fn collate_raw_dylibs<'a>( .collect()) } +fn create_dll_import_libs<'a>( + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + used_libraries: impl IntoIterator<Item = &'a NativeLib>, + tmpdir: &Path, + is_direct_dependency: bool, +) -> Result<Vec<PathBuf>, ErrorGuaranteed> { + Ok(collate_raw_dylibs(sess, used_libraries)? + .into_iter() + .map(|(raw_dylib_name, raw_dylib_imports)| { + let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; + let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); + + let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target); + + let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = raw_dylib_imports + .iter() + .map(|import: &DllImport| { + if sess.target.arch == "x86" { + ( + common::i686_decorated_name(import, mingw_gnu_toolchain, false), + import.ordinal(), + ) + } else { + (import.name.to_string(), import.ordinal()) + } + }) + .collect(); + + archive_builder_builder.create_dll_import_lib( + sess, + &raw_dylib_name, + import_name_and_ordinal_vector, + &output_path, + ); + + output_path + }) + .collect()) +} + /// Create a static archive. /// /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream @@ -2305,16 +2343,14 @@ fn linker_with_args( ); // Link with the import library generated for any raw-dylib functions. - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - true, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir, + true, + )? { + cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case // they are used within inlined functions or instantiated generic functions. We do this *after* @@ -2339,16 +2375,14 @@ fn linker_with_args( .flatten() .collect::<Vec<_>>(); native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str())); - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, native_libraries_from_nonstatics)? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - false, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + native_libraries_from_nonstatics, + tmpdir, + false, + )? { + cmd.add_object(&output_path); } // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 19394029c94..9b5a797ad51 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -208,6 +208,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static "powerpc64" => (Architecture::PowerPc64, None), "riscv32" => (Architecture::Riscv32, None), "riscv64" => (Architecture::Riscv64, None), + "sparc" => (Architecture::Sparc32Plus, None), "sparc64" => (Architecture::Sparc64, None), "avr" => (Architecture::Avr, None), "msp430" => (Architecture::Msp430, None), diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f4e44e63d73..f1e7f87f567 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -143,7 +143,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> Bx::Value { let cx = bx.cx(); let (source, target) = - cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env()); + cx.tcx().struct_lockstep_tails_for_codegen(source, target, bx.param_env()); match (source.kind(), target.kind()) { (&ty::Array(_, len), &ty::Slice(_)) => { cx.const_usize(len.eval_target_usize(cx.tcx(), ty::ParamEnv::reveal_all())) diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index a972c0cd99d..bfb1d217eae 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -4,7 +4,9 @@ use rustc_hir::LangItem; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; +use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; use rustc_span::Span; +use rustc_target::spec::Target; use crate::traits::*; @@ -176,3 +178,66 @@ pub fn asm_const_to_str<'tcx>( _ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty), } } + +pub fn is_mingw_gnu_toolchain(target: &Target) -> bool { + target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() +} + +pub fn i686_decorated_name( + dll_import: &DllImport, + mingw: bool, + disable_name_mangling: bool, +) -> String { + let name = dll_import.name.as_str(); + + let (add_prefix, add_suffix) = match dll_import.import_name_type { + Some(PeImportNameType::NoPrefix) => (false, true), + Some(PeImportNameType::Undecorated) => (false, false), + _ => (true, true), + }; + + // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). + let mut decorated_name = String::with_capacity(name.len() + 6); + + if disable_name_mangling { + // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. + decorated_name.push('\x01'); + } + + let prefix = if add_prefix && dll_import.is_fn { + match dll_import.calling_convention { + DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, + DllCallingConvention::Stdcall(_) => (!mingw + || dll_import.import_name_type == Some(PeImportNameType::Decorated)) + .then_some('_'), + DllCallingConvention::Fastcall(_) => Some('@'), + } + } else if !dll_import.is_fn && !mingw { + // For static variables, prefix with '_' on MSVC. + Some('_') + } else { + None + }; + if let Some(prefix) = prefix { + decorated_name.push(prefix); + } + + decorated_name.push_str(name); + + if add_suffix && dll_import.is_fn { + use std::fmt::Write; + + match dll_import.calling_convention { + DllCallingConvention::C => {} + DllCallingConvention::Stdcall(arg_list_size) + | DllCallingConvention::Fastcall(arg_list_size) => { + write!(&mut decorated_name, "@{arg_list_size}").unwrap(); + } + DllCallingConvention::Vectorcall(arg_list_size) => { + write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); + } + } + } + + decorated_name +} diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 2d1eae630cf..94bf0ab34e2 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1027,6 +1027,28 @@ pub struct FailedToGetLayout<'tcx> { } #[derive(Diagnostic)] +#[diag(codegen_ssa_dlltool_fail_import_library)] +pub(crate) struct DlltoolFailImportLibrary<'a> { + pub dlltool_path: Cow<'a, str>, + pub dlltool_args: String, + pub stdout: Cow<'a, str>, + pub stderr: Cow<'a, str>, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_writing_def_file)] +pub(crate) struct ErrorWritingDEFFile { + pub error: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_calling_dlltool)] +pub(crate) struct ErrorCallingDllTool<'a> { + pub dlltool_path: Cow<'a, str>, + pub error: std::io::Error, +} + +#[derive(Diagnostic)] #[diag(codegen_ssa_error_creating_remark_dir)] pub struct ErrorCreatingRemarkDir { pub error: std::io::Error, diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 127244a34f8..cf8f7fa25d8 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,11 +1,12 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; @@ -18,7 +19,7 @@ pub fn from_target_feature( tcx: TyCtxt<'_>, attr: &ast::Attribute, supported_target_features: &UnordMap<String, Option<Symbol>>, - target_features: &mut Vec<Symbol>, + target_features: &mut Vec<TargetFeature>, ) { let Some(list) = attr.meta_item_list() else { return }; let bad_item = |span| { @@ -30,6 +31,7 @@ pub fn from_target_feature( .emit(); }; let rust_features = tcx.features(); + let mut added_target_features = Vec::new(); for item in list { // Only `enable = ...` is accepted in the meta-item list. if !item.has_name(sym::enable) { @@ -44,7 +46,7 @@ pub fn from_target_feature( }; // We allow comma separation to enable multiple features. - target_features.extend(value.as_str().split(',').filter_map(|feature| { + added_target_features.extend(value.as_str().split(',').filter_map(|feature| { let Some(feature_gate) = supported_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); @@ -98,13 +100,26 @@ pub fn from_target_feature( })); } - for (feature, requires) in tcx.sess.target.implicit_target_features() { - if target_features.iter().any(|f| f.as_str() == *feature) - && !target_features.iter().any(|f| f.as_str() == *requires) - { - target_features.push(Symbol::intern(requires)); - } + // Add explicit features + target_features.extend( + added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), + ); + + // Add implied features + let mut implied_target_features = UnordSet::new(); + for feature in added_target_features.iter() { + implied_target_features.extend(tcx.implied_target_features(*feature).clone()); } + for feature in added_target_features.iter() { + implied_target_features.remove(feature); + } + target_features.extend( + implied_target_features + .into_sorted_stable_ord() + .iter() + .copied() + .map(|name| TargetFeature { name, implied: true }), + ) } /// Computes the set of target features used in a function for the purposes of @@ -113,7 +128,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> { let mut target_features = tcx.sess.unstable_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); - target_features.extend(&attrs.target_features); + target_features.extend(attrs.target_features.iter().map(|feature| feature.name)); match attrs.instruction_set { None => {} Some(InstructionSetAttr::ArmA32) => { @@ -158,10 +173,14 @@ pub(crate) fn provide(providers: &mut Providers) { .target .supported_target_features() .iter() - .map(|&(a, b)| (a.to_string(), b.as_feature_name())) + .map(|&(a, b, _)| (a.to_string(), b.as_feature_name())) .collect() } }, + implied_target_features: |tcx, feature| { + UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) + .into_sorted_stable_ord() + }, asm_target_features, ..*providers } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 80dad79179a..7c042c0c621 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -91,7 +91,7 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { return false; } - let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env); + let tail = self.tcx().struct_tail_for_codegen(ty, param_env); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 6d5bca57313..ff27e400016 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -73,7 +73,9 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( cid.promoted.map_or_else(String::new, |p| format!("::{p:?}")) ); - ecx.push_stack_frame( + // This can't use `init_stack_frame` since `body` is not a function, + // so computing its ABI would fail. It's also not worth it since there are no arguments to pass. + ecx.push_stack_frame_raw( cid.instance, body, &ret.clone().into(), diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 79e8e212776..a075bdc1911 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -24,8 +24,9 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup, - throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame, + throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, + StackPopCleanup, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -306,17 +307,15 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { let align = ImmTy::from_uint(target_align, args[1].layout).into(); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - // We replace the entire function call with a "tail call". - // Note that this happens before the frame of the original function - // is pushed on the stack. - self.eval_fn_call( - FnVal::Instance(instance), - (CallAbi::Rust, fn_abi), + // Push the stack frame with our own adjusted arguments. + self.init_stack_frame( + instance, + self.load_mir(instance.def, None)?, + fn_abi, &[FnArg::Copy(addr), FnArg::Copy(align)], /* with_caller_location = */ false, dest, - ret, - mir::UnwindAction::Unreachable, + StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable }, )?; Ok(ControlFlow::Break(())) } else { diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 47d0e22b527..fdbdfc7e1b8 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -1,24 +1,23 @@ +//! Manages calling a concrete function (with known MIR body) with argument passing, +//! and returning the return value to the caller. use std::borrow::Cow; -use either::Either; +use either::{Left, Right}; use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::source_map::Spanned; use rustc_span::sym; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; use rustc_target::abi::{self, FieldIdx, Integer}; use rustc_target::spec::abi::Abi; -use tracing::trace; +use tracing::{info, instrument, trace}; use super::{ throw_ub, throw_ub_custom, throw_unsup_format, CtfeProvenance, FnVal, ImmTy, InterpCx, - InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, Scalar, - StackPopCleanup, + InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, ReturnAction, Scalar, + StackPopCleanup, StackPopInfo, }; use crate::fluent_generated as fluent; -use crate::interpret::eval_context::StackPopInfo; -use crate::interpret::ReturnAction; /// An argment passed to a function. #[derive(Clone, Debug)] @@ -39,15 +38,6 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> { } } -struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> { - callee: FnVal<'tcx, M::ExtraFnVal>, - args: Vec<FnArg<'tcx, M::Provenance>>, - fn_sig: ty::FnSig<'tcx>, - fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, - /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`]) - with_caller_location: bool, -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. @@ -67,7 +57,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { args.iter().map(|fn_arg| self.copy_fn_arg(fn_arg)).collect() } - pub fn fn_arg_field( + /// Helper function for argument untupling. + pub(super) fn fn_arg_field( &self, arg: &FnArg<'tcx, M::Provenance>, field: usize, @@ -78,190 +69,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }) } - pub(super) fn eval_terminator( - &mut self, - terminator: &mir::Terminator<'tcx>, - ) -> InterpResult<'tcx> { - use rustc_middle::mir::TerminatorKind::*; - match terminator.kind { - Return => { - self.return_from_current_stack_frame(/* unwinding */ false)? - } - - Goto { target } => self.go_to_block(target), - - SwitchInt { ref discr, ref targets } => { - let discr = self.read_immediate(&self.eval_operand(discr, None)?)?; - trace!("SwitchInt({:?})", *discr); - - // Branch to the `otherwise` case by default, if no match is found. - let mut target_block = targets.otherwise(); - - for (const_int, target) in targets.iter() { - // Compare using MIR BinOp::Eq, to also support pointer values. - // (Avoiding `self.binary_op` as that does some redundant layout computation.) - let res = self.binary_op( - mir::BinOp::Eq, - &discr, - &ImmTy::from_uint(const_int, discr.layout), - )?; - if res.to_scalar().to_bool()? { - target_block = target; - break; - } - } - - self.go_to_block(target_block); - } - - Call { - ref func, - ref args, - destination, - target, - unwind, - call_source: _, - fn_span: _, - } => { - let old_stack = self.frame_idx(); - let old_loc = self.frame().loc; - - let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; - - let destination = self.force_allocation(&self.eval_place(destination)?)?; - self.eval_fn_call( - callee, - (fn_sig.abi, fn_abi), - &args, - with_caller_location, - &destination, - target, - if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable }, - )?; - // Sanity-check that `eval_fn_call` either pushed a new frame or - // did a jump to another block. - if self.frame_idx() == old_stack && self.frame().loc == old_loc { - span_bug!(terminator.source_info.span, "evaluating this call made no progress"); - } - } - - TailCall { ref func, ref args, fn_span: _ } => { - let old_frame_idx = self.frame_idx(); - - let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; - - self.eval_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; - - if self.frame_idx() != old_frame_idx { - span_bug!( - terminator.source_info.span, - "evaluating this tail call pushed a new stack frame" - ); - } - } - - Drop { place, target, unwind, replace: _ } => { - let place = self.eval_place(place)?; - let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); - if let ty::InstanceKind::DropGlue(_, None) = instance.def { - // This is the branch we enter if and only if the dropped type has no drop glue - // whatsoever. This can happen as a result of monomorphizing a drop of a - // generic. In order to make sure that generic and non-generic code behaves - // roughly the same (and in keeping with Mir semantics) we do nothing here. - self.go_to_block(target); - return Ok(()); - } - trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty); - self.drop_in_place(&place, instance, target, unwind)?; - } - - Assert { ref cond, expected, ref msg, target, unwind } => { - let ignored = - M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check(); - let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?; - if ignored || expected == cond_val { - self.go_to_block(target); - } else { - M::assert_panic(self, msg, unwind)?; - } - } - - UnwindTerminate(reason) => { - M::unwind_terminate(self, reason)?; - } - - // When we encounter Resume, we've finished unwinding - // cleanup for the current stack frame. We pop it in order - // to continue unwinding the next frame - UnwindResume => { - trace!("unwinding: resuming from cleanup"); - // By definition, a Resume terminator means - // that we're unwinding - self.return_from_current_stack_frame(/* unwinding */ true)?; - return Ok(()); - } - - // It is UB to ever encounter this. - Unreachable => throw_ub!(Unreachable), - - // These should never occur for MIR we actually run. - FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!( - terminator.source_info.span, - "{:#?} should have been eliminated by MIR pass", - terminator.kind - ), - - InlineAsm { template, ref operands, options, ref targets, .. } => { - M::eval_inline_asm(self, template, operands, options, targets)?; - } - } - - Ok(()) - } - - /// Evaluate the arguments of a function call - pub(super) fn eval_fn_call_arguments( - &self, - ops: &[Spanned<mir::Operand<'tcx>>], - ) -> InterpResult<'tcx, Vec<FnArg<'tcx, M::Provenance>>> { - ops.iter() - .map(|op| { - let arg = match &op.node { - mir::Operand::Copy(_) | mir::Operand::Constant(_) => { - // Make a regular copy. - let op = self.eval_operand(&op.node, None)?; - FnArg::Copy(op) - } - mir::Operand::Move(place) => { - // If this place lives in memory, preserve its location. - // We call `place_to_op` which will be an `MPlaceTy` whenever there exists - // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local` - // which can return a local even if that has an mplace.) - let place = self.eval_place(*place)?; - let op = self.place_to_op(&place)?; - - match op.as_mplace_or_imm() { - Either::Left(mplace) => FnArg::InPlace(mplace), - Either::Right(_imm) => { - // This argument doesn't live in memory, so there's no place - // to make inaccessible during the call. - // We rely on there not being any stray `PlaceTy` that would let the - // caller directly access this local! - // This is also crucial for tail calls, where we want the `FnArg` to - // stay valid when the old stack frame gets popped. - FnArg::Copy(op) - } - } - } - }; - - Ok(arg) - }) - .collect() - } - /// Find the wrapped inner type of a transparent wrapper. /// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field"). /// @@ -503,46 +310,206 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } - /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the - /// necessary information about callee and arguments to make a call. - fn eval_callee_and_args( - &self, - terminator: &mir::Terminator<'tcx>, - func: &mir::Operand<'tcx>, - args: &[Spanned<mir::Operand<'tcx>>], - ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { - let func = self.eval_operand(func, None)?; - let args = self.eval_fn_call_arguments(args)?; - - let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); - let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); - let extra_args = &args[fn_sig.inputs().len()..]; - let extra_args = - self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); - - let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { - ty::FnPtr(_sig) => { - let fn_ptr = self.read_pointer(&func)?; - let fn_val = self.get_ptr_fn(fn_ptr)?; - (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) - } - ty::FnDef(def_id, args) => { - let instance = self.resolve(def_id, args)?; - ( - FnVal::Instance(instance), - self.fn_abi_of_instance(instance, extra_args)?, - instance.def.requires_caller_location(*self.tcx), + fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { + // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 + let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + if !self.tcx.sess.target.is_like_wasm + && attrs + .target_features + .iter() + .any(|feature| !self.tcx.sess.target_features.contains(&feature.name)) + { + throw_ub_custom!( + fluent::const_eval_unavailable_target_features_for_fn, + unavailable_feats = attrs + .target_features + .iter() + .filter(|&feature| !feature.implied + && !self.tcx.sess.target_features.contains(&feature.name)) + .fold(String::new(), |mut s, feature| { + if !s.is_empty() { + s.push_str(", "); + } + s.push_str(feature.name.as_str()); + s + }), + ); + } + Ok(()) + } + + /// The main entry point for creating a new stack frame: performs ABI checks and initializes + /// arguments. + #[instrument(skip(self), level = "trace")] + pub fn init_stack_frame( + &mut self, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[FnArg<'tcx, M::Provenance>], + with_caller_location: bool, + destination: &MPlaceTy<'tcx, M::Provenance>, + mut stack_pop: StackPopCleanup, + ) -> InterpResult<'tcx> { + // Compute callee information. + // FIXME: for variadic support, do we have to somehow determine callee's extra_args? + let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + + if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { + throw_unsup_format!("calling a c-variadic function is not supported"); + } + + if M::enforce_abi(self) { + if caller_fn_abi.conv != callee_fn_abi.conv { + throw_ub_custom!( + fluent::const_eval_incompatible_calling_conventions, + callee_conv = format!("{:?}", callee_fn_abi.conv), + caller_conv = format!("{:?}", caller_fn_abi.conv), ) } - _ => { - span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty) + } + + // Check that all target features required by the callee (i.e., from + // the attribute `#[target_feature(enable = ...)]`) are enabled at + // compile time. + self.check_fn_target_features(instance)?; + + if !callee_fn_abi.can_unwind { + // The callee cannot unwind, so force the `Unreachable` unwind handling. + match &mut stack_pop { + StackPopCleanup::Root { .. } => {} + StackPopCleanup::Goto { unwind, .. } => { + *unwind = mir::UnwindAction::Unreachable; + } } - }; + } + + self.push_stack_frame_raw(instance, body, destination, stack_pop)?; + + // If an error is raised here, pop the frame again to get an accurate backtrace. + // To this end, we wrap it all in a `try` block. + let res: InterpResult<'tcx> = try { + trace!( + "caller ABI: {:#?}, args: {:#?}", + caller_fn_abi, + args.iter() + .map(|arg| ( + arg.layout().ty, + match arg { + FnArg::Copy(op) => format!("copy({op:?})"), + FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), + } + )) + .collect::<Vec<_>>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + body.spread_arg, + body.args_iter() + .map(|local| ( + local, + self.layout_of_local(self.frame(), local, None).unwrap().ty, + )) + .collect::<Vec<_>>() + ); + + // In principle, we have two iterators: Where the arguments come from, and where + // they go to. + + // The "where they come from" part is easy, we expect the caller to do any special handling + // that might be required here (e.g. for untupling). + // If `with_caller_location` is set we pretend there is an extra argument (that + // we will not pass; our `caller_location` intrinsic implementation walks the stack instead). + assert_eq!( + args.len() + if with_caller_location { 1 } else { 0 }, + caller_fn_abi.args.len(), + "mismatch between caller ABI and caller arguments", + ); + let mut caller_args = args + .iter() + .zip(caller_fn_abi.args.iter()) + .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); + + // Now we have to spread them out across the callee's locals, + // taking into account the `spread_arg`. If we could write + // this is a single iterator (that handles `spread_arg`), then + // `pass_argument` would be the loop body. It takes care to + // not advance `caller_iter` for ignored arguments. + let mut callee_args_abis = callee_fn_abi.args.iter(); + for local in body.args_iter() { + // Construct the destination place for this argument. At this point all + // locals are still dead, so we cannot construct a `PlaceTy`. + let dest = mir::Place::from(local); + // `layout_of_local` does more than just the instantiation we need to get the + // type, but the result gets cached so this avoids calling the instantiation + // query *again* the next time this local is accessed. + let ty = self.layout_of_local(self.frame(), local, None)?.ty; + if Some(local) == body.spread_arg { + // Make the local live once, then fill in the value field by field. + self.storage_live(local)?; + // Must be a tuple + let ty::Tuple(fields) = ty.kind() else { + span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}") + }; + for (i, field_ty) in fields.iter().enumerate() { + let dest = dest.project_deeper( + &[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)], + *self.tcx, + ); + let callee_abi = callee_args_abis.next().unwrap(); + self.pass_argument( + &mut caller_args, + callee_abi, + &dest, + field_ty, + /* already_live */ true, + )?; + } + } else { + // Normal argument. Cannot mark it as live yet, it might be unsized! + let callee_abi = callee_args_abis.next().unwrap(); + self.pass_argument( + &mut caller_args, + callee_abi, + &dest, + ty, + /* already_live */ false, + )?; + } + } + // If the callee needs a caller location, pretend we consume one more argument from the ABI. + if instance.def.requires_caller_location(*self.tcx) { + callee_args_abis.next().unwrap(); + } + // Now we should have no more caller args or callee arg ABIs + assert!( + callee_args_abis.next().is_none(), + "mismatch between callee ABI and callee body arguments" + ); + if caller_args.next().is_some() { + throw_ub_custom!(fluent::const_eval_too_many_caller_args); + } + // Don't forget to check the return type! + if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { + throw_ub!(AbiMismatchReturn { + caller_ty: caller_fn_abi.ret.layout.ty, + callee_ty: callee_fn_abi.ret.layout.ty + }); + } + + // Protect return place for in-place return value passing. + M::protect_in_place_function_argument(self, &destination)?; - Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + // Don't forget to mark "initially live" locals as live. + self.storage_live_for_always_live_locals()?; + }; + res.inspect_err(|_| { + // Don't show the incomplete stack frame in the error stacktrace. + self.stack_mut().pop(); + }) } - /// Call this function -- pushing the stack frame and initializing the arguments. + /// Initiate a call to this function -- pushing the stack frame and initializing the arguments. /// /// `caller_fn_abi` is used to determine if all the arguments are passed the proper way. /// However, we also need `caller_abi` to determine if we need to do untupling of arguments. @@ -550,7 +517,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// `with_caller_location` indicates whether the caller passed a caller location. Miri /// implements caller locations without argument passing, but to match `FnAbi` we need to know /// when those arguments are present. - pub(crate) fn eval_fn_call( + pub(super) fn init_fn_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), @@ -558,9 +525,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { with_caller_location: bool, destination: &MPlaceTy<'tcx, M::Provenance>, target: Option<mir::BasicBlock>, - mut unwind: mir::UnwindAction, + unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - trace!("eval_fn_call: {:#?}", fn_val); + trace!("init_fn_call: {:#?}", fn_val); let instance = match fn_val { FnVal::Instance(instance) => instance, @@ -591,7 +558,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )? { assert!(!self.tcx.intrinsic(fallback.def_id()).unwrap().must_be_overridden); assert!(matches!(fallback.def, ty::InstanceKind::Item(_))); - return self.eval_fn_call( + return self.init_fn_call( FnVal::Instance(fallback), (caller_abi, caller_fn_abi), args, @@ -630,189 +597,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return Ok(()); }; - // Compute callee information using the `instance` returned by - // `find_mir_or_eval_fn`. - // FIXME: for variadic support, do we have to somehow determine callee's extra_args? - let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - - if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { - throw_unsup_format!("calling a c-variadic function is not supported"); - } - - if M::enforce_abi(self) { - if caller_fn_abi.conv != callee_fn_abi.conv { - throw_ub_custom!( - fluent::const_eval_incompatible_calling_conventions, - callee_conv = format!("{:?}", callee_fn_abi.conv), - caller_conv = format!("{:?}", caller_fn_abi.conv), + // Special handling for the closure ABI: untuple the last argument. + let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = + if caller_abi == Abi::RustCall && !args.is_empty() { + // Untuple + let (untuple_arg, args) = args.split_last().unwrap(); + trace!("init_fn_call: Will pass last argument by untupling"); + Cow::from( + args.iter() + .map(|a| Ok(a.clone())) + .chain( + (0..untuple_arg.layout().fields.count()) + .map(|i| self.fn_arg_field(untuple_arg, i)), + ) + .collect::<InterpResult<'_, Vec<_>>>()?, ) - } - } - - // Check that all target features required by the callee (i.e., from - // the attribute `#[target_feature(enable = ...)]`) are enabled at - // compile time. - self.check_fn_target_features(instance)?; - - if !callee_fn_abi.can_unwind { - // The callee cannot unwind, so force the `Unreachable` unwind handling. - unwind = mir::UnwindAction::Unreachable; - } + } else { + // Plain arg passing + Cow::from(args) + }; - self.push_stack_frame( + self.init_stack_frame( instance, body, + caller_fn_abi, + &args, + with_caller_location, destination, StackPopCleanup::Goto { ret: target, unwind }, - )?; - - // If an error is raised here, pop the frame again to get an accurate backtrace. - // To this end, we wrap it all in a `try` block. - let res: InterpResult<'tcx> = try { - trace!( - "caller ABI: {:?}, args: {:#?}", - caller_abi, - args.iter() - .map(|arg| ( - arg.layout().ty, - match arg { - FnArg::Copy(op) => format!("copy({op:?})"), - FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), - } - )) - .collect::<Vec<_>>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - body.spread_arg, - body.args_iter() - .map(|local| ( - local, - self.layout_of_local(self.frame(), local, None).unwrap().ty, - )) - .collect::<Vec<_>>() - ); - - // In principle, we have two iterators: Where the arguments come from, and where - // they go to. - - // For where they come from: If the ABI is RustCall, we untuple the - // last incoming argument. These two iterators do not have the same type, - // so to keep the code paths uniform we accept an allocation - // (for RustCall ABI only). - let caller_args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = - if caller_abi == Abi::RustCall && !args.is_empty() { - // Untuple - let (untuple_arg, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - Cow::from( - args.iter() - .map(|a| Ok(a.clone())) - .chain( - (0..untuple_arg.layout().fields.count()) - .map(|i| self.fn_arg_field(untuple_arg, i)), - ) - .collect::<InterpResult<'_, Vec<_>>>()?, - ) - } else { - // Plain arg passing - Cow::from(args) - }; - // If `with_caller_location` is set we pretend there is an extra argument (that - // we will not pass). - assert_eq!( - caller_args.len() + if with_caller_location { 1 } else { 0 }, - caller_fn_abi.args.len(), - "mismatch between caller ABI and caller arguments", - ); - let mut caller_args = caller_args - .iter() - .zip(caller_fn_abi.args.iter()) - .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); - - // Now we have to spread them out across the callee's locals, - // taking into account the `spread_arg`. If we could write - // this is a single iterator (that handles `spread_arg`), then - // `pass_argument` would be the loop body. It takes care to - // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = callee_fn_abi.args.iter(); - for local in body.args_iter() { - // Construct the destination place for this argument. At this point all - // locals are still dead, so we cannot construct a `PlaceTy`. - let dest = mir::Place::from(local); - // `layout_of_local` does more than just the instantiation we need to get the - // type, but the result gets cached so this avoids calling the instantiation - // query *again* the next time this local is accessed. - let ty = self.layout_of_local(self.frame(), local, None)?.ty; - if Some(local) == body.spread_arg { - // Make the local live once, then fill in the value field by field. - self.storage_live(local)?; - // Must be a tuple - let ty::Tuple(fields) = ty.kind() else { - span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}") - }; - for (i, field_ty) in fields.iter().enumerate() { - let dest = dest.project_deeper( - &[mir::ProjectionElem::Field( - FieldIdx::from_usize(i), - field_ty, - )], - *self.tcx, - ); - let callee_abi = callee_args_abis.next().unwrap(); - self.pass_argument( - &mut caller_args, - callee_abi, - &dest, - field_ty, - /* already_live */ true, - )?; - } - } else { - // Normal argument. Cannot mark it as live yet, it might be unsized! - let callee_abi = callee_args_abis.next().unwrap(); - self.pass_argument( - &mut caller_args, - callee_abi, - &dest, - ty, - /* already_live */ false, - )?; - } - } - // If the callee needs a caller location, pretend we consume one more argument from the ABI. - if instance.def.requires_caller_location(*self.tcx) { - callee_args_abis.next().unwrap(); - } - // Now we should have no more caller args or callee arg ABIs - assert!( - callee_args_abis.next().is_none(), - "mismatch between callee ABI and callee body arguments" - ); - if caller_args.next().is_some() { - throw_ub_custom!(fluent::const_eval_too_many_caller_args); - } - // Don't forget to check the return type! - if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { - throw_ub!(AbiMismatchReturn { - caller_ty: caller_fn_abi.ret.layout.ty, - callee_ty: callee_fn_abi.ret.layout.ty - }); - } - - // Protect return place for in-place return value passing. - M::protect_in_place_function_argument(self, &destination)?; - - // Don't forget to mark "initially live" locals as live. - self.storage_live_for_always_live_locals()?; - }; - match res { - Err(err) => { - self.stack_mut().pop(); - Err(err) - } - Ok(()) => Ok(()), - } + ) } // `InstanceKind::Virtual` does not have callable MIR. Calls to `Virtual` instances must be // codegen'd / interpreted as virtual calls through the vtable. @@ -865,9 +678,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else { // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. // (For that reason we also cannot use `unpack_dyn_trait`.) - let receiver_tail = self - .tcx - .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); + let receiver_tail = + self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.param_env); let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { span_bug!( self.cur_span(), @@ -935,7 +747,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { caller_fn_abi.args[0].layout.ty = receiver_ty; // recurse with concrete function - self.eval_fn_call( + self.init_fn_call( FnVal::Instance(fn_inst), (caller_abi, &caller_fn_abi), &args, @@ -948,30 +760,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - pub(crate) fn eval_fn_tail_call( + /// Initiate a tail call to this function -- popping the current stack frame, pushing the new + /// stack frame and initializing the arguments. + pub(super) fn init_fn_tail_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, ) -> InterpResult<'tcx> { - trace!("eval_fn_call: {:#?}", fn_val); + trace!("init_fn_tail_call: {:#?}", fn_val); // This is the "canonical" implementation of tails calls, // a pop of the current stack frame, followed by a normal call // which pushes a new stack frame, with the return address from // the popped stack frame. // - // Note that we are using `pop_stack_frame` and not `return_from_current_stack_frame`, + // Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`, // as the latter "executes" the goto to the return block, but we don't want to, // only the tail called function should return to the current return block. M::before_stack_pop(self, self.frame())?; let StackPopInfo { return_action, return_to_block, return_place } = - self.pop_stack_frame(false)?; + self.pop_stack_frame_raw(false)?; assert_eq!(return_action, ReturnAction::Normal); + // Take the "stack pop cleanup" info, and use that to initiate the next call. let StackPopCleanup::Goto { ret, unwind } = return_to_block else { bug!("can't tailcall as root"); }; @@ -980,7 +795,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // we should check if both caller&callee can/n't unwind, // see <https://github.com/rust-lang/rust/pull/113128#issuecomment-1614979803> - self.eval_fn_call( + self.init_fn_call( fn_val, (caller_abi, caller_fn_abi), args, @@ -991,41 +806,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } - fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { - // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 - let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - if !self.tcx.sess.target.is_like_wasm - && attrs - .target_features - .iter() - .any(|feature| !self.tcx.sess.target_features.contains(feature)) - { - throw_ub_custom!( - fluent::const_eval_unavailable_target_features_for_fn, - unavailable_feats = attrs - .target_features - .iter() - .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) - .fold(String::new(), |mut s, feature| { - if !s.is_empty() { - s.push_str(", "); - } - s.push_str(feature.as_str()); - s - }), - ); - } - Ok(()) - } - - fn drop_in_place( + pub(super) fn init_drop_in_place_call( &mut self, place: &PlaceTy<'tcx, M::Provenance>, instance: ty::Instance<'tcx>, target: mir::BasicBlock, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - trace!("drop_in_place: {:?},\n instance={:?}", place, instance); + trace!("init_drop_in_place_call: {:?},\n instance={:?}", place, instance); // We take the address of the object. This may well be unaligned, which is fine // for us here. However, unaligned accesses will probably make the actual drop // implementation fail -- a problem shared by rustc. @@ -1060,7 +848,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let arg = self.mplace_to_ref(&place)?; let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); - self.eval_fn_call( + self.init_fn_call( FnVal::Instance(instance), (Abi::Rust, fn_abi), &[FnArg::Copy(arg.into())], @@ -1070,4 +858,118 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { unwind, ) } + + /// Pops the current frame from the stack, copies the return value to the caller, deallocates + /// the memory for allocated locals, and jumps to an appropriate place. + /// + /// If `unwinding` is `false`, then we are performing a normal return + /// from a function. In this case, we jump back into the frame of the caller, + /// and continue execution as normal. + /// + /// If `unwinding` is `true`, then we are in the middle of a panic, + /// and need to unwind this frame. In this case, we jump to the + /// `cleanup` block for the function, which is responsible for running + /// `Drop` impls for any locals that have been initialized at this point. + /// The cleanup block ends with a special `Resume` terminator, which will + /// cause us to continue unwinding. + #[instrument(skip(self), level = "trace")] + pub(super) fn return_from_current_stack_frame( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx> { + info!( + "popping stack frame ({})", + if unwinding { "during unwinding" } else { "returning from function" } + ); + + // Check `unwinding`. + assert_eq!( + unwinding, + match self.frame().loc { + Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, + Right(_) => true, + } + ); + if unwinding && self.frame_idx() == 0 { + throw_ub_custom!(fluent::const_eval_unwind_past_top); + } + + M::before_stack_pop(self, self.frame())?; + + // Copy return value. Must of course happen *before* we deallocate the locals. + // Must be *after* `before_stack_pop` as otherwise the return place might still be protected. + let copy_ret_result = if !unwinding { + let op = self + .local_to_op(mir::RETURN_PLACE, None) + .expect("return place should always be live"); + let dest = self.frame().return_place.clone(); + let res = if self.stack().len() == 1 { + // The initializer of constants and statics will get validated separately + // after the constant has been fully evaluated. While we could fall back to the default + // code path, that will cause -Zenforce-validity to cycle on static initializers. + // Reading from a static's memory is not allowed during its evaluation, and will always + // trigger a cycle error. Validation must read from the memory of the current item. + // For Miri this means we do not validate the root frame return value, + // but Miri anyway calls `read_target_isize` on that so separate validation + // is not needed. + self.copy_op_no_dest_validation(&op, &dest) + } else { + self.copy_op_allow_transmute(&op, &dest) + }; + trace!("return value: {:?}", self.dump_place(&dest.into())); + // We delay actually short-circuiting on this error until *after* the stack frame is + // popped, since we want this error to be attributed to the caller, whose type defines + // this transmute. + res + } else { + Ok(()) + }; + + // All right, now it is time to actually pop the frame. + let stack_pop_info = self.pop_stack_frame_raw(unwinding)?; + + // Report error from return value copy, if any. + copy_ret_result?; + + match stack_pop_info.return_action { + ReturnAction::Normal => {} + ReturnAction::NoJump => { + // The hook already did everything. + return Ok(()); + } + ReturnAction::NoCleanup => { + // If we are not doing cleanup, also skip everything else. + assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); + assert!(!unwinding, "tried to skip cleanup during unwinding"); + // Skip machine hook. + return Ok(()); + } + } + + // Normal return, figure out where to jump. + if unwinding { + // Follow the unwind edge. + match stack_pop_info.return_to_block { + StackPopCleanup::Goto { unwind, .. } => { + // This must be the very last thing that happens, since it can in fact push a new stack frame. + self.unwind_to_block(unwind) + } + StackPopCleanup::Root { .. } => { + panic!("encountered StackPopCleanup::Root when unwinding!") + } + } + } else { + // Follow the normal return edge. + match stack_pop_info.return_to_block { + StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret), + StackPopCleanup::Root { .. } => { + assert!( + self.stack().is_empty(), + "only the bottommost frame can have StackPopCleanup::Root" + ); + Ok(()) + } + } + } + } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index b2f07de0ac4..c3d7f2234ed 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -386,7 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { // A<Struct> -> A<Trait> conversion let (src_pointee_ty, dest_pointee_ty) = - self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); + self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.param_env); match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 18d585e4a7a..7a6bbdfdcb5 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,40 +1,29 @@ -use std::cell::Cell; -use std::{fmt, mem}; - -use either::{Either, Left, Right}; +use either::{Left, Right}; use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::DefId; -use rustc_hir::definitions::DefPathData; -use rustc_hir::{self as hir}; -use rustc_index::IndexVec; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; -use rustc_middle::mir::interpret::{ - CtfeProvenance, ErrorHandled, InvalidMetaKind, ReportedErrorInfo, -}; +use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ - self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers, - TyAndLayout, + self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, Variance}; -use rustc_middle::{bug, mir, span_bug}; -use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_middle::{mir, span_bug}; use rustc_session::Limit; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_trait_selection::traits::ObligationCtxt; -use tracing::{debug, info, info_span, instrument, trace}; +use tracing::{debug, trace}; use super::{ - err_inval, throw_inval, throw_ub, throw_ub_custom, throw_unsup, GlobalId, Immediate, - InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind, - OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, - ReturnAction, Scalar, + err_inval, throw_inval, throw_ub, throw_ub_custom, Frame, FrameInfo, GlobalId, InterpErrorInfo, + InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, + Projectable, Provenance, }; -use crate::{errors, fluent_generated as fluent, util, ReportErrorExt}; +use crate::{fluent_generated as fluent, util, ReportErrorExt}; pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// Stores the `Machine` instance. @@ -57,314 +46,6 @@ pub struct InterpCx<'tcx, M: Machine<'tcx>> { pub recursion_limit: Limit, } -// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread -// boundary and dropped in the other thread, it would exit the span in the other thread. -struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>); - -impl SpanGuard { - /// By default a `SpanGuard` does nothing. - fn new() -> Self { - Self(tracing::Span::none(), std::marker::PhantomData) - } - - /// If a span is entered, we exit the previous span (if any, normally none) and enter the - /// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of - /// `Frame` by creating a dummy span to being with and then entering it once the frame has - /// been pushed. - fn enter(&mut self, span: tracing::Span) { - // This executes the destructor on the previous instance of `SpanGuard`, ensuring that - // we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we - // can't protect the tracing stack, but that'll just lead to weird logging, no actual - // problems. - *self = Self(span, std::marker::PhantomData); - self.0.with_subscriber(|(id, dispatch)| { - dispatch.enter(id); - }); - } -} - -impl Drop for SpanGuard { - fn drop(&mut self) { - self.0.with_subscriber(|(id, dispatch)| { - dispatch.exit(id); - }); - } -} - -/// A stack frame. -pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { - //////////////////////////////////////////////////////////////////////////////// - // Function and callsite information - //////////////////////////////////////////////////////////////////////////////// - /// The MIR for the function called on this frame. - pub body: &'tcx mir::Body<'tcx>, - - /// The def_id and args of the current function. - pub instance: ty::Instance<'tcx>, - - /// Extra data for the machine. - pub extra: Extra, - - //////////////////////////////////////////////////////////////////////////////// - // Return place and locals - //////////////////////////////////////////////////////////////////////////////// - /// Work to perform when returning from this function. - pub return_to_block: StackPopCleanup, - - /// The location where the result of the current stack frame should be written to, - /// and its layout in the caller. - pub return_place: MPlaceTy<'tcx, Prov>, - - /// The list of locals for this stack frame, stored in order as - /// `[return_ptr, arguments..., variables..., temporaries...]`. - /// The locals are stored as `Option<Value>`s. - /// `None` represents a local that is currently dead, while a live local - /// can either directly contain `Scalar` or refer to some part of an `Allocation`. - /// - /// Do *not* access this directly; always go through the machine hook! - pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>, - - /// The span of the `tracing` crate is stored here. - /// When the guard is dropped, the span is exited. This gives us - /// a full stack trace on all tracing statements. - tracing_span: SpanGuard, - - //////////////////////////////////////////////////////////////////////////////// - // Current position within the function - //////////////////////////////////////////////////////////////////////////////// - /// If this is `Right`, we are not currently executing any particular statement in - /// this frame (can happen e.g. during frame initialization, and during unwinding on - /// frames without cleanup code). - /// - /// Needs to be public because ConstProp does unspeakable things to it. - pub loc: Either<mir::Location, Span>, -} - -/// What we store about a frame in an interpreter backtrace. -#[derive(Clone, Debug)] -pub struct FrameInfo<'tcx> { - pub instance: ty::Instance<'tcx>, - pub span: Span, -} - -#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these -pub enum StackPopCleanup { - /// Jump to the next block in the caller, or cause UB if None (that's a function - /// that may never return). Also store layout of return place so - /// we can validate it at that layout. - /// `ret` stores the block we jump to on a normal return, while `unwind` - /// stores the block used for cleanup during unwinding. - Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction }, - /// The root frame of the stack: nowhere else to jump to. - /// `cleanup` says whether locals are deallocated. Static computation - /// wants them leaked to intern what they need (and just throw away - /// the entire `ecx` when it is done). - Root { cleanup: bool }, -} - -/// Return type of [`InterpCx::pop_stack_frame`]. -pub struct StackPopInfo<'tcx, Prov: Provenance> { - /// Additional information about the action to be performed when returning from the popped - /// stack frame. - pub return_action: ReturnAction, - - /// [`return_to_block`](Frame::return_to_block) of the popped stack frame. - pub return_to_block: StackPopCleanup, - - /// [`return_place`](Frame::return_place) of the popped stack frame. - pub return_place: MPlaceTy<'tcx, Prov>, -} - -/// State of a local variable including a memoized layout -#[derive(Clone)] -pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { - value: LocalValue<Prov>, - /// Don't modify if `Some`, this is only used to prevent computing the layout twice. - /// Avoids computing the layout of locals that are never actually initialized. - layout: Cell<Option<TyAndLayout<'tcx>>>, -} - -impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LocalState") - .field("value", &self.value) - .field("ty", &self.layout.get().map(|l| l.ty)) - .finish() - } -} - -/// Current value of a local variable -/// -/// This does not store the type of the local; the type is given by `body.local_decls` and can never -/// change, so by not storing here we avoid having to maintain that as an invariant. -#[derive(Copy, Clone, Debug)] // Miri debug-prints these -pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> { - /// This local is not currently alive, and cannot be used at all. - Dead, - /// A normal, live local. - /// Mostly for convenience, we re-use the `Operand` type here. - /// This is an optimization over just always having a pointer here; - /// we can thus avoid doing an allocation when the local just stores - /// immediate values *and* never has its address taken. - Live(Operand<Prov>), -} - -impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { - pub fn make_live_uninit(&mut self) { - self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit)); - } - - /// This is a hack because Miri needs a way to visit all the provenance in a `LocalState` - /// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type - /// private. - pub fn as_mplace_or_imm( - &self, - ) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> { - match self.value { - LocalValue::Dead => None, - LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))), - LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)), - } - } - - /// Read the local's value or error if the local is not yet live or not live anymore. - #[inline(always)] - pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> { - match &self.value { - LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), - } - } - - /// Overwrite the local. If the local can be overwritten in place, return a reference - /// to do so; otherwise return the `MemPlace` to consult instead. - #[inline(always)] - pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> { - match &mut self.value { - LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), - } - } -} - -impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { - pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'tcx, Prov, Extra> { - Frame { - body: self.body, - instance: self.instance, - return_to_block: self.return_to_block, - return_place: self.return_place, - locals: self.locals, - loc: self.loc, - extra, - tracing_span: self.tracing_span, - } - } -} - -impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { - /// Get the current location within the Frame. - /// - /// If this is `Right`, we are not currently executing any particular statement in - /// this frame (can happen e.g. during frame initialization, and during unwinding on - /// frames without cleanup code). - /// - /// Used by priroda. - pub fn current_loc(&self) -> Either<mir::Location, Span> { - self.loc - } - - /// Return the `SourceInfo` of the current instruction. - pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { - self.loc.left().map(|loc| self.body.source_info(loc)) - } - - pub fn current_span(&self) -> Span { - match self.loc { - Left(loc) => self.body.source_info(loc).span, - Right(span) => span, - } - } - - pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> { - // We first try to get a HirId via the current source scope, - // and fall back to `body.source`. - self.current_source_info() - .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data { - mir::ClearCrossCrate::Set(data) => Some(data.lint_root), - mir::ClearCrossCrate::Clear => None, - }) - .or_else(|| { - let def_id = self.body.source.def_id().as_local(); - def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id)) - }) - } - - /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a - /// sanity check to detect bugs where we mix up which stack frame a place refers to. - #[inline(always)] - pub(super) fn locals_addr(&self) -> usize { - self.locals.raw.as_ptr().addr() - } - - #[must_use] - pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec<FrameInfo<'tcx>> { - let mut frames = Vec::new(); - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - for frame in stack.iter().rev() { - let span = match frame.loc { - Left(loc) => { - // If the stacktrace passes through MIR-inlined source scopes, add them. - let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); - let mut scope_data = &frame.body.source_scopes[scope]; - while let Some((instance, call_span)) = scope_data.inlined { - frames.push(FrameInfo { span, instance }); - span = call_span; - scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; - } - span - } - Right(span) => span, - }; - frames.push(FrameInfo { span, instance: frame.instance }); - } - trace!("generate stacktrace: {:#?}", frames); - frames - } -} - -// FIXME: only used by miri, should be removed once translatable. -impl<'tcx> fmt::Display for FrameInfo<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { - write!(f, "inside closure") - } else { - // Note: this triggers a `must_produce_diag` state, which means that if we ever - // get here we must emit a diagnostic. We should never display a `FrameInfo` unless - // we actually want to emit a warning or error to the user. - write!(f, "inside `{}`", self.instance) - } - }) - } -} - -impl<'tcx> FrameInfo<'tcx> { - pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote { - let span = self.span; - if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { - errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 } - } else { - let instance = format!("{}", self.instance); - // Note: this triggers a `must_produce_diag` state, which means that if we ever get - // here we must emit a diagnostic. We should never display a `FrameInfo` unless we - // actually want to emit a warning or error to the user. - errors::FrameNote { where_: "instance", span, instance, times: 0 } - } - } -} - impl<'tcx, M: Machine<'tcx>> HasDataLayout for InterpCx<'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { @@ -703,30 +384,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") } - #[inline(always)] - pub(super) fn layout_of_local( - &self, - frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, - local: mir::Local, - layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - let state = &frame.locals[local]; - if let Some(layout) = state.layout.get() { - return Ok(layout); - } - - let layout = from_known_layout(self.tcx, self.param_env, layout, || { - let local_ty = frame.body.local_decls[local].ty; - let local_ty = - self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; - self.layout_of(local_ty) - })?; - - // Layouts of locals are requested a lot, so we cache them. - state.layout.set(Some(layout)); - Ok(layout) - } - /// Returns the actual dynamic size and alignment of the place at the given type. /// Only the "meta" (metadata) part of the place matters. /// This can fail to provide an answer for extern types. @@ -825,132 +482,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.size_and_align_of(&mplace.meta(), &mplace.layout) } - #[instrument(skip(self, body, return_place, return_to_block), level = "debug")] - pub fn push_stack_frame( - &mut self, - instance: ty::Instance<'tcx>, - body: &'tcx mir::Body<'tcx>, - return_place: &MPlaceTy<'tcx, M::Provenance>, - return_to_block: StackPopCleanup, - ) -> InterpResult<'tcx> { - trace!("body: {:#?}", body); - - // First push a stack frame so we have access to the local args - self.push_new_stack_frame(instance, body, return_to_block, return_place.clone())?; - - self.after_stack_frame_push(instance, body)?; - - Ok(()) - } - - /// Creates a new stack frame, initializes it and pushes it onto the stack. - /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). - fn push_new_stack_frame( - &mut self, - instance: ty::Instance<'tcx>, - body: &'tcx mir::Body<'tcx>, - return_to_block: StackPopCleanup, - return_place: MPlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx> { - let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; - let locals = IndexVec::from_elem(dead_local, &body.local_decls); - let pre_frame = Frame { - body, - loc: Right(body.span), // Span used for errors caused during preamble. - return_to_block, - return_place, - locals, - instance, - tracing_span: SpanGuard::new(), - extra: (), - }; - let frame = M::init_frame(self, pre_frame)?; - self.stack_mut().push(frame); - - Ok(()) - } - - /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). - fn after_stack_frame_push( - &mut self, - instance: ty::Instance<'tcx>, - body: &'tcx mir::Body<'tcx>, - ) -> InterpResult<'tcx> { - // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). - for &const_ in body.required_consts() { - let c = - self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; - c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| { - err.emit_note(*self.tcx); - err - })?; - } - - // done - M::after_stack_push(self)?; - self.frame_mut().loc = Left(mir::Location::START); - - let span = info_span!("frame", "{}", instance); - self.frame_mut().tracing_span.enter(span); - - Ok(()) - } - - /// Pops a stack frame from the stack and returns some information about it. - /// - /// This also deallocates locals, if necessary. - /// - /// [`M::before_stack_pop`] should be called before calling this function. - /// [`M::after_stack_pop`] is called by this function automatically. - /// - /// [`M::before_stack_pop`]: Machine::before_stack_pop - /// [`M::after_stack_pop`]: Machine::after_stack_pop - pub fn pop_stack_frame( - &mut self, - unwinding: bool, - ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { - let cleanup = self.cleanup_current_frame_locals()?; - - let frame = - self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); - - let return_to_block = frame.return_to_block; - let return_place = frame.return_place.clone(); - - let return_action; - if cleanup { - return_action = M::after_stack_pop(self, frame, unwinding)?; - assert_ne!(return_action, ReturnAction::NoCleanup); - } else { - return_action = ReturnAction::NoCleanup; - }; - - Ok(StackPopInfo { return_action, return_to_block, return_place }) - } - - /// A private helper for [`pop_stack_frame`](InterpCx::pop_stack_frame). - /// Returns `true` if cleanup has been done, `false` otherwise. - fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { - // Cleanup: deallocate locals. - // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. - // We do this while the frame is still on the stack, so errors point to the callee. - let return_to_block = self.frame().return_to_block; - let cleanup = match return_to_block { - StackPopCleanup::Goto { .. } => true, - StackPopCleanup::Root { cleanup, .. } => cleanup, - }; - - if cleanup { - // We need to take the locals out, since we need to mutate while iterating. - let locals = mem::take(&mut self.frame_mut().locals); - for local in &locals { - self.deallocate_local(local.value)?; - } - } - - Ok(cleanup) - } - /// Jump to the given block. #[inline] pub fn go_to_block(&mut self, target: mir::BasicBlock) { @@ -997,248 +528,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } - /// Pops the current frame from the stack, deallocating the - /// memory for allocated locals, and jumps to an appropriate place. - /// - /// If `unwinding` is `false`, then we are performing a normal return - /// from a function. In this case, we jump back into the frame of the caller, - /// and continue execution as normal. - /// - /// If `unwinding` is `true`, then we are in the middle of a panic, - /// and need to unwind this frame. In this case, we jump to the - /// `cleanup` block for the function, which is responsible for running - /// `Drop` impls for any locals that have been initialized at this point. - /// The cleanup block ends with a special `Resume` terminator, which will - /// cause us to continue unwinding. - #[instrument(skip(self), level = "debug")] - pub(super) fn return_from_current_stack_frame( - &mut self, - unwinding: bool, - ) -> InterpResult<'tcx> { - info!( - "popping stack frame ({})", - if unwinding { "during unwinding" } else { "returning from function" } - ); - - // Check `unwinding`. - assert_eq!( - unwinding, - match self.frame().loc { - Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, - Right(_) => true, - } - ); - if unwinding && self.frame_idx() == 0 { - throw_ub_custom!(fluent::const_eval_unwind_past_top); - } - - M::before_stack_pop(self, self.frame())?; - - // Copy return value. Must of course happen *before* we deallocate the locals. - let copy_ret_result = if !unwinding { - let op = self - .local_to_op(mir::RETURN_PLACE, None) - .expect("return place should always be live"); - let dest = self.frame().return_place.clone(); - let err = if self.stack().len() == 1 { - // The initializer of constants and statics will get validated separately - // after the constant has been fully evaluated. While we could fall back to the default - // code path, that will cause -Zenforce-validity to cycle on static initializers. - // Reading from a static's memory is not allowed during its evaluation, and will always - // trigger a cycle error. Validation must read from the memory of the current item. - // For Miri this means we do not validate the root frame return value, - // but Miri anyway calls `read_target_isize` on that so separate validation - // is not needed. - self.copy_op_no_dest_validation(&op, &dest) - } else { - self.copy_op_allow_transmute(&op, &dest) - }; - trace!("return value: {:?}", self.dump_place(&dest.into())); - // We delay actually short-circuiting on this error until *after* the stack frame is - // popped, since we want this error to be attributed to the caller, whose type defines - // this transmute. - err - } else { - Ok(()) - }; - - // All right, now it is time to actually pop the frame. - let stack_pop_info = self.pop_stack_frame(unwinding)?; - - // Report error from return value copy, if any. - copy_ret_result?; - - match stack_pop_info.return_action { - ReturnAction::Normal => {} - ReturnAction::NoJump => { - // The hook already did everything. - return Ok(()); - } - ReturnAction::NoCleanup => { - // If we are not doing cleanup, also skip everything else. - assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); - assert!(!unwinding, "tried to skip cleanup during unwinding"); - // Skip machine hook. - return Ok(()); - } - } - - // Normal return, figure out where to jump. - if unwinding { - // Follow the unwind edge. - let unwind = match stack_pop_info.return_to_block { - StackPopCleanup::Goto { unwind, .. } => unwind, - StackPopCleanup::Root { .. } => { - panic!("encountered StackPopCleanup::Root when unwinding!") - } - }; - // This must be the very last thing that happens, since it can in fact push a new stack frame. - self.unwind_to_block(unwind) - } else { - // Follow the normal return edge. - match stack_pop_info.return_to_block { - StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret), - StackPopCleanup::Root { .. } => { - assert!( - self.stack().is_empty(), - "only the topmost frame can have StackPopCleanup::Root" - ); - Ok(()) - } - } - } - } - - /// In the current stack frame, mark all locals as live that are not arguments and don't have - /// `Storage*` annotations (this includes the return place). - pub fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> { - self.storage_live(mir::RETURN_PLACE)?; - - let body = self.body(); - let always_live = always_storage_live_locals(body); - for local in body.vars_and_temps_iter() { - if always_live.contains(local) { - self.storage_live(local)?; - } - } - Ok(()) - } - - pub fn storage_live_dyn( - &mut self, - local: mir::Local, - meta: MemPlaceMeta<M::Provenance>, - ) -> InterpResult<'tcx> { - trace!("{:?} is now live", local); - - // We avoid `ty.is_trivially_sized` since that does something expensive for ADTs. - fn is_very_trivially_sized(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::RawPtr(..) - | ty::Char - | ty::Ref(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Array(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Never - | ty::Error(_) - | ty::Dynamic(_, _, ty::DynStar) => true, - - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - - ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), - - ty::Pat(ty, ..) => is_very_trivially_sized(*ty), - - // We don't want to do any queries, so there is not much we can do with ADTs. - ty::Adt(..) => false, - - ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false, - - ty::Infer(ty::TyVar(_)) => false, - - ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty) - } - } - } - - // This is a hot function, we avoid computing the layout when possible. - // `unsized_` will be `None` for sized types and `Some(layout)` for unsized types. - let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) { - None - } else { - // We need the layout. - let layout = self.layout_of_local(self.frame(), local, None)?; - if layout.is_sized() { None } else { Some(layout) } - }; - - let local_val = LocalValue::Live(if let Some(layout) = unsized_ { - if !meta.has_meta() { - throw_unsup!(UnsizedLocal); - } - // Need to allocate some memory, since `Immediate::Uninit` cannot be unsized. - let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?; - Operand::Indirect(*dest_place.mplace()) - } else { - assert!(!meta.has_meta()); // we're dropping the metadata - // Just make this an efficient immediate. - // Note that not calling `layout_of` here does have one real consequence: - // if the type is too big, we'll only notice this when the local is actually initialized, - // which is a bit too late -- we should ideally notice this already here, when the memory - // is conceptually allocated. But given how rare that error is and that this is a hot function, - // we accept this downside for now. - Operand::Immediate(Immediate::Uninit) - }); - - // If the local is already live, deallocate its old memory. - let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); - self.deallocate_local(old)?; - Ok(()) - } - - /// Mark a storage as live, killing the previous content. - #[inline(always)] - pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> { - self.storage_live_dyn(local, MemPlaceMeta::None) - } - - pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> { - assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); - trace!("{:?} is now dead", local); - - // If the local is already dead, this is a NOP. - let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); - self.deallocate_local(old)?; - Ok(()) - } - - #[instrument(skip(self), level = "debug")] - fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> { - if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { - // All locals have a backing allocation, even if the allocation is empty - // due to the local having ZST type. Hence we can `unwrap`. - trace!( - "deallocating local {:?}: {:?}", - local, - // Locals always have a `alloc_id` (they are never the result of a int2ptr). - self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) - ); - self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; - }; - Ok(()) - } - /// Call a query that can return `ErrorHandled`. Should be used for statics and other globals. /// (`mir::Const`/`ty::Const` have `eval` methods that can be used directly instead.) pub fn ctfe_query<T>( @@ -1328,39 +617,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for PlacePrinter<'a, 'tcx, M> { } write!(fmt, ":")?; - match self.ecx.frame().locals[local].value { - LocalValue::Dead => write!(fmt, " is dead")?, - LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { - write!(fmt, " is uninitialized")? - } - LocalValue::Live(Operand::Indirect(mplace)) => { - write!( - fmt, - " by {} ref {:?}:", - match mplace.meta { - MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"), - MemPlaceMeta::None => String::new(), - }, - mplace.ptr, - )?; - allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id)); - } - LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { - write!(fmt, " {val:?}")?; - if let Scalar::Ptr(ptr, _size) = val { - allocs.push(ptr.provenance.get_alloc_id()); - } - } - LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { - write!(fmt, " ({val1:?}, {val2:?})")?; - if let Scalar::Ptr(ptr, _size) = val1 { - allocs.push(ptr.provenance.get_alloc_id()); - } - if let Scalar::Ptr(ptr, _size) = val2 { - allocs.push(ptr.provenance.get_alloc_id()); - } - } - } + self.ecx.frame().locals[local].print(&mut allocs, fmt)?; write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs.into_iter().flatten().collect())) } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index bdce8253b2e..7af4e0c285b 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -37,7 +37,7 @@ pub enum ReturnAction { /// took care of everything. NoJump, - /// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done. + /// Returned by [`InterpCx::pop_stack_frame_raw`] when no cleanup should be done. NoCleanup, } diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index afa2303e387..511756e3f86 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -1,5 +1,6 @@ //! An interpreter for MIR used in CTFE and by miri +mod call; mod cast; mod discriminant; mod eval_context; @@ -11,8 +12,8 @@ mod operand; mod operator; mod place; mod projection; +mod stack; mod step; -mod terminator; mod traits; mod util; mod validity; @@ -22,7 +23,8 @@ use eval_context::{from_known_layout, mir_assign_valid_types}; #[doc(no_inline)] pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here -pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup}; +pub use self::call::FnArg; +pub use self::eval_context::{format_interp_error, InterpCx}; pub use self::intern::{ intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind, InternResult, @@ -35,7 +37,7 @@ pub use self::operand::{ImmTy, Immediate, OpTy, Readable}; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; -pub use self::terminator::FnArg; +pub use self::stack::{Frame, FrameInfo, LocalState, StackPopCleanup, StackPopInfo}; pub(crate) use self::util::create_static_alloc; pub use self::validity::{CtfeValidationMode, RefTracking}; pub use self::visitor::ValueVisitor; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index d4559e1e8c6..4ced009ab39 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -184,6 +184,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self { debug_assert!(layout.abi.is_scalar(), "`ImmTy::from_scalar` on non-scalar layout"); + debug_assert_eq!(val.size(), layout.size); ImmTy { imm: val.into(), layout } } diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs new file mode 100644 index 00000000000..50dbced6a2a --- /dev/null +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -0,0 +1,651 @@ +//! Manages the low-level pushing and popping of stack frames and the (de)allocation of local variables. +//! For hadling of argument passing and return values, see the `call` module. +use std::cell::Cell; +use std::{fmt, mem}; + +use either::{Either, Left, Right}; +use rustc_hir as hir; +use rustc_hir::definitions::DefPathData; +use rustc_index::IndexVec; +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, mir}; +use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_span::Span; +use tracing::{info_span, instrument, trace}; + +use super::{ + from_known_layout, throw_ub, throw_unsup, AllocId, CtfeProvenance, Immediate, InterpCx, + InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, MemoryKind, Operand, Pointer, + Provenance, ReturnAction, Scalar, +}; +use crate::errors; + +// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread +// boundary and dropped in the other thread, it would exit the span in the other thread. +struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>); + +impl SpanGuard { + /// By default a `SpanGuard` does nothing. + fn new() -> Self { + Self(tracing::Span::none(), std::marker::PhantomData) + } + + /// If a span is entered, we exit the previous span (if any, normally none) and enter the + /// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of + /// `Frame` by creating a dummy span to being with and then entering it once the frame has + /// been pushed. + fn enter(&mut self, span: tracing::Span) { + // This executes the destructor on the previous instance of `SpanGuard`, ensuring that + // we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we + // can't protect the tracing stack, but that'll just lead to weird logging, no actual + // problems. + *self = Self(span, std::marker::PhantomData); + self.0.with_subscriber(|(id, dispatch)| { + dispatch.enter(id); + }); + } +} + +impl Drop for SpanGuard { + fn drop(&mut self) { + self.0.with_subscriber(|(id, dispatch)| { + dispatch.exit(id); + }); + } +} + +/// A stack frame. +pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { + //////////////////////////////////////////////////////////////////////////////// + // Function and callsite information + //////////////////////////////////////////////////////////////////////////////// + /// The MIR for the function called on this frame. + pub(super) body: &'tcx mir::Body<'tcx>, + + /// The def_id and args of the current function. + pub(super) instance: ty::Instance<'tcx>, + + /// Extra data for the machine. + pub extra: Extra, + + //////////////////////////////////////////////////////////////////////////////// + // Return place and locals + //////////////////////////////////////////////////////////////////////////////// + /// Work to perform when returning from this function. + return_to_block: StackPopCleanup, + + /// The location where the result of the current stack frame should be written to, + /// and its layout in the caller. + pub return_place: MPlaceTy<'tcx, Prov>, + + /// The list of locals for this stack frame, stored in order as + /// `[return_ptr, arguments..., variables..., temporaries...]`. + /// The locals are stored as `Option<Value>`s. + /// `None` represents a local that is currently dead, while a live local + /// can either directly contain `Scalar` or refer to some part of an `Allocation`. + /// + /// Do *not* access this directly; always go through the machine hook! + pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>, + + /// The span of the `tracing` crate is stored here. + /// When the guard is dropped, the span is exited. This gives us + /// a full stack trace on all tracing statements. + tracing_span: SpanGuard, + + //////////////////////////////////////////////////////////////////////////////// + // Current position within the function + //////////////////////////////////////////////////////////////////////////////// + /// If this is `Right`, we are not currently executing any particular statement in + /// this frame (can happen e.g. during frame initialization, and during unwinding on + /// frames without cleanup code). + /// + /// Needs to be public because ConstProp does unspeakable things to it. + pub(super) loc: Either<mir::Location, Span>, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these +pub enum StackPopCleanup { + /// Jump to the next block in the caller, or cause UB if None (that's a function + /// that may never return). Also store layout of return place so + /// we can validate it at that layout. + /// `ret` stores the block we jump to on a normal return, while `unwind` + /// stores the block used for cleanup during unwinding. + Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction }, + /// The root frame of the stack: nowhere else to jump to. + /// `cleanup` says whether locals are deallocated. Static computation + /// wants them leaked to intern what they need (and just throw away + /// the entire `ecx` when it is done). + Root { cleanup: bool }, +} + +/// Return type of [`InterpCx::pop_stack_frame_raw`]. +pub struct StackPopInfo<'tcx, Prov: Provenance> { + /// Additional information about the action to be performed when returning from the popped + /// stack frame. + pub return_action: ReturnAction, + + /// [`return_to_block`](Frame::return_to_block) of the popped stack frame. + pub return_to_block: StackPopCleanup, + + /// [`return_place`](Frame::return_place) of the popped stack frame. + pub return_place: MPlaceTy<'tcx, Prov>, +} + +/// State of a local variable including a memoized layout +#[derive(Clone)] +pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { + value: LocalValue<Prov>, + /// Don't modify if `Some`, this is only used to prevent computing the layout twice. + /// Avoids computing the layout of locals that are never actually initialized. + layout: Cell<Option<TyAndLayout<'tcx>>>, +} + +impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LocalState") + .field("value", &self.value) + .field("ty", &self.layout.get().map(|l| l.ty)) + .finish() + } +} + +/// Current value of a local variable +/// +/// This does not store the type of the local; the type is given by `body.local_decls` and can never +/// change, so by not storing here we avoid having to maintain that as an invariant. +#[derive(Copy, Clone, Debug)] // Miri debug-prints these +pub(super) enum LocalValue<Prov: Provenance = CtfeProvenance> { + /// This local is not currently alive, and cannot be used at all. + Dead, + /// A normal, live local. + /// Mostly for convenience, we re-use the `Operand` type here. + /// This is an optimization over just always having a pointer here; + /// we can thus avoid doing an allocation when the local just stores + /// immediate values *and* never has its address taken. + Live(Operand<Prov>), +} + +impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { + pub fn make_live_uninit(&mut self) { + self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit)); + } + + /// This is a hack because Miri needs a way to visit all the provenance in a `LocalState` + /// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type + /// private. + pub fn as_mplace_or_imm( + &self, + ) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> { + match self.value { + LocalValue::Dead => None, + LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))), + LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)), + } + } + + /// Read the local's value or error if the local is not yet live or not live anymore. + #[inline(always)] + pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> { + match &self.value { + LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? + LocalValue::Live(val) => Ok(val), + } + } + + /// Overwrite the local. If the local can be overwritten in place, return a reference + /// to do so; otherwise return the `MemPlace` to consult instead. + #[inline(always)] + pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> { + match &mut self.value { + LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? + LocalValue::Live(val) => Ok(val), + } + } +} + +/// What we store about a frame in an interpreter backtrace. +#[derive(Clone, Debug)] +pub struct FrameInfo<'tcx> { + pub instance: ty::Instance<'tcx>, + pub span: Span, +} + +// FIXME: only used by miri, should be removed once translatable. +impl<'tcx> fmt::Display for FrameInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { + write!(f, "inside closure") + } else { + // Note: this triggers a `must_produce_diag` state, which means that if we ever + // get here we must emit a diagnostic. We should never display a `FrameInfo` unless + // we actually want to emit a warning or error to the user. + write!(f, "inside `{}`", self.instance) + } + }) + } +} + +impl<'tcx> FrameInfo<'tcx> { + pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote { + let span = self.span; + if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { + errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 } + } else { + let instance = format!("{}", self.instance); + // Note: this triggers a `must_produce_diag` state, which means that if we ever get + // here we must emit a diagnostic. We should never display a `FrameInfo` unless we + // actually want to emit a warning or error to the user. + errors::FrameNote { where_: "instance", span, instance, times: 0 } + } + } +} + +impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { + pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'tcx, Prov, Extra> { + Frame { + body: self.body, + instance: self.instance, + return_to_block: self.return_to_block, + return_place: self.return_place, + locals: self.locals, + loc: self.loc, + extra, + tracing_span: self.tracing_span, + } + } +} + +impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { + /// Get the current location within the Frame. + /// + /// If this is `Right`, we are not currently executing any particular statement in + /// this frame (can happen e.g. during frame initialization, and during unwinding on + /// frames without cleanup code). + /// + /// Used by [priroda](https://github.com/oli-obk/priroda). + pub fn current_loc(&self) -> Either<mir::Location, Span> { + self.loc + } + + pub fn body(&self) -> &'tcx mir::Body<'tcx> { + self.body + } + + pub fn instance(&self) -> ty::Instance<'tcx> { + self.instance + } + + /// Return the `SourceInfo` of the current instruction. + pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { + self.loc.left().map(|loc| self.body.source_info(loc)) + } + + pub fn current_span(&self) -> Span { + match self.loc { + Left(loc) => self.body.source_info(loc).span, + Right(span) => span, + } + } + + pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> { + // We first try to get a HirId via the current source scope, + // and fall back to `body.source`. + self.current_source_info() + .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data { + mir::ClearCrossCrate::Set(data) => Some(data.lint_root), + mir::ClearCrossCrate::Clear => None, + }) + .or_else(|| { + let def_id = self.body.source.def_id().as_local(); + def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id)) + }) + } + + /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a + /// sanity check to detect bugs where we mix up which stack frame a place refers to. + #[inline(always)] + pub(super) fn locals_addr(&self) -> usize { + self.locals.raw.as_ptr().addr() + } + + #[must_use] + pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec<FrameInfo<'tcx>> { + let mut frames = Vec::new(); + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + for frame in stack.iter().rev() { + let span = match frame.loc { + Left(loc) => { + // If the stacktrace passes through MIR-inlined source scopes, add them. + let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); + let mut scope_data = &frame.body.source_scopes[scope]; + while let Some((instance, call_span)) = scope_data.inlined { + frames.push(FrameInfo { span, instance }); + span = call_span; + scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; + } + span + } + Right(span) => span, + }; + frames.push(FrameInfo { span, instance: frame.instance }); + } + trace!("generate stacktrace: {:#?}", frames); + frames + } +} + +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Very low-level helper that pushes a stack frame without initializing + /// the arguments or local variables. + /// + /// The high-level version of this is `init_stack_frame`. + #[instrument(skip(self, body, return_place, return_to_block), level = "debug")] + pub(crate) fn push_stack_frame_raw( + &mut self, + instance: ty::Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + return_place: &MPlaceTy<'tcx, M::Provenance>, + return_to_block: StackPopCleanup, + ) -> InterpResult<'tcx> { + trace!("body: {:#?}", body); + + // We can push a `Root` frame if and only if the stack is empty. + debug_assert_eq!( + self.stack().is_empty(), + matches!(return_to_block, StackPopCleanup::Root { .. }) + ); + + // First push a stack frame so we have access to `instantiate_from_current_frame` and other + // `self.frame()`-based functions. + let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; + let locals = IndexVec::from_elem(dead_local, &body.local_decls); + let pre_frame = Frame { + body, + loc: Right(body.span), // Span used for errors caused during preamble. + return_to_block, + return_place: return_place.clone(), + locals, + instance, + tracing_span: SpanGuard::new(), + extra: (), + }; + let frame = M::init_frame(self, pre_frame)?; + self.stack_mut().push(frame); + + // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). + for &const_ in body.required_consts() { + let c = + self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; + c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| { + err.emit_note(*self.tcx); + err + })?; + } + + // Finish things up. + M::after_stack_push(self)?; + self.frame_mut().loc = Left(mir::Location::START); + let span = info_span!("frame", "{}", instance); + self.frame_mut().tracing_span.enter(span); + + Ok(()) + } + + /// Low-level helper that pops a stack frame from the stack and returns some information about + /// it. + /// + /// This also deallocates locals, if necessary. + /// + /// [`M::before_stack_pop`] should be called before calling this function. + /// [`M::after_stack_pop`] is called by this function automatically. + /// + /// The high-level version of this is `return_from_current_stack_frame`. + /// + /// [`M::before_stack_pop`]: Machine::before_stack_pop + /// [`M::after_stack_pop`]: Machine::after_stack_pop + pub(super) fn pop_stack_frame_raw( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { + let cleanup = self.cleanup_current_frame_locals()?; + + let frame = + self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + + let return_to_block = frame.return_to_block; + let return_place = frame.return_place.clone(); + + let return_action; + if cleanup { + return_action = M::after_stack_pop(self, frame, unwinding)?; + assert_ne!(return_action, ReturnAction::NoCleanup); + } else { + return_action = ReturnAction::NoCleanup; + }; + + Ok(StackPopInfo { return_action, return_to_block, return_place }) + } + + /// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw). + /// Returns `true` if cleanup has been done, `false` otherwise. + fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { + // Cleanup: deallocate locals. + // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. + // We do this while the frame is still on the stack, so errors point to the callee. + let return_to_block = self.frame().return_to_block; + let cleanup = match return_to_block { + StackPopCleanup::Goto { .. } => true, + StackPopCleanup::Root { cleanup, .. } => cleanup, + }; + + if cleanup { + // We need to take the locals out, since we need to mutate while iterating. + let locals = mem::take(&mut self.frame_mut().locals); + for local in &locals { + self.deallocate_local(local.value)?; + } + } + + Ok(cleanup) + } + + /// In the current stack frame, mark all locals as live that are not arguments and don't have + /// `Storage*` annotations (this includes the return place). + pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> { + self.storage_live(mir::RETURN_PLACE)?; + + let body = self.body(); + let always_live = always_storage_live_locals(body); + for local in body.vars_and_temps_iter() { + if always_live.contains(local) { + self.storage_live(local)?; + } + } + Ok(()) + } + + pub fn storage_live_dyn( + &mut self, + local: mir::Local, + meta: MemPlaceMeta<M::Provenance>, + ) -> InterpResult<'tcx> { + trace!("{:?} is now live", local); + + // We avoid `ty.is_trivially_sized` since that does something expensive for ADTs. + fn is_very_trivially_sized(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Never + | ty::Error(_) + | ty::Dynamic(_, _, ty::DynStar) => true, + + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, + + ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), + + ty::Pat(ty, ..) => is_very_trivially_sized(*ty), + + // We don't want to do any queries, so there is not much we can do with ADTs. + ty::Adt(..) => false, + + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false, + + ty::Infer(ty::TyVar(_)) => false, + + ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty) + } + } + } + + // This is a hot function, we avoid computing the layout when possible. + // `unsized_` will be `None` for sized types and `Some(layout)` for unsized types. + let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) { + None + } else { + // We need the layout. + let layout = self.layout_of_local(self.frame(), local, None)?; + if layout.is_sized() { None } else { Some(layout) } + }; + + let local_val = LocalValue::Live(if let Some(layout) = unsized_ { + if !meta.has_meta() { + throw_unsup!(UnsizedLocal); + } + // Need to allocate some memory, since `Immediate::Uninit` cannot be unsized. + let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?; + Operand::Indirect(*dest_place.mplace()) + } else { + assert!(!meta.has_meta()); // we're dropping the metadata + // Just make this an efficient immediate. + // Note that not calling `layout_of` here does have one real consequence: + // if the type is too big, we'll only notice this when the local is actually initialized, + // which is a bit too late -- we should ideally notice this already here, when the memory + // is conceptually allocated. But given how rare that error is and that this is a hot function, + // we accept this downside for now. + Operand::Immediate(Immediate::Uninit) + }); + + // If the local is already live, deallocate its old memory. + let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); + self.deallocate_local(old)?; + Ok(()) + } + + /// Mark a storage as live, killing the previous content. + #[inline(always)] + pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> { + self.storage_live_dyn(local, MemPlaceMeta::None) + } + + pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> { + assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); + trace!("{:?} is now dead", local); + + // If the local is already dead, this is a NOP. + let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); + self.deallocate_local(old)?; + Ok(()) + } + + fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> { + if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { + // All locals have a backing allocation, even if the allocation is empty + // due to the local having ZST type. Hence we can `unwrap`. + trace!( + "deallocating local {:?}: {:?}", + local, + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) + ); + self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; + }; + Ok(()) + } + + #[inline(always)] + pub(super) fn layout_of_local( + &self, + frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, + local: mir::Local, + layout: Option<TyAndLayout<'tcx>>, + ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { + let state = &frame.locals[local]; + if let Some(layout) = state.layout.get() { + return Ok(layout); + } + + let layout = from_known_layout(self.tcx, self.param_env, layout, || { + let local_ty = frame.body.local_decls[local].ty; + let local_ty = + self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; + self.layout_of(local_ty) + })?; + + // Layouts of locals are requested a lot, so we cache them. + state.layout.set(Some(layout)); + Ok(layout) + } +} + +impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { + pub(super) fn print( + &self, + allocs: &mut Vec<Option<AllocId>>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + match self.value { + LocalValue::Dead => write!(fmt, " is dead")?, + LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { + write!(fmt, " is uninitialized")? + } + LocalValue::Live(Operand::Indirect(mplace)) => { + write!( + fmt, + " by {} ref {:?}:", + match mplace.meta { + MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"), + MemPlaceMeta::None => String::new(), + }, + mplace.ptr, + )?; + allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id)); + } + LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { + write!(fmt, " {val:?}")?; + if let Scalar::Ptr(ptr, _size) = val { + allocs.push(ptr.provenance.get_alloc_id()); + } + } + LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { + write!(fmt, " ({val1:?}, {val2:?})")?; + if let Scalar::Ptr(ptr, _size) = val1 { + allocs.push(ptr.provenance.get_alloc_id()); + } + if let Scalar::Ptr(ptr, _size) = val2 { + allocs.push(ptr.provenance.get_alloc_id()); + } + } + } + + Ok(()) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 28cf1068f40..2527eca3446 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -4,15 +4,29 @@ use either::Either; use rustc_index::IndexSlice; -use rustc_middle::{bug, mir}; +use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_middle::{bug, mir, span_bug}; +use rustc_span::source_map::Spanned; +use rustc_target::abi::call::FnAbi; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use tracing::{info, instrument, trace}; use super::{ - ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar, + throw_ub, FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, + PlaceTy, Projectable, Scalar, }; use crate::util; +struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> { + callee: FnVal<'tcx, M::ExtraFnVal>, + args: Vec<FnArg<'tcx, M::Provenance>>, + fn_sig: ty::FnSig<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`]) + with_caller_location: bool, +} + impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` as long as there are more things to do. /// @@ -36,7 +50,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(stmt) = basic_block.statements.get(loc.statement_index) { let old_frames = self.frame_idx(); - self.statement(stmt)?; + self.eval_statement(stmt)?; // Make sure we are not updating `statement_index` of the wrong frame. assert_eq!(old_frames, self.frame_idx()); // Advance the program counter. @@ -47,7 +61,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::before_terminator(self)?; let terminator = basic_block.terminator(); - self.terminator(terminator)?; + self.eval_terminator(terminator)?; + if !self.stack().is_empty() { + if let Either::Left(loc) = self.frame().loc { + info!("// executing {:?}", loc.block); + } + } Ok(true) } @@ -55,7 +74,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// statement counter. /// /// This does NOT move the statement counter forward, the caller has to do that! - pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { + pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { info!("{:?}", stmt); use rustc_middle::mir::StatementKind::*; @@ -349,16 +368,222 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } - /// Evaluate the given terminator. Will also adjust the stack frame and statement position accordingly. - fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { + /// Evaluate the arguments of a function call + fn eval_fn_call_argument( + &self, + op: &mir::Operand<'tcx>, + ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { + Ok(match op { + mir::Operand::Copy(_) | mir::Operand::Constant(_) => { + // Make a regular copy. + let op = self.eval_operand(op, None)?; + FnArg::Copy(op) + } + mir::Operand::Move(place) => { + // If this place lives in memory, preserve its location. + // We call `place_to_op` which will be an `MPlaceTy` whenever there exists + // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local` + // which can return a local even if that has an mplace.) + let place = self.eval_place(*place)?; + let op = self.place_to_op(&place)?; + + match op.as_mplace_or_imm() { + Either::Left(mplace) => FnArg::InPlace(mplace), + Either::Right(_imm) => { + // This argument doesn't live in memory, so there's no place + // to make inaccessible during the call. + // We rely on there not being any stray `PlaceTy` that would let the + // caller directly access this local! + // This is also crucial for tail calls, where we want the `FnArg` to + // stay valid when the old stack frame gets popped. + FnArg::Copy(op) + } + } + } + }) + } + + /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the + /// necessary information about callee and arguments to make a call. + fn eval_callee_and_args( + &self, + terminator: &mir::Terminator<'tcx>, + func: &mir::Operand<'tcx>, + args: &[Spanned<mir::Operand<'tcx>>], + ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { + let func = self.eval_operand(func, None)?; + let args = args + .iter() + .map(|arg| self.eval_fn_call_argument(&arg.node)) + .collect::<InterpResult<'tcx, Vec<_>>>()?; + + let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); + let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = + self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); + + let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { + ty::FnPtr(_sig) => { + let fn_ptr = self.read_pointer(&func)?; + let fn_val = self.get_ptr_fn(fn_ptr)?; + (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) + } + ty::FnDef(def_id, args) => { + let instance = self.resolve(def_id, args)?; + ( + FnVal::Instance(instance), + self.fn_abi_of_instance(instance, extra_args)?, + instance.def.requires_caller_location(*self.tcx), + ) + } + _ => { + span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty) + } + }; + + Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + } + + fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { info!("{:?}", terminator.kind); - self.eval_terminator(terminator)?; - if !self.stack().is_empty() { - if let Either::Left(loc) = self.frame().loc { - info!("// executing {:?}", loc.block); + use rustc_middle::mir::TerminatorKind::*; + match terminator.kind { + Return => { + self.return_from_current_stack_frame(/* unwinding */ false)? + } + + Goto { target } => self.go_to_block(target), + + SwitchInt { ref discr, ref targets } => { + let discr = self.read_immediate(&self.eval_operand(discr, None)?)?; + trace!("SwitchInt({:?})", *discr); + + // Branch to the `otherwise` case by default, if no match is found. + let mut target_block = targets.otherwise(); + + for (const_int, target) in targets.iter() { + // Compare using MIR BinOp::Eq, to also support pointer values. + // (Avoiding `self.binary_op` as that does some redundant layout computation.) + let res = self.binary_op( + mir::BinOp::Eq, + &discr, + &ImmTy::from_uint(const_int, discr.layout), + )?; + if res.to_scalar().to_bool()? { + target_block = target; + break; + } + } + + self.go_to_block(target_block); + } + + Call { + ref func, + ref args, + destination, + target, + unwind, + call_source: _, + fn_span: _, + } => { + let old_stack = self.frame_idx(); + let old_loc = self.frame().loc; + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; + + let destination = self.force_allocation(&self.eval_place(destination)?)?; + self.init_fn_call( + callee, + (fn_sig.abi, fn_abi), + &args, + with_caller_location, + &destination, + target, + if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable }, + )?; + // Sanity-check that `eval_fn_call` either pushed a new frame or + // did a jump to another block. + if self.frame_idx() == old_stack && self.frame().loc == old_loc { + span_bug!(terminator.source_info.span, "evaluating this call made no progress"); + } + } + + TailCall { ref func, ref args, fn_span: _ } => { + let old_frame_idx = self.frame_idx(); + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; + + self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; + + if self.frame_idx() != old_frame_idx { + span_bug!( + terminator.source_info.span, + "evaluating this tail call pushed a new stack frame" + ); + } + } + + Drop { place, target, unwind, replace: _ } => { + let place = self.eval_place(place)?; + let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); + if let ty::InstanceKind::DropGlue(_, None) = instance.def { + // This is the branch we enter if and only if the dropped type has no drop glue + // whatsoever. This can happen as a result of monomorphizing a drop of a + // generic. In order to make sure that generic and non-generic code behaves + // roughly the same (and in keeping with Mir semantics) we do nothing here. + self.go_to_block(target); + return Ok(()); + } + trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty); + self.init_drop_in_place_call(&place, instance, target, unwind)?; + } + + Assert { ref cond, expected, ref msg, target, unwind } => { + let ignored = + M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check(); + let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?; + if ignored || expected == cond_val { + self.go_to_block(target); + } else { + M::assert_panic(self, msg, unwind)?; + } + } + + UnwindTerminate(reason) => { + M::unwind_terminate(self, reason)?; + } + + // When we encounter Resume, we've finished unwinding + // cleanup for the current stack frame. We pop it in order + // to continue unwinding the next frame + UnwindResume => { + trace!("unwinding: resuming from cleanup"); + // By definition, a Resume terminator means + // that we're unwinding + self.return_from_current_stack_frame(/* unwinding */ true)?; + return Ok(()); + } + + // It is UB to ever encounter this. + Unreachable => throw_ub!(Unreachable), + + // These should never occur for MIR we actually run. + FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!( + terminator.source_info.span, + "{:#?} should have been eliminated by MIR pass", + terminator.kind + ), + + InlineAsm { template, ref operands, options, ref targets, .. } => { + M::eval_inline_asm(self, template, operands, options, targets)?; } } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 460f5448634..fadc4ee6c8a 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -343,7 +343,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { meta: MemPlaceMeta<M::Provenance>, pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { - let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); + let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.param_env); match tail.kind() { ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index 528274e6aba..5ad55968398 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -22,7 +22,7 @@ where }; let ty = place.ty(local_decls, tcx).ty; - let unsized_tail = || tcx.struct_tail_with_normalize(ty, |ty| ty, || {}); + let unsized_tail = || tcx.struct_tail_for_codegen(ty, param_env); match tcx.layout_of(param_env.and(ty)) { Ok(layout) if layout.align.abi <= pack diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 4b6b1e453b8..cbd1fdeea2a 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,6 +1,6 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; @@ -30,10 +30,10 @@ pub fn check_validity_requirement<'tcx>( return Ok(!layout.abi.is_uninhabited()); } + let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { - might_permit_raw_init_strict(layout, tcx, kind) + might_permit_raw_init_strict(layout, &layout_cx, kind) } else { - let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; might_permit_raw_init_lax(layout, &layout_cx, kind) } } @@ -42,12 +42,12 @@ pub fn check_validity_requirement<'tcx>( /// details. fn might_permit_raw_init_strict<'tcx>( ty: TyAndLayout<'tcx>, - tcx: TyCtxt<'tcx>, + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, kind: ValidityRequirement, ) -> Result<bool, &'tcx LayoutError<'tcx>> { let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); - let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); + let mut cx = InterpCx::new(cx.tcx, rustc_span::DUMMY_SP, cx.param_env, machine); let allocated = cx .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index 9a0fd52677d..0f2c0eee27d 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -51,6 +51,15 @@ impl<T> Steal<T> { let value = value_ref.take(); value.expect("attempt to steal from stolen value") } + + /// Writers of rustc drivers often encounter stealing issues. This function makes it possible to + /// handle these errors gracefully. + /// + /// This should not be used within rustc as it leaks information not tracked + /// by the query system, breaking incremental compilation. + pub fn is_stolen(&self) -> bool { + self.value.borrow().is_none() + } } impl<CTX, T: HashStable<CTX>> HashStable<CTX> for Steal<T> { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 627a0ebb4e5..b014fc2dc58 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -51,7 +51,8 @@ use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ - nightly_options, ErrorOutputType, Input, OutFileName, OutputType, CG_OPTIONS, Z_OPTIONS, + nightly_options, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, CG_OPTIONS, + Z_OPTIONS, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -301,6 +302,8 @@ fn run_compiler( let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) }; let sopts = config::build_session_options(&mut default_early_dcx, &matches); + // fully initialize ice path static once unstable options are available as context + let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone(); if let Some(ref code) = matches.opt_str("explain") { handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); @@ -315,7 +318,7 @@ fn run_compiler( input: Input::File(PathBuf::new()), output_file: ofile, output_dir: odir, - ice_file: ice_path().clone(), + ice_file, file_loader, locale_resources: DEFAULT_LOCALE_RESOURCES, lint_caps: Default::default(), @@ -1306,25 +1309,43 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new(); +// This function should only be called from the ICE hook. +// +// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the +// initialization process to properly initialize the ICE_PATH static based on parsed CLI flags. +// +// Subsequent calls to either function will then return the proper ICE path as configured by +// the environment and cli flags fn ice_path() -> &'static Option<PathBuf> { + ice_path_with_config(None) +} + +fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<PathBuf> { + if ICE_PATH.get().is_some() && config.is_some() && cfg!(debug_assertions) { + tracing::warn!( + "ICE_PATH has already been initialized -- files may be emitted at unintended paths" + ) + } + ICE_PATH.get_or_init(|| { if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { return None; } - if let Some(s) = std::env::var_os("RUST_BACKTRACE") - && s == "0" - { - return None; - } let mut path = match std::env::var_os("RUSTC_ICE") { Some(s) => { if s == "0" { // Explicitly opting out of writing ICEs to disk. return None; } + if let Some(unstable_opts) = config && unstable_opts.metrics_dir.is_some() { + tracing::warn!("ignoring -Zerror-metrics in favor of RUSTC_ICE for destination of ICE report files"); + } PathBuf::from(s) } - None => std::env::current_dir().unwrap_or_default(), + None => config + .and_then(|unstable_opts| unstable_opts.metrics_dir.to_owned()) + .or_else(|| std::env::current_dir().ok()) + .unwrap_or_default(), }; let now: OffsetDateTime = SystemTime::now().into(); let file_now = now diff --git a/compiler/rustc_error_codes/src/error_codes/E0517.md b/compiler/rustc_error_codes/src/error_codes/E0517.md index ae802245bd1..5354a08bf31 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0517.md +++ b/compiler/rustc_error_codes/src/error_codes/E0517.md @@ -25,14 +25,17 @@ impl Foo { These attributes do not work on typedefs, since typedefs are just aliases. Representations like `#[repr(u8)]`, `#[repr(i64)]` are for selecting the -discriminant size for enums with no data fields on any of the variants, e.g. -`enum Color {Red, Blue, Green}`, effectively setting the size of the enum to -the size of the provided type. Such an enum can be cast to a value of the same -type as well. In short, `#[repr(u8)]` makes the enum behave like an integer -with a constrained set of allowed values. +discriminant size for enums. For enums with no data fields on any of the +variants, e.g. `enum Color {Red, Blue, Green}`, this effectively sets the size +of the enum to the size of the provided type. Such an enum can be cast to a +value of the same type as well. In short, `#[repr(u8)]` makes a field-less enum +behave like an integer with a constrained set of allowed values. -Only field-less enums can be cast to numerical primitives, so this attribute -will not apply to structs. +For a description of how `#[repr(C)]` and representations like `#[repr(u8)]` +affect the layout of enums with data fields, see [RFC 2195][rfc2195]. + +Only field-less enums can be cast to numerical primitives. Representations like +`#[repr(u8)]` will not apply to structs. `#[repr(packed)]` reduces padding to make the struct size smaller. The representation of enums isn't strictly defined in Rust, and this attribute @@ -42,3 +45,5 @@ won't work on enums. types (i.e., `u8`, `i32`, etc) a representation that permits vectorization via SIMD. This doesn't make much sense for enums since they don't consist of a single list of data. + +[rfc2195]: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e1dcbf5b280..fae8b5647fc 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -741,6 +741,16 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] + pub fn highlighted_span_note( + &mut self, + span: impl Into<MultiSpan>, + msg: Vec<StringPart>, + ) -> &mut Self { + self.sub_with_highlights(Level::Note, msg, span.into()); + self + } + /// This is like [`Diag::note()`], but it's only printed once. #[rustc_lint_diagnostics] pub fn note_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self { @@ -815,6 +825,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + /// Add a help message attached to this diagnostic with a customizable highlighted message. + #[rustc_lint_diagnostics] + pub fn highlighted_span_help( + &mut self, + span: impl Into<MultiSpan>, + msg: Vec<StringPart>, + ) -> &mut Self { + self.sub_with_highlights(Level::Help, msg, span.into()); + self + } + /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] @@ -899,8 +920,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - suggestion.sort_unstable(); - suggestion.dedup_by(|(s1, m1), (s2, m2)| s1.source_equal(*s2) && m1 == m2); + let mut seen = crate::FxHashSet::default(); + suggestion.retain(|(span, msg)| seen.insert((span.lo(), span.hi(), msg.clone()))); let parts = suggestion .into_iter() diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 483b757f20c..b8813ea4125 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -43,19 +43,14 @@ const DEFAULT_COLUMN_WIDTH: usize = 140; /// Describes the way the content of the `rendered` field of the json output is generated #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HumanReadableErrorType { - Default(ColorConfig), - AnnotateSnippet(ColorConfig), - Short(ColorConfig), + Default, + AnnotateSnippet, + Short, } impl HumanReadableErrorType { - /// Returns a (`short`, `color`) tuple - pub fn unzip(self) -> (bool, ColorConfig) { - match self { - HumanReadableErrorType::Default(cc) => (false, cc), - HumanReadableErrorType::Short(cc) => (true, cc), - HumanReadableErrorType::AnnotateSnippet(cc) => (false, cc), - } + pub fn short(&self) -> bool { + *self == HumanReadableErrorType::Short } } @@ -1347,7 +1342,7 @@ impl HumanEmitter { label_width += 2; } let mut line = 0; - for (text, _) in msgs.iter() { + for (text, style) in msgs.iter() { let text = self.translate_message(text, args).map_err(Report::new).unwrap(); // Account for newlines to align output to its label. for text in normalize_whitespace(&text).lines() { @@ -1358,10 +1353,21 @@ impl HumanEmitter { if line == 0 { String::new() } else { " ".repeat(label_width) }, text ), - header_style, + match style { + Style::Highlight => *style, + _ => header_style, + }, ); line += 1; } + // We add lines above, but if the last line has no explicit newline (which would + // yield an empty line), then we revert one line up to continue with the next + // styled text chunk on the same line as the last one from the prior one. Otherwise + // every `text` would appear on their own line (because even though they didn't end + // in '\n', they advanced `line` by one). + if line > 0 { + line -= 1; + } } if self.short_message { let labels = msp @@ -2584,9 +2590,7 @@ fn num_decimal_digits(num: usize) -> usize { // We replace some characters so the CLI output is always consistent and underlines aligned. // Keep the following list in sync with `rustc_span::char_width`. -// ATTENTION: keep lexicografically sorted so that the binary search will work const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ - // tidy-alphabetical-start // In terminals without Unicode support the following will be garbled, but in *all* terminals // the underlying codepoint will be as well. We could gate this replacement behind a "unicode // support" gate. @@ -2599,7 +2603,7 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{0006}', "␆"), ('\u{0007}', "␇"), ('\u{0008}', "␈"), - ('\u{0009}', " "), // We do our own tab replacement + ('\t', " "), // We do our own tab replacement ('\u{000b}', "␋"), ('\u{000c}', "␌"), ('\u{000d}', "␍"), @@ -2632,13 +2636,23 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{2067}', "�"), ('\u{2068}', "�"), ('\u{2069}', "�"), - // tidy-alphabetical-end ]; fn normalize_whitespace(s: &str) -> String { - // Scan the input string for a character in the ordered table above. If it's present, replace - // it with it's alternative string (it can be more than 1 char!). Otherwise, retain the input - // char. At the end, allocate all chars into a string in one operation. + const { + let mut i = 1; + while i < OUTPUT_REPLACEMENTS.len() { + assert!( + OUTPUT_REPLACEMENTS[i - 1].0 < OUTPUT_REPLACEMENTS[i].0, + "The OUTPUT_REPLACEMENTS array must be sorted (for binary search to work) \ + and must contain no duplicate entries" + ); + i += 1; + } + } + // Scan the input string for a character in the ordered table above. + // If it's present, replace it with its alternative string (it can be more than 1 char!). + // Otherwise, retain the input char. s.chars().fold(String::with_capacity(s.len()), |mut s, c| { match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) { Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1), diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 42a28bc7890..32e59f9ab03 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -55,6 +55,7 @@ pub struct JsonEmitter { ignored_directories_in_source_blocks: Vec<String>, #[setters(skip)] json_rendered: HumanReadableErrorType, + color_config: ColorConfig, diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, @@ -68,6 +69,7 @@ impl JsonEmitter { fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, + color_config: ColorConfig, ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), @@ -79,6 +81,7 @@ impl JsonEmitter { ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), json_rendered, + color_config, diagnostic_width: None, macro_backtrace: false, track_diagnostics: false, @@ -173,7 +176,7 @@ impl Emitter for JsonEmitter { } fn should_show_explain(&self) -> bool { - !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) + !self.json_rendered.short() } } @@ -353,8 +356,8 @@ impl Diagnostic { let buf = BufWriter::default(); let mut dst: Destination = Box::new(buf.clone()); - let (short, color_config) = je.json_rendered.unzip(); - match color_config { + let short = je.json_rendered.short(); + match je.color_config { ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Never => {} } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index e3549fc3aa5..6af376d7afd 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -50,7 +50,8 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { sm, fallback_bundle, true, // pretty - HumanReadableErrorType::Short(ColorConfig::Never), + HumanReadableErrorType::Short, + ColorConfig::Never, ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 6ce9ff18c27..628c6bfeb79 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -3,34 +3,32 @@ use std::borrow::Cow; use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, Diag, DiagMessage}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery}; +use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use tracing::debug; use super::macro_rules::{parser_from_cx, NoopTracker}; -use crate::base::{DummyResult, ExtCtxt, MacResult}; use crate::expand::{parse_ast_fragment, AstFragmentKind}; use crate::mbe::macro_parser::ParseResult::*; use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser}; use crate::mbe::macro_rules::{try_match_macro, Tracker}; -pub(super) fn failed_to_match_macro<'cx>( - cx: &'cx mut ExtCtxt<'_>, +pub(super) fn failed_to_match_macro( + psess: &ParseSess, sp: Span, def_span: Span, name: Ident, arg: TokenStream, lhses: &[Vec<MatcherLoc>], -) -> Box<dyn MacResult + 'cx> { - let psess = &cx.sess.psess; - +) -> (Span, ErrorGuaranteed) { // An error occurred, try the expansion again, tracking the expansion closely for better // diagnostics. - let mut tracker = CollectTrackerAndEmitter::new(cx, sp); + let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); let try_success_result = try_match_macro(psess, name, &arg, lhses, &mut tracker); @@ -38,7 +36,7 @@ pub(super) fn failed_to_match_macro<'cx>( // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already assert!( - tracker.cx.dcx().has_errors().is_some(), + tracker.dcx.has_errors().is_some(), "Macro matching returned a success on the second try" ); } @@ -50,15 +48,15 @@ pub(super) fn failed_to_match_macro<'cx>( let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure else { - return DummyResult::any(sp, cx.dcx().span_delayed_bug(sp, "failed to match a macro")); + return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro")); }; let span = token.span.substitute_dummy(sp); - let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token, None)); + let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)); err.span_label(span, label); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { - err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); + if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { + err.span_label(psess.source_map().guess_head_span(def_span), "when calling this macro"); } annotate_doc_comment(&mut err, psess.source_map(), span); @@ -76,7 +74,7 @@ pub(super) fn failed_to_match_macro<'cx>( err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens"); err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information"); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { + if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { err.help("try using `:tt` instead in the macro definition"); } } @@ -104,18 +102,17 @@ pub(super) fn failed_to_match_macro<'cx>( } } let guar = err.emit(); - cx.trace_macros_diag(); - DummyResult::any(sp, guar) + (sp, guar) } /// The tracker used for the slow error path that collects useful info for diagnostics. -struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> { - cx: &'a mut ExtCtxt<'cx>, +struct CollectTrackerAndEmitter<'dcx, 'matcher> { + dcx: DiagCtxtHandle<'dcx>, remaining_matcher: Option<&'matcher MatcherLoc>, /// Which arm's failure should we report? (the one furthest along) best_failure: Option<BestFailure>, root_span: Span, - result: Option<Box<dyn MacResult + 'cx>>, + result: Option<(Span, ErrorGuaranteed)>, } struct BestFailure { @@ -131,7 +128,7 @@ impl BestFailure { } } -impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> { +impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> { type Failure = (Token, u32, &'static str); fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure { @@ -151,7 +148,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, Success(_) => { // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already - self.cx.dcx().span_delayed_bug( + self.dcx.span_delayed_bug( self.root_span, "should not collect detailed info for successful macro match", ); @@ -177,10 +174,10 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } Error(err_sp, msg) => { let span = err_sp.substitute_dummy(self.root_span); - let guar = self.cx.dcx().span_err(span, msg.clone()); - self.result = Some(DummyResult::any(span, guar)); + let guar = self.dcx.span_err(span, msg.clone()); + self.result = Some((span, guar)); } - ErrorReported(guar) => self.result = Some(DummyResult::any(self.root_span, *guar)), + ErrorReported(guar) => self.result = Some((self.root_span, *guar)), } } @@ -193,9 +190,9 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } } -impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> { - fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self { - Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None } +impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> { + fn new(dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self { + Self { dcx, remaining_matcher: None, best_failure: None, root_span, result: None } } } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 1502177563d..6f177107e70 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -268,7 +268,10 @@ fn expand_macro<'cx>( } Err(CanRetry::Yes) => { // Retry and emit a better error. - diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) + let (span, guar) = + diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, lhses); + cx.trace_macros_diag(); + DummyResult::any(span, guar) } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 9257be86471..33e8432596b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3700,6 +3700,11 @@ impl<'hir> OwnerNode<'hir> { } } + /// Check if node is an impl block. + pub fn is_impl_block(&self) -> bool { + matches!(self, OwnerNode::Item(Item { kind: ItemKind::Impl(_), .. })) + } + expect_methods_self! { expect_item, &'hir Item<'hir>, OwnerNode::Item(n), n; expect_foreign_item, &'hir ForeignItem<'hir>, OwnerNode::ForeignItem(n), n; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c878095ba0d..a9f8630741a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1276,7 +1276,7 @@ fn check_item_type( UnsizedHandling::Forbid => true, UnsizedHandling::Allow => false, UnsizedHandling::AllowIfForeignTail => { - let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env); + let tail = tcx.struct_tail_for_codegen(item_ty, wfcx.param_env); !matches!(tail.kind(), ty::Foreign(_)) } }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index f18224c39ae..a59e9aa85fd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -253,32 +253,6 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( match (args_iter.peek(), params.peek()) { (Some(&arg), Some(¶m)) => { match (arg, ¶m.kind, arg_count.explicit_late_bound) { - ( - GenericArg::Const(hir::ConstArg { - is_desugared_from_effects: true, - .. - }), - GenericParamDefKind::Const { is_host_effect: false, .. } - | GenericParamDefKind::Type { .. } - | GenericParamDefKind::Lifetime, - _, - ) => { - // FIXME(effects): this should be removed - // SPECIAL CASE FOR DESUGARED EFFECT PARAMS - // This comes from the following example: - // - // ``` - // #[const_trait] - // pub trait PartialEq<Rhs: ?Sized = Self> {} - // impl const PartialEq for () {} - // ``` - // - // Since this is a const impl, we need to insert a host arg at the end of - // `PartialEq`'s generics, but this errors since `Rhs` isn't specified. - // To work around this, we infer all arguments until we reach the host param. - args.push(ctx.inferred_kind(&args, param, infer_args)); - params.next(); - } (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) | ( GenericArg::Type(_) | GenericArg::Infer(_), @@ -552,21 +526,34 @@ pub(crate) fn check_generic_arg_count( synth_provided, } } else { - let num_missing_args = expected_max - provided; + // Check if associated type bounds are incorrectly written in impl block header like: + // ``` + // trait Foo<T> {} + // impl Foo<T: Default> for u8 {} + // ``` + let parent_is_impl_block = cx + .tcx() + .hir() + .parent_owner_iter(seg.hir_id) + .next() + .is_some_and(|(_, owner_node)| owner_node.is_impl_block()); + if parent_is_impl_block { + let constraint_names: Vec<_> = + gen_args.constraints.iter().map(|b| b.ident.name).collect(); + let param_names: Vec<_> = gen_params + .own_params + .iter() + .filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter + .map(|param| param.name) + .collect(); + if constraint_names == param_names { + // We set this to true and delay emitting `WrongNumberOfGenericArgs` + // to provide a succinct error for cases like issue #113073 + all_params_are_binded = true; + }; + } - let constraint_names: Vec<_> = - gen_args.constraints.iter().map(|b| b.ident.name).collect(); - let param_names: Vec<_> = gen_params - .own_params - .iter() - .filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter - .map(|param| param.name) - .collect(); - if constraint_names == param_names { - // We set this to true and delay emitting `WrongNumberOfGenericArgs` - // to provide a succinct error for cases like issue #113073 - all_params_are_binded = true; - }; + let num_missing_args = expected_max - provided; GenericArgsInfo::MissingTypesOrConsts { num_missing_args, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e54f9486f6a..d6ff0cb9462 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1736,6 +1736,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, guar) }; + // Check that the expected field type is WF. Otherwise, we emit no use-site error + // in the case of coercions for non-WF fields, which leads to incorrect error + // tainting. See issue #126272. + self.register_wf_obligation( + field_type.into(), + field.expr.span, + ObligationCauseCode::WellFormed(None), + ); + // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. let ty = self.check_expr_with_hint(field.expr, field_type); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 8c1aa66332f..841d25b54cc 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -404,8 +404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { code: traits::ObligationCauseCode<'tcx>, ) { if !ty.references_error() { - let tail = - self.tcx.struct_tail_with_normalize(ty, |ty| self.normalize(span, ty), || {}); + let tail = self.tcx.struct_tail_with_normalize( + ty, + |ty| { + if self.next_trait_solver() { + self.try_structurally_resolve_type(span, ty) + } else { + self.normalize(span, ty) + } + }, + || {}, + ); // Sized types have static alignment, and so do slices. if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) { // Nothing else is required here. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index cef003e0a43..89e7227eda2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; @@ -1140,8 +1140,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .get(arg_idx + 1) .map(|&(_, sp)| sp) .unwrap_or_else(|| { - // Subtract one to move before `)` - call_expr.span.with_lo(call_expr.span.hi() - BytePos(1)) + // Try to move before `)`. Note that `)` here is not necessarily + // the latin right paren, it could be a Unicode-confusable that + // looks like a `)`, so we must not use `- BytePos(1)` + // manipulations here. + self.tcx().sess.source_map().end_point(call_expr.span) }); // Include next comma diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e64f4ebf45d..55f002291f0 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -219,7 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `async-await/async-closures/force-move-due-to-inferred-kind.rs`. // // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it - // uses its upvars, but not *all* upvars would force the closure to `FnOnce`. + // uses its upvars (e.g. it consumes a non-copy value), but not *all* upvars + // would force the closure to `FnOnce`. // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`. // // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` @@ -227,11 +228,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we force the inner coroutine to also be `move`. This only matters for // coroutine-closures that are `move` since otherwise they themselves will // be borrowing from the outer environment, so there's no self-borrows occuring. - // - // One *important* note is that we do a call to `process_collected_capture_information` - // to eagerly test whether the coroutine would end up `FnOnce`, but we do this - // *before* capturing all the closure args by-value below, since that would always - // cause the analysis to return `FnOnce`. if let UpvarArgs::Coroutine(..) = args && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") @@ -246,19 +242,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause = hir::CaptureBy::Value { move_kw }; } // (2.) The way that the closure uses its upvars means it's `FnOnce`. - else if let (_, ty::ClosureKind::FnOnce, _) = self - .process_collected_capture_information( - capture_clause, - &delegate.capture_information, - ) - { + else if self.coroutine_body_consumes_upvars(closure_def_id, body) { capture_clause = hir::CaptureBy::Value { move_kw }; } } // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode // to `ByRef` for the `async {}` block internal to async fns/closure. This means - // that we would *not* be moving all of the parameters into the async block by default. + // that we would *not* be moving all of the parameters into the async block in all cases. + // For example, when one of the arguments is `Copy`, we turn a consuming use into a copy of + // a reference, so for `async fn x(t: i32) {}`, we'd only take a reference to `t`. // // We force all of these arguments to be captured by move before we do expr use analysis. // @@ -535,6 +528,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Determines whether the body of the coroutine uses its upvars in a way that + /// consumes (i.e. moves) the value, which would force the coroutine to `FnOnce`. + /// In a more detailed comment above, we care whether this happens, since if + /// this happens, we want to force the coroutine to move all of the upvars it + /// would've borrowed from the parent coroutine-closure. + /// + /// This only really makes sense to be called on the child coroutine of a + /// coroutine-closure. + fn coroutine_body_consumes_upvars( + &self, + coroutine_def_id: LocalDefId, + body: &'tcx hir::Body<'tcx>, + ) -> bool { + // This block contains argument capturing details. Since arguments + // aren't upvars, we do not care about them for determining if the + // coroutine body actually consumes its upvars. + let hir::ExprKind::Block(&hir::Block { expr: Some(body), .. }, None) = body.value.kind + else { + bug!(); + }; + // Specifically, we only care about the *real* body of the coroutine. + // We skip out into the drop-temps within the block of the body in order + // to skip over the args of the desugaring. + let hir::ExprKind::DropTemps(body) = body.kind else { + bug!(); + }; + + let mut delegate = InferBorrowKind { + closure_def_id: coroutine_def_id, + capture_information: Default::default(), + fake_reads: Default::default(), + }; + + let _ = euv::ExprUseVisitor::new( + &FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id), + &mut delegate, + ) + .consume_expr(body); + + let (_, kind, _) = self.process_collected_capture_information( + hir::CaptureBy::Ref, + &delegate.capture_information, + ); + + matches!(kind, ty::ClosureKind::FnOnce) + } + // Returns a list of `Ty`s for each upvar. fn final_upvar_tys(&self, closure_id: LocalDefId) -> Vec<Ty<'tcx>> { self.typeck_results diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8c99b1f4447..96a6f52d60b 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -818,6 +818,19 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { }); sess.time("layout_testing", || layout_test::test_layout(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); + + // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item + // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs + // in MIR optimizations that may only be reachable through codegen, or other codepaths + // that requires the optimized/ctfe MIR, such as polymorphization, coroutine bodies, + // or evaluating consts. + if tcx.sess.opts.unstable_opts.validate_mir { + sess.time("ensuring_final_MIR_is_computable", || { + tcx.hir().par_body_owners(|def_id| { + tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); + }); + }); + } } /// Runs the type-checking, region checking and other miscellaneous analysis diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index ce3b2f77f21..34f2dca7c42 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -315,7 +315,8 @@ fn test_search_paths_tracking_hash_different_order() { let early_dcx = EarlyDiagCtxt::new(JSON); const JSON: ErrorOutputType = ErrorOutputType::Json { pretty: false, - json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), + json_rendered: HumanReadableErrorType::Default, + color_config: ColorConfig::Never, }; let push = |opts: &mut Options, search_path| { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7e4feb0a827..7a394a6d6c1 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -518,8 +518,8 @@ lint_non_binding_let_multi_suggestion = lint_non_binding_let_on_drop_type = non-binding let on a type that implements `Drop` -lint_non_binding_let_on_sync_lock = - non-binding let on a synchronization lock +lint_non_binding_let_on_sync_lock = non-binding let on a synchronization lock + .label = this lock is not assigned to a binding and is immediately dropped lint_non_binding_let_suggestion = consider binding to an unused variable to avoid immediately dropping the value @@ -775,6 +775,10 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value +lint_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag + .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}` + .incoherent = manually setting a built-in cfg can and does create incoherent behaviors + lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs` lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 11ad1aa0e8d..c9e2eee16b3 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -533,7 +533,7 @@ pub struct EarlyContext<'a> { } impl EarlyContext<'_> { - /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// Emit a lint at the appropriate level, with an associated span and an existing /// diagnostic. /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature @@ -544,7 +544,21 @@ impl EarlyContext<'_> { span: MultiSpan, diagnostic: BuiltinLintDiag, ) { - self.opt_span_lint(lint, Some(span), |diag| { + self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic); + } + + /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// diagnostic. + /// + /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature + #[rustc_lint_diagnostics] + pub fn opt_span_lint_with_diagnostics( + &self, + lint: &'static Lint, + span: Option<MultiSpan>, + diagnostic: BuiltinLintDiag, + ) { + self.opt_span_lint(lint, span, |diag| { diagnostics::decorate_lint(self.sess(), diagnostic, diag); }); } diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index a96af076477..f289d4c81b3 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -438,5 +438,8 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::OutOfScopeMacroCalls { path } => { lints::OutOfScopeMacroCalls { path }.decorate_lint(diag) } + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { + lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 7b04e8c39e6..6fb0a624644 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -47,7 +47,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.span_lint_with_diagnostics(lint_id.lint, span, diagnostic); + self.context.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic); } } diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 92db8a88e42..1368cc87e3e 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -104,7 +104,6 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; @@ -156,12 +155,12 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)), }; if is_sync_lock { - let mut span = MultiSpan::from_span(pat.span); - span.push_span_label( - pat.span, - "this lock is not assigned to a binding and is immediately dropped".to_string(), + let span = MultiSpan::from_span(pat.span); + cx.emit_span_lint( + LET_UNDERSCORE_LOCK, + span, + NonBindingLet::SyncLock { sub, pat: pat.span }, ); - cx.emit_span_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub }); // Only emit let_underscore_drop for top-level `_` patterns. } else if can_use_init.is_some() { cx.emit_span_lint(LET_UNDERSCORE_DROP, local.span, NonBindingLet::DropType { sub }); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 46e7655a656..03962d796f4 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -957,6 +957,8 @@ pub struct BadOptAccessDiag<'a> { pub enum NonBindingLet { #[diag(lint_non_binding_let_on_sync_lock)] SyncLock { + #[label] + pat: Span, #[subdiagnostic] sub: NonBindingLetSub, }, @@ -2378,6 +2380,16 @@ pub mod unexpected_cfg_value { } #[derive(LintDiagnostic)] +#[diag(lint_unexpected_builtin_cfg)] +#[note(lint_controlled_by)] +#[note(lint_incoherent)] +pub struct UnexpectedBuiltinCfg { + pub(crate) cfg: String, + pub(crate) cfg_name: Symbol, + pub(crate) controlled_by: &'static str, +} + +#[derive(LintDiagnostic)] #[diag(lint_macro_use_deprecated)] #[help] pub struct MacroUseDeprecated; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f3196cfed53..5b17c0d718a 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -458,8 +458,11 @@ fn lint_int_literal<'tcx>( } let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; - let lit = - cx.sess().source_map().span_to_snippet(span).expect("must get snippet from literal"); + let lit = cx + .sess() + .source_map() + .span_to_snippet(span) + .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); @@ -485,6 +488,7 @@ fn lint_uint_literal<'tcx>( ast::LitKind::Int(v, _) => v.get(), _ => bug!(), }; + if lit_val < min || lit_val > max { if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { match par_e.kind { @@ -526,7 +530,7 @@ fn lint_uint_literal<'tcx>( .sess() .source_map() .span_to_snippet(lit.span) - .expect("must get snippet from literal"), + .unwrap_or_else(|_| lit_val.to_string()), min, max, }, @@ -551,14 +555,14 @@ fn lint_literal<'tcx>( } ty::Uint(t) => lint_uint_literal(cx, e, lit, t), ty::Float(t) => { - let is_infinite = match lit.node { + let (is_infinite, sym) = match lit.node { ast::LitKind::Float(v, _) => match t { // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI // issues resolved). - ty::FloatTy::F16 => Ok(false), - ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), - ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), - ty::FloatTy::F128 => Ok(false), + ty::FloatTy::F16 => (Ok(false), v), + ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), + ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), + ty::FloatTy::F128 => (Ok(false), v), }, _ => bug!(), }; @@ -572,7 +576,7 @@ fn lint_literal<'tcx>( .sess() .source_map() .span_to_snippet(lit.span) - .expect("must get snippet from literal"), + .unwrap_or_else(|_| sym.to_string()), }, ); } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 795333224ba..e2c05129ee2 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -675,6 +675,13 @@ trait UnusedDelimLint { return true; } + // Do not lint against parentheses around `&raw [const|mut] expr`. + // These parentheses will have to be added e.g. when calling a method on the result of this + // expression, and we want to avoid churn wrt adding and removing parentheses. + if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) { + return true; + } + // Check if LHS needs parens to prevent false-positives in cases like // `fn x() -> u8 { ({ 0 } + 1) }`. // diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff0bdfcc9d2..c731b03a875 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -42,6 +42,7 @@ declare_lint_pass! { DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, + EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, FORBIDDEN_LINT_GROUPS, @@ -3289,6 +3290,39 @@ declare_lint! { } declare_lint! { + /// The `explicit_builtin_cfgs_in_flags` lint detects builtin cfgs set via the `--cfg` flag. + /// + /// ### Example + /// + /// ```text + /// rustc --cfg unix + /// ``` + /// + /// ```rust,ignore (needs command line option) + /// fn main() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: unexpected `--cfg unix` flag + /// | + /// = note: config `unix` is only supposed to be controlled by `--target` + /// = note: manually setting a built-in cfg can and does create incoherent behaviors + /// = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// Setting builtin cfgs can and does produce incoherent behavior, it's better to the use + /// the appropriate `rustc` flag that controls the config. For example setting the `windows` + /// cfg but on Linux based target. + pub EXPLICIT_BUILTIN_CFGS_IN_FLAGS, + Deny, + "detects builtin cfgs set via the `--cfg`" +} + +declare_lint! { /// The `repr_transparent_external_private_fields` lint /// detects types marked `#[repr(transparent)]` that (transitively) /// contain an external ZST type marked `#[non_exhaustive]` or containing diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 19efa36b40f..0f07de43e80 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -746,6 +746,11 @@ pub enum BuiltinLintDiag { OutOfScopeMacroCalls { path: String, }, + UnexpectedBuiltinCfg { + cfg: String, + cfg_name: Symbol, + controlled_by: &'static str, + }, } /// Lints that are buffered up early on in the `Session` before the @@ -753,7 +758,7 @@ pub enum BuiltinLintDiag { #[derive(Debug)] pub struct BufferedEarlyLint { /// The span of code that we are linting on. - pub span: MultiSpan, + pub span: Option<MultiSpan>, /// The `NodeId` of the AST node that generated the lint. pub node_id: NodeId, @@ -791,7 +796,7 @@ impl LintBuffer { self.add_early_lint(BufferedEarlyLint { lint_id: LintId::of(lint), node_id, - span: span.into(), + span: Some(span.into()), diagnostic, }); } diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 4c1f78e6bee..b2ff9efb41c 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -259,6 +259,7 @@ fn main() { cmd.args(&components); for lib in output(&mut cmd).split_whitespace() { + let mut is_static = false; let name = if let Some(stripped) = lib.strip_prefix("-l") { stripped } else if let Some(stripped) = lib.strip_prefix('-') { @@ -266,8 +267,24 @@ fn main() { } else if Path::new(lib).exists() { // On MSVC llvm-config will print the full name to libraries, but // we're only interested in the name part - let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); - name.trim_end_matches(".lib") + // On Unix when we get a static library llvm-config will print the + // full name and we *are* interested in the path, but we need to + // handle it separately. For example, when statically linking to + // libzstd llvm-config will output something like + // -lrt -ldl -lm -lz /usr/local/lib/libzstd.a -lxml2 + // and we transform the zstd part into + // cargo:rustc-link-search-native=/usr/local/lib + // cargo:rustc-link-lib=static=zstd + let path = Path::new(lib); + if lib.ends_with(".a") { + is_static = true; + println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); + let name = path.file_stem().unwrap().to_str().unwrap(); + name.trim_start_matches("lib") + } else { + let name = path.file_name().unwrap().to_str().unwrap(); + name.trim_end_matches(".lib") + } } else if lib.ends_with(".lib") { // Some MSVC libraries just come up with `.lib` tacked on, so chop // that off @@ -285,7 +302,13 @@ fn main() { continue; } - let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; + let kind = if name.starts_with("LLVM") { + llvm_kind + } else if is_static { + "static" + } else { + "dylib" + }; println!("cargo:rustc-link-lib={kind}={name}"); } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 6a9e67f74da..8c27cac1ea8 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -228,9 +228,11 @@ pub fn explain_lint_level_source( err.note_once(format!( "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`" )); - err.help_once(format!( - "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`" - )); + if matches!(orig_level, Level::Warn | Level::Deny) { + err.help_once(format!( + "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`" + )); + } } } LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index ff6a3a9c12d..b7d290e58d2 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -28,7 +28,7 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option<u16>, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). - pub target_features: Vec<Symbol>, + pub target_features: Vec<TargetFeature>, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option<Linkage>, /// The `#[linkage = "..."]` attribute on foreign items and the value we found. @@ -52,6 +52,15 @@ pub struct CodegenFnAttrs { } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct TargetFeature { + /// The name of the target feature (e.g. "avx") + pub name: Symbol, + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute + pub implied: bool, +} + +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function prefix: u8, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c22c2e985ab..5b114c9515c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2183,6 +2183,12 @@ rustc_queries! { desc { "looking up supported target features" } } + query implied_target_features(feature: Symbol) -> &'tcx Vec<Symbol> { + arena_cache + eval_always + desc { "looking up implied target features" } + } + query features_query(_: ()) -> &'tcx rustc_feature::Features { feedable desc { "looking up enabled feature gates" } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d7d27975f60..9204405d58f 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -869,7 +869,7 @@ where metadata } } else { - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { + match tcx.struct_tail_for_codegen(pointee, cx.param_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()), _ => bug!("TyAndLayout::field({:?}): not applicable", this), @@ -1348,7 +1348,7 @@ impl<'tcx> TyCtxt<'tcx> { layout = layout.field(&cx, index); if !layout.is_sized() { // If it is not sized, then the tail must still have at least a known static alignment. - let tail = self.struct_tail_erasing_lifetimes(layout.ty, param_env); + let tail = self.struct_tail_for_codegen(layout.ty, param_env); if !matches!(tail.kind(), ty::Slice(..)) { bug!( "offset of not-statically-aligned field (type {:?}) cannot be computed statically", diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3cf8531bb62..365f434a264 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -186,11 +186,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Should only be called if `ty` has no inference variables and does not /// need its lifetimes preserved (e.g. as part of codegen); otherwise /// normalization attempt may cause compiler bugs. - pub fn struct_tail_erasing_lifetimes( - self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Ty<'tcx> { + pub fn struct_tail_for_codegen(self, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { let tcx = self; tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty), || {}) } @@ -203,7 +199,7 @@ impl<'tcx> TyCtxt<'tcx> { /// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity /// function to indicate no normalization should take place. /// - /// See also `struct_tail_erasing_lifetimes`, which is suitable for use + /// See also `struct_tail_for_codegen`, which is suitable for use /// during codegen. pub fn struct_tail_with_normalize( self, @@ -272,13 +268,13 @@ impl<'tcx> TyCtxt<'tcx> { /// Same as applying `struct_tail` on `source` and `target`, but only /// keeps going as long as the two types are instances of the same /// structure definitions. - /// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`, + /// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, dyn Trait)`, /// whereas struct_tail produces `T`, and `Trait`, respectively. /// /// Should only be called if the types have no inference variables and do /// not need their lifetimes preserved (e.g., as part of codegen); otherwise, /// normalization attempt may cause compiler bugs. - pub fn struct_lockstep_tails_erasing_lifetimes( + pub fn struct_lockstep_tails_for_codegen( self, source: Ty<'tcx>, target: Ty<'tcx>, @@ -296,7 +292,7 @@ impl<'tcx> TyCtxt<'tcx> { /// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`, /// whereas struct_tail produces `T`, and `Trait`, respectively. /// - /// See also `struct_lockstep_tails_erasing_lifetimes`, which is suitable for use + /// See also `struct_lockstep_tails_for_codegen`, which is suitable for use /// during codegen. pub fn struct_lockstep_tails_with_normalize( self, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 48018fcaa36..54a4204da71 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,6 +5,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; use rustc_middle::thir::visit::Visitor; @@ -31,7 +32,7 @@ struct UnsafetyVisitor<'a, 'tcx> { safety_context: SafetyContext, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). - body_target_features: &'tcx [Symbol], + body_target_features: &'tcx [TargetFeature], /// When inside the LHS of an assignment to a field, this is the type /// of the LHS and the span of the assignment expression. assignment_info: Option<Ty<'tcx>>, @@ -442,14 +443,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { // is_like_wasm check in hir_analysis/src/collect.rs let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; if !self.tcx.sess.target.options.is_like_wasm - && !callee_features - .iter() - .all(|feature| self.body_target_features.contains(feature)) + && !callee_features.iter().all(|feature| { + self.body_target_features.iter().any(|f| f.name == feature.name) + }) { let missing: Vec<_> = callee_features .iter() .copied() - .filter(|feature| !self.body_target_features.contains(feature)) + .filter(|feature| { + !feature.implied + && !self + .body_target_features + .iter() + .any(|body_feature| body_feature.name == feature.name) + }) + .map(|feature| feature.name) .collect(); let build_enabled = self .tcx diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f30732e6aaf..324ddc5e799 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -479,7 +479,9 @@ impl<'tcx> Inliner<'tcx> { return Err("incompatible instruction set"); } - if callee_attrs.target_features != self.codegen_fn_attrs.target_features { + let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); + let this_feature_names = self.codegen_fn_attrs.target_features.iter().map(|f| f.name); + if callee_feature_names.ne(this_feature_names) { // In general it is not correct to inline a callee with target features that are a // subset of the caller. This is because the callee might contain calls, and the ABI of // those calls depends on the target features of the surrounding function. By moving a @@ -503,6 +505,10 @@ impl<'tcx> Inliner<'tcx> { ) -> Result<(), &'static str> { let tcx = self.tcx; + if let Some(_) = callee_body.tainted_by_errors { + return Err("Body is tainted"); + } + let mut threshold = if self.caller_is_inline_forwarder { self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) } else if cross_crate_inlinable { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index df2abf6dc9c..0ae635f9b73 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1019,7 +1019,7 @@ fn find_vtable_types_for_unsizing<'tcx>( if ty.is_sized(tcx.tcx, param_env) { return false; } - let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env); + let tail = tcx.struct_tail_for_codegen(ty, param_env); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, @@ -1029,7 +1029,7 @@ fn find_vtable_types_for_unsizing<'tcx>( if type_has_metadata(inner_source) { (inner_source, inner_target) } else { - tcx.struct_lockstep_tails_erasing_lifetimes(inner_source, inner_target, param_env) + tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, param_env) } }; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index e69d8d84d7d..00837f7cdd8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -458,28 +458,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: )) } - ty::FnDef(..) | ty::FnPtr(..) => { - let bound_sig = self_ty.fn_sig(cx); - let sig = bound_sig.skip_binder(); - let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); - // `FnDef` and `FnPtr` only implement `AsyncFn*` when their - // return type implements `Future`. - let nested = vec![ - bound_sig - .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])) - .upcast(cx), - ]; - let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); - let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); - Ok(( - bound_sig.rebind(AsyncCallableRelevantTypes { - tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), - output_coroutine_ty: sig.output(), - coroutine_return_ty: future_output_ty, - }), - nested, - )) + ty::FnDef(def_id, _) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } + } + ty::FnPtr(..) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } } + ty::Closure(_, args) => { let args = args.as_closure(); let bound_sig = args.sig(); @@ -563,6 +558,29 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: } } +fn fn_item_to_async_callable<I: Interner>( + cx: I, + bound_sig: ty::Binder<I, ty::FnSig<I>>, +) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> { + let sig = bound_sig.skip_binder(); + let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); + // `FnDef` and `FnPtr` only implement `AsyncFn*` when their + // return type implements `Future`. + let nested = vec![ + bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), + ]; + let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); + let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); + Ok(( + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), + nested, + )) +} + /// Given a coroutine-closure, project to its returned coroutine when we are *certain* /// that the closure's kind is compatible with the goal. fn coroutine_closure_to_certain_coroutine<I: Interner>( diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index b3efb87a4a2..a3b782d651d 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -408,10 +408,14 @@ impl<'a> Parser<'a> { fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> { let eq_consumed = match self.token.kind { token::BinOpEq(..) => { - // Recover `let x <op>= 1` as `let x = 1` + // Recover `let x <op>= 1` as `let x = 1` We must not use `+ BytePos(1)` here + // because `<op>` can be a multi-byte lookalike that was recovered, e.g. `➖=` (the + // `➖` is a U+2796 Heavy Minus Sign Unicode Character) that was recovered as a + // `-=`. + let extra_op_span = self.psess.source_map().start_point(self.token.span); self.dcx().emit_err(errors::CompoundAssignmentExpressionInLet { span: self.token.span, - suggestion: self.token.span.with_hi(self.token.span.lo() + BytePos(1)), + suggestion: extra_op_span, }); self.bump(); true diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a0d0d80b957..c8d4c190113 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -125,6 +125,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::optimize, ..] => self.check_optimize(hir_id, attr, target), + [sym::no_sanitize, ..] => self.check_no_sanitize(hir_id, attr, span, target), [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), [sym::target_feature, ..] => { @@ -250,12 +251,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::deny | sym::forbid | sym::cfg + | sym::cfg_attr // need to be fixed | sym::cfi_encoding // FIXME(cfi_encoding) | sym::may_dangle // FIXME(dropck_eyepatch) | sym::pointee // FIXME(derive_smart_pointer) | sym::linkage // FIXME(linkage) - | sym::no_sanitize // FIXME(no_sanitize) | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) | sym::used // handled elsewhere to restrict to static items | sym::repr // handled elsewhere to restrict to type decls items @@ -450,6 +451,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks that `#[no_sanitize(..)]` is applied to a function or method. + fn check_no_sanitize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + fn check_generic_attr( &self, hir_id: HirId, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2fa3692bb28..307625bcfb1 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -283,6 +283,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { parent_scope, finalize.then(|| Finalize::new(id, path.span)), None, + None, ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { let res = module.res().expect("visibility resolved to unnamed block"); @@ -372,7 +373,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { has_attributes: !item.attrs.is_empty(), root_span, root_id, - vis: Cell::new(Some(vis)), + vis, used: Default::default(), }); @@ -888,7 +889,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: item.span, span: item.span, module_path: Vec::new(), - vis: Cell::new(Some(vis)), + vis, used: Cell::new(used.then_some(Used::Other)), }); self.r.potentially_unused_imports.push(import); @@ -1089,7 +1090,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: Cell::new(Some(ty::Visibility::Restricted(CRATE_DEF_ID))), + vis: ty::Visibility::Restricted(CRATE_DEF_ID), used: Default::default(), }) }; @@ -1125,6 +1126,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { ident, MacroNS, &self.parent_scope, + None, ); if let Ok(binding) = result { let import = macro_use_import(self, ident.span, false); @@ -1253,7 +1255,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: Cell::new(Some(vis)), + vis, used: Cell::new(Some(Used::Other)), }); let import_binding = self.r.import(binding, import); diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index a8199971561..75b8ecebdd9 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -382,7 +382,7 @@ impl Resolver<'_, '_> { for import in self.potentially_unused_imports.iter() { match import.kind { _ if import.used.get().is_some() - || import.expect_vis().is_public() + || import.vis.is_public() || import.span.is_dummy() => { if let ImportKind::MacroUse { .. } = import.kind { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 8080bb60e41..942026ef012 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1052,6 +1052,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, false, false, + None, ) { suggestions.extend( ext.helper_attrs @@ -1506,6 +1507,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, None, + None, ) { let desc = match binding.res() { Res::Def(DefKind::Macro(MacroKind::Bang), _) => { @@ -1983,6 +1985,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, ribs: Option<&PerNS<Vec<Rib<'a>>>>, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, module: Option<ModuleOrUniformRoot<'a>>, failed_segment_idx: usize, ident: Ident, @@ -2066,11 +2069,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, None, ignore_binding, + ignore_import, ) .ok() } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns { + assert!(ignore_import.is_none()); match self.resolve_ident_in_lexical_scope( ident, ns_to_try, @@ -2091,6 +2096,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, ignore_binding, + ignore_import, ) .ok() }; @@ -2132,6 +2138,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { // Check whether the name refers to an item in the value namespace. let binding = if let Some(ribs) = ribs { + assert!(ignore_import.is_none()); self.resolve_ident_in_lexical_scope( ident, ValueNS, @@ -2206,6 +2213,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, ignore_binding, + ignore_import, ) { let descr = binding.res().descr(); (format!("{descr} `{ident}` is not a crate or module"), suggestion) @@ -2259,7 +2267,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2278,7 +2286,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some(( @@ -2309,7 +2317,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2343,7 +2351,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!( "make_external_crate_suggestion: name={:?} path={:?} result={:?}", name, path, result @@ -2509,12 +2517,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } /// Finds a cfg-ed out item inside `module` with the matching name. - pub(crate) fn find_cfg_stripped( - &mut self, - err: &mut Diag<'_>, - segment: &Symbol, - module: DefId, - ) { + pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { let local_items; let symbols = if module.is_local() { local_items = self diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 947ba569ab0..149c639efab 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -14,6 +14,7 @@ use Determinacy::*; use Namespace::*; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use crate::imports::Import; use crate::late::{ConstantHasGenerics, NoConstantGenericsReason, PathSource, Rib, RibKind}; use crate::macros::{sub_namespace_match, MacroRulesScope}; use crate::{ @@ -351,6 +352,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), ignore_binding, + None, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -364,6 +366,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + None, ) .ok() .map(LexicalScopeBinding::Item) @@ -383,6 +386,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize: Option<Finalize>, force: bool, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> Result<NameBinding<'a>, Determinacy> { bitflags::bitflags! { #[derive(Clone, Copy)] @@ -455,6 +459,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, true, force, + ignore_import, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { @@ -496,6 +501,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize, ignore_binding, + ignore_import, ); match binding { Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), @@ -518,6 +524,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { !matches!(scope_set, ScopeSet::Late(..)), finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), ignore_binding, + ignore_import, ); match binding { Ok(binding) => { @@ -585,6 +592,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, None, ignore_binding, + ignore_import, ) { if matches!(use_prelude, UsePrelude::Yes) || this.is_builtin_macro(binding.res()) @@ -738,8 +746,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, + ignore_import: Option<Import<'a>>, ) -> Result<NameBinding<'a>, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None) + self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None, ignore_import) .map_err(|(determinacy, _)| determinacy) } @@ -752,9 +761,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option<Finalize>, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> Result<NameBinding<'a>, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding) - .map_err(|(determinacy, _)| determinacy) + self.resolve_ident_in_module_ext( + module, + ident, + ns, + parent_scope, + finalize, + ignore_binding, + ignore_import, + ) + .map_err(|(determinacy, _)| determinacy) } #[instrument(level = "debug", skip(self))] @@ -766,6 +784,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option<Finalize>, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> Result<NameBinding<'a>, (Determinacy, Weak)> { let tmp_parent_scope; let mut adjusted_parent_scope = parent_scope; @@ -792,6 +811,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { false, finalize, ignore_binding, + ignore_import, ) } @@ -804,6 +824,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option<Finalize>, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> Result<NameBinding<'a>, Determinacy> { self.resolve_ident_in_module_unadjusted_ext( module, @@ -813,6 +834,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { false, finalize, ignore_binding, + ignore_import, ) .map_err(|(determinacy, _)| determinacy) } @@ -831,6 +853,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // This binding should be ignored during in-module resolution, so that we don't get // "self-confirming" import resolutions during import validation and checking. ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> Result<NameBinding<'a>, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, @@ -843,6 +866,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -879,6 +903,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -962,25 +987,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. for single_import in &resolution.single_imports { - let Some(import_vis) = single_import.vis.get() else { - // This branch handles a cycle in single imports, which occurs - // when we've previously **steal** the `vis` value during an import - // process. + if ignore_import == Some(*single_import) { + // This branch handles a cycle in single imports. // // For example: // ``` // use a::b; // use b as a; // ``` - // 1. Steal the `vis` in `use a::b` and attempt to locate `a` in the + // 1. Record `use a::b` as the `ignore_import` and attempt to locate `a` in the // current module. // 2. Encounter the import `use b as a`, which is a `single_import` for `a`, // and try to find `b` in the current module. // 3. Re-encounter the `use a::b` import since it's a `single_import` of `b`. // This leads to entering this branch. continue; - }; - if !self.is_accessible_from(import_vis, parent_scope.module) { + } + if !self.is_accessible_from(single_import.vis, parent_scope.module) { continue; } if let Some(ignored) = ignore_binding @@ -1022,6 +1045,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &single_import.parent_scope, None, ignore_binding, + ignore_import, ) { Err(Determined) => continue, Ok(binding) @@ -1070,10 +1094,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check if one of glob imports can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. for glob_import in module.globs.borrow().iter() { - let Some(import_vis) = glob_import.vis.get() else { + if ignore_import == Some(*glob_import) { continue; - }; - if !self.is_accessible_from(import_vis, parent_scope.module) { + } + if !self.is_accessible_from(glob_import.vis, parent_scope.module) { continue; } let module = match glob_import.imported_module.get() { @@ -1100,6 +1124,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { adjusted_parent_scope, None, ignore_binding, + ignore_import, ); match result { @@ -1412,8 +1437,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path: &[Segment], opt_ns: Option<Namespace>, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, + ignore_import: Option<Import<'a>>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None) + self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import) } #[instrument(level = "debug", skip(self))] @@ -1424,8 +1450,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option<Finalize>, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding) + self.resolve_path_with_ribs( + path, + opt_ns, + parent_scope, + finalize, + None, + ignore_binding, + ignore_import, + ) } pub(crate) fn resolve_path_with_ribs( @@ -1436,6 +1471,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize: Option<Finalize>, ribs: Option<&PerNS<Vec<Rib<'a>>>>, ignore_binding: Option<NameBinding<'a>>, + ignore_import: Option<Import<'a>>, ) -> PathResult<'a> { let mut module = None; let mut allow_super = true; @@ -1538,10 +1574,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize, ignore_binding, + ignore_import, ) } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns { + assert!(ignore_import.is_none()); match self.resolve_ident_in_lexical_scope( ident, ns, @@ -1570,6 +1608,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ) }; @@ -1644,6 +1683,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, ribs, ignore_binding, + ignore_import, module, segment_idx, ident, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c7af21027b8..4a891d12281 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -175,7 +175,7 @@ pub(crate) struct ImportData<'a> { pub module_path: Vec<Segment>, /// The resolution of `module_path`. pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>, - pub vis: Cell<Option<ty::Visibility>>, + pub vis: ty::Visibility, pub used: Cell<Option<Used>>, } @@ -195,10 +195,6 @@ impl<'a> ImportData<'a> { } } - pub(crate) fn expect_vis(&self) -> ty::Visibility { - self.vis.get().expect("encountered cleared import visibility") - } - pub(crate) fn id(&self) -> Option<NodeId> { match self.kind { ImportKind::Single { id, .. } @@ -267,7 +263,7 @@ fn pub_use_of_private_extern_crate_hack( match (&import.kind, &binding.kind) { (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) if let ImportKind::ExternCrate { id, .. } = binding_import.kind - && import.expect_vis().is_public() => + && import.vis.is_public() => { Some(id) } @@ -279,7 +275,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Given a binding and an import that resolves to it, /// return the corresponding binding defined by the import. pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> { - let import_vis = import.expect_vis().to_def_id(); + let import_vis = import.vis.to_def_id(); let vis = if binding.vis.is_at_least(import_vis, self.tcx) || pub_use_of_private_extern_crate_hack(import, binding).is_some() { @@ -773,11 +769,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let module = if let Some(module) = import.imported_module.get() { module } else { - // For better failure detection, pretend that the import will - // not define any names while resolving its module path. - let orig_vis = import.vis.take(); - let path_res = self.maybe_resolve_path(&import.module_path, None, &import.parent_scope); - import.vis.set(orig_vis); + let path_res = self.maybe_resolve_path( + &import.module_path, + None, + &import.parent_scope, + Some(import), + ); match path_res { PathResult::Module(module) => module, @@ -807,16 +804,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { if let Err(Undetermined) = source_bindings[ns].get() { - // For better failure detection, pretend that the import will - // not define any names while resolving its module path. - let orig_vis = import.vis.take(); let binding = this.maybe_resolve_ident_in_module( module, source, ns, &import.parent_scope, + Some(import), ); - import.vis.set(orig_vis); source_bindings[ns].set(binding); } else { return; @@ -855,7 +849,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Optionally returns an unresolved import error. This error is buffered and used to /// consolidate multiple unresolved import errors into a single diagnostic. fn finalize_import(&mut self, import: Import<'a>) -> Option<UnresolvedImportError> { - let orig_vis = import.vis.take(); let ignore_binding = match &import.kind { ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(), _ => None, @@ -874,11 +867,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(finalize), ignore_binding, + Some(import), ); let no_ambiguity = ambiguity_errors_len(&self.ambiguity_errors) == prev_ambiguity_errors_len; - import.vis.set(orig_vis); + let module = match path_res { PathResult::Module(module) => { // Consistency checks, analogous to `finalize_macro_resolutions`. @@ -1013,8 +1007,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if !is_prelude && let Some(max_vis) = max_vis.get() - && let import_vis = import.expect_vis() - && !max_vis.is_at_least(import_vis, self.tcx) + && !max_vis.is_at_least(import.vis, self.tcx) { let def_id = self.local_def_id(id); self.lint_buffer.buffer_lint( @@ -1023,7 +1016,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { import.span, BuiltinLintDiag::RedundantImportVisibility { max_vis: max_vis.to_string(def_id, self.tcx), - import_vis: import_vis.to_string(def_id, self.tcx), + import_vis: import.vis.to_string(def_id, self.tcx), span: import.span, }, ); @@ -1038,9 +1031,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // importing it if available. let mut path = import.module_path.clone(); path.push(Segment::from_ident(ident)); - if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(&path, None, &import.parent_scope, Some(finalize), ignore_binding) - { + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path( + &path, + None, + &import.parent_scope, + Some(finalize), + ignore_binding, + None, + ) { let res = module.res().map(|r| (r, ident)); for error in &mut self.privacy_errors[privacy_errors_len..] { error.outermost_res = res; @@ -1051,7 +1049,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut all_ns_err = true; self.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { - let orig_vis = import.vis.take(); let binding = this.resolve_ident_in_module( module, ident, @@ -1059,8 +1056,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(Finalize { report_private: false, ..finalize }), target_bindings[ns].get(), + Some(import), ); - import.vis.set(orig_vis); match binding { Ok(binding) => { @@ -1123,6 +1120,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(finalize), None, + None, ); if binding.is_ok() { all_ns_failed = false; @@ -1233,7 +1231,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut crate_private_reexport = false; self.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { - if !binding.vis.is_at_least(import.expect_vis(), this.tcx) { + if !binding.vis.is_at_least(import.vis, this.tcx) { reexport_error = Some((ns, binding)); if let ty::Visibility::Restricted(binding_def_id) = binding.vis { if binding_def_id.is_top_level_module() { @@ -1370,6 +1368,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, target_bindings[ns].get(), + None, ) { Ok(other_binding) => { is_redundant = binding.res() == other_binding.res() diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f844930ad26..a5bd2fdd8f3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1388,6 +1388,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { finalize, Some(&self.ribs), None, + None, ) } @@ -2667,119 +2668,128 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let mut function_type_rib = Rib::new(kind); let mut function_value_rib = Rib::new(kind); let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind); - let mut seen_bindings = FxHashMap::default(); - // Store all seen lifetimes names from outer scopes. - let mut seen_lifetimes = FxHashSet::default(); - - // We also can't shadow bindings from associated parent items. - for ns in [ValueNS, TypeNS] { - for parent_rib in self.ribs[ns].iter().rev() { - seen_bindings.extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); - - // Break at mod level, to account for nested items which are - // allowed to shadow generic param names. - if matches!(parent_rib.kind, RibKind::Module(..)) { - break; - } - } - } - // Forbid shadowing lifetime bindings - for rib in self.lifetime_ribs.iter().rev() { - seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident)); - if let LifetimeRibKind::Item = rib.kind { - break; + // Only check for shadowed bindings if we're declaring new params. + if !params.is_empty() { + let mut seen_bindings = FxHashMap::default(); + // Store all seen lifetimes names from outer scopes. + let mut seen_lifetimes = FxHashSet::default(); + + // We also can't shadow bindings from associated parent items. + for ns in [ValueNS, TypeNS] { + for parent_rib in self.ribs[ns].iter().rev() { + seen_bindings + .extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); + + // Break at mod level, to account for nested items which are + // allowed to shadow generic param names. + if matches!(parent_rib.kind, RibKind::Module(..)) { + break; + } + } } - } - for param in params { - let ident = param.ident.normalize_to_macros_2_0(); - debug!("with_generic_param_rib: {}", param.id); - - if let GenericParamKind::Lifetime = param.kind - && let Some(&original) = seen_lifetimes.get(&ident) - { - diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; + // Forbid shadowing lifetime bindings + for rib in self.lifetime_ribs.iter().rev() { + seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident)); + if let LifetimeRibKind::Item = rib.kind { + break; + } } - match seen_bindings.entry(ident) { - Entry::Occupied(entry) => { - let span = *entry.get(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); - self.report_error(param.ident.span, err); - let rib = match param.kind { - GenericParamKind::Lifetime => { - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } - GenericParamKind::Type { .. } => &mut function_type_rib, - GenericParamKind::Const { .. } => &mut function_value_rib, - }; + for param in params { + let ident = param.ident.normalize_to_macros_2_0(); + debug!("with_generic_param_rib: {}", param.id); - // Taint the resolution in case of errors to prevent follow up errors in typeck - self.r.record_partial_res(param.id, PartialRes::new(Res::Err)); - rib.bindings.insert(ident, Res::Err); + if let GenericParamKind::Lifetime = param.kind + && let Some(&original) = seen_lifetimes.get(&ident) + { + diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } - Entry::Vacant(entry) => { - entry.insert(param.ident.span); - } - } - if param.ident.name == kw::UnderscoreLifetime { - self.r - .dcx() - .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } + match seen_bindings.entry(ident) { + Entry::Occupied(entry) => { + let span = *entry.get(); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + self.report_error(param.ident.span, err); + let rib = match param.kind { + GenericParamKind::Lifetime => { + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } + GenericParamKind::Type { .. } => &mut function_type_rib, + GenericParamKind::Const { .. } => &mut function_value_rib, + }; - if param.ident.name == kw::StaticLifetime { - self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { - span: param.ident.span, - lifetime: param.ident, - }); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } + // Taint the resolution in case of errors to prevent follow up errors in typeck + self.r.record_partial_res(param.id, PartialRes::new(Res::Err)); + rib.bindings.insert(ident, Res::Err); + continue; + } + Entry::Vacant(entry) => { + entry.insert(param.ident.span); + } + } - let def_id = self.r.local_def_id(param.id); + if param.ident.name == kw::UnderscoreLifetime { + self.r + .dcx() + .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } - // Plain insert (no renaming). - let (rib, def_kind) = match param.kind { - GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), - GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), - GenericParamKind::Lifetime => { - let res = LifetimeRes::Param { param: def_id, binder }; - self.record_lifetime_param(param.id, res); - function_lifetime_rib.bindings.insert(ident, (param.id, res)); + if param.ident.name == kw::StaticLifetime { + self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { + span: param.ident.span, + lifetime: param.ident, + }); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } - }; - let res = match kind { - RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()), - RibKind::Normal => { - // FIXME(non_lifetime_binders): Stop special-casing - // const params to error out here. - if self.r.tcx.features().non_lifetime_binders - && matches!(param.kind, GenericParamKind::Type { .. }) - { + let def_id = self.r.local_def_id(param.id); + + // Plain insert (no renaming). + let (rib, def_kind) = match param.kind { + GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), + GenericParamKind::Const { .. } => { + (&mut function_value_rib, DefKind::ConstParam) + } + GenericParamKind::Lifetime => { + let res = LifetimeRes::Param { param: def_id, binder }; + self.record_lifetime_param(param.id, res); + function_lifetime_rib.bindings.insert(ident, (param.id, res)); + continue; + } + }; + + let res = match kind { + RibKind::Item(..) | RibKind::AssocItem => { Res::Def(def_kind, def_id.to_def_id()) - } else { - Res::Err } - } - _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), - }; - self.r.record_partial_res(param.id, PartialRes::new(res)); - rib.bindings.insert(ident, res); + RibKind::Normal => { + // FIXME(non_lifetime_binders): Stop special-casing + // const params to error out here. + if self.r.tcx.features().non_lifetime_binders + && matches!(param.kind, GenericParamKind::Type { .. }) + { + Res::Def(def_kind, def_id.to_def_id()) + } else { + Res::Err + } + } + _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), + }; + self.r.record_partial_res(param.id, PartialRes::new(res)); + rib.bindings.insert(ident, res); + } } self.lifetime_ribs.push(function_lifetime_rib); @@ -4186,7 +4196,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = - self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) + self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None) { return Ok(Some(PartialRes::new(res))); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f126563fe58..f9896fb2196 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2058,6 +2058,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ident, ns, &self.parent_scope, + None, ) { let res = binding.res(); if filter_fn(res) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 79d3bb685b3..89ac839651f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1129,9 +1129,6 @@ pub struct Resolver<'a, 'tcx> { /// Also includes of list of each fields visibility struct_constructors: LocalDefIdMap<(Res, ty::Visibility<DefId>, Vec<ty::Visibility<DefId>>)>, - /// Features declared for this crate. - declared_features: FxHashSet<Symbol>, - lint_buffer: LintBuffer, next_node_id: NodeId, @@ -1402,7 +1399,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let registered_tools = tcx.registered_tools(()); - let features = tcx.features(); let pub_vis = ty::Visibility::<DefId>::Public; let edition = tcx.sess.edition(); @@ -1506,7 +1502,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { multi_segment_macro_resolutions: Default::default(), builtin_attrs: Default::default(), containers_deriving_copy: Default::default(), - declared_features: features.declared_features.clone(), lint_buffer: LintBuffer::default(), next_node_id: CRATE_NODE_ID, node_id_to_def_id, @@ -2120,7 +2115,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) { + match self.maybe_resolve_path(&segments, Some(ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), PathResult::NonModule(path_res) => path_res.full_res(), PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => { @@ -2204,6 +2199,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident, ValueNS, parent_scope, + None, ) else { return; }; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 64ae0d82952..da7278175e9 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -39,6 +39,7 @@ use crate::errors::{ self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive, }; +use crate::imports::Import; use crate::Namespace::*; use crate::{ BindingKey, BuiltinMacroState, DeriveData, Determinacy, Finalize, MacroData, ModuleKind, @@ -399,6 +400,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { &parent_scope, true, force, + None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -551,6 +553,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { force, deleg_impl, invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), + None, ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), @@ -704,8 +707,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, trace: bool, force: bool, + ignore_import: Option<Import<'a>>, ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> { - self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None, None) + self.resolve_macro_or_delegation_path( + path, + kind, + parent_scope, + trace, + force, + None, + None, + ignore_import, + ) } fn resolve_macro_or_delegation_path( @@ -717,6 +730,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { force: bool, deleg_impl: Option<LocalDefId>, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, + ignore_import: Option<Import<'a>>, ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> { let path_span = ast_path.span; let mut path = Segment::from_path(ast_path); @@ -733,7 +747,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let res = if deleg_impl.is_some() || path.len() > 1 { let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS }; - let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) { + let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope, ignore_import) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) @@ -768,6 +782,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, force, None, + None, ); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); @@ -852,6 +867,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, + None, ) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { check_consistency(self, &path, path_span, kind, initial_res, res) @@ -871,7 +887,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if let PathResult::Failed { span, label, module, .. } = path_res { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = - self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) + self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope, None) && partial_res.unresolved_segments() == 0 { let sm = self.tcx.sess.source_map(); @@ -921,6 +937,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), true, None, + None, ) { Ok(binding) => { let initial_res = initial_binding.map(|initial_binding| { @@ -966,6 +983,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), true, None, + None, ); } } @@ -983,7 +1001,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let feature = stability.feature; let is_allowed = |feature| { - self.declared_features.contains(&feature) || span.allows_unstable(feature) + self.tcx.features().declared_features.contains(&feature) + || span.allows_unstable(feature) }; let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { @@ -1070,6 +1089,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, None, + None, ); if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) { self.tcx.sess.psess.buffer_lint( @@ -1143,7 +1163,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut indeterminate = false; for ns in namespaces { - match self.maybe_resolve_path(path, Some(*ns), &parent_scope) { + match self.maybe_resolve_path(path, Some(*ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true), PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => { return Ok(true); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f58a991a616..95d171409d8 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -602,7 +602,7 @@ impl OutputType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ErrorOutputType { /// Output meant for the consumption of humans. - HumanReadable(HumanReadableErrorType), + HumanReadable(HumanReadableErrorType, ColorConfig), /// Output that's consumed by other tools such as `rustfix` or the `RLS`. Json { /// Render the JSON in a human readable way (with indents and newlines). @@ -610,12 +610,13 @@ pub enum ErrorOutputType { /// The JSON output includes a `rendered` field that includes the rendered /// human output. json_rendered: HumanReadableErrorType, + color_config: ColorConfig, }, } impl Default for ErrorOutputType { fn default() -> Self { - Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + Self::HumanReadable(HumanReadableErrorType::Default, ColorConfig::Auto) } } @@ -838,10 +839,14 @@ pub enum Input { impl Input { pub fn filestem(&self) -> &str { - match *self { - Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), - Input::Str { .. } => "rust_out", + if let Input::File(ifile) = self { + // If for some reason getting the file stem as a UTF-8 string fails, + // then fallback to a fixed name. + if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) { + return name; + } } + "rust_out" } pub fn source_name(&self) -> FileName { @@ -1300,7 +1305,10 @@ pub(crate) const fn default_lib_output() -> CrateType { } pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { - // Combine the configuration requested by the session (command line) with + // First disallow some configuration given on the command line + cfg::disallow_cfgs(sess, &user_cfg); + + // Then combine the configuration requested by the session (command line) with // some default and generated configuration items. user_cfg.extend(cfg::default_configuration(sess)); user_cfg @@ -1624,6 +1632,7 @@ pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Col /// Possible json config files pub struct JsonConfig { pub json_rendered: HumanReadableErrorType, + pub json_color: ColorConfig, json_artifact_notifications: bool, pub json_unused_externs: JsonUnusedExterns, json_future_incompat: bool, @@ -1661,8 +1670,7 @@ impl JsonUnusedExterns { /// The first value returned is how to render JSON diagnostics, and the second /// is whether or not artifact notifications are enabled. pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig { - let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = - HumanReadableErrorType::Default; + let mut json_rendered = HumanReadableErrorType::Default; let mut json_color = ColorConfig::Never; let mut json_artifact_notifications = false; let mut json_unused_externs = JsonUnusedExterns::No; @@ -1689,7 +1697,8 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json } JsonConfig { - json_rendered: json_rendered(json_color), + json_rendered, + json_color, json_artifact_notifications, json_unused_externs, json_future_incompat, @@ -1701,6 +1710,7 @@ pub fn parse_error_format( early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches, color: ColorConfig, + json_color: ColorConfig, json_rendered: HumanReadableErrorType, ) -> ErrorOutputType { // We need the `opts_present` check because the driver will send us Matches @@ -1710,18 +1720,22 @@ pub fn parse_error_format( let error_format = if matches.opts_present(&["error-format".to_owned()]) { match matches.opt_str("error-format").as_deref() { None | Some("human") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) } Some("human-annotate-rs") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, color) } - Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, - Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, - Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), - + Some("json") => { + ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } + } + Some("pretty-json") => { + ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } + } + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short, color), Some(arg) => { early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( - HumanReadableErrorType::Default(color), + HumanReadableErrorType::Default, + color, )); early_dcx.early_fatal(format!( "argument for `--error-format` must be `human`, `json` or \ @@ -1730,7 +1744,7 @@ pub fn parse_error_format( } } } else { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) }; match error_format { @@ -1784,7 +1798,7 @@ fn check_error_format_stability( if let ErrorOutputType::Json { pretty: true, .. } = error_format { early_dcx.early_fatal("`--error-format=pretty-json` is unstable"); } - if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, _) = error_format { early_dcx.early_fatal("`--error-format=human-annotate-rs` is unstable"); @@ -2385,12 +2399,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let JsonConfig { json_rendered, + json_color, json_artifact_notifications, json_unused_externs, json_future_incompat, } = parse_json(early_dcx, matches); - let error_format = parse_error_format(early_dcx, matches, color, json_rendered); + let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); early_dcx.abort_if_error_and_set_error_format(error_format); diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 4109ebb6d34..a64b1e21e9e 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -17,12 +17,16 @@ //! - Add the activation logic in [`default_configuration`] //! - Add the cfg to [`CheckCfg::fill_well_known`] (and related files), //! so that the compiler can know the cfg is expected +//! - Add the cfg in [`disallow_cfgs`] to disallow users from setting it via `--cfg` //! - Add the feature gating in `compiler/rustc_feature/src/builtin_attrs.rs` use std::hash::Hash; use std::iter; +use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS; +use rustc_lint_defs::BuiltinLintDiag; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Align; use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target, TargetTriple, TARGETS}; @@ -82,6 +86,67 @@ impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> { } } +/// Disallow builtin cfgs from the CLI. +pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { + let disallow = |cfg: &(Symbol, Option<Symbol>), controlled_by| { + let cfg_name = cfg.0; + let cfg = if let Some(value) = cfg.1 { + format!(r#"{}="{}""#, cfg_name, value) + } else { + format!("{}", cfg_name) + }; + sess.psess.opt_span_buffer_lint( + EXPLICIT_BUILTIN_CFGS_IN_FLAGS, + None, + ast::CRATE_NODE_ID, + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }, + ) + }; + + // We want to restrict setting builtin cfgs that will produce incoherent behavior + // between the cfg and the rustc cli flag that sets it. + // + // The tests are in tests/ui/cfg/disallowed-cli-cfgs.rs. + + // By-default all builtin cfgs are disallowed, only those are allowed: + // - test: as it makes sense to the have the `test` cfg active without the builtin + // test harness. See Cargo `harness = false` config. + // + // Cargo `--cfg test`: https://github.com/rust-lang/cargo/blob/bc89bffa5987d4af8f71011c7557119b39e44a65/src/cargo/core/compiler/mod.rs#L1124 + + for cfg in user_cfgs { + match cfg { + (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"), + (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"), + (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"), + (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"), + ( + sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers, + None | Some(_), + ) => disallow(cfg, "-Z sanitizer=cfi"), + (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"), + (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"), + (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"), + (sym::unix, None) + | (sym::windows, None) + | (sym::relocation_model, Some(_)) + | (sym::target_abi, None | Some(_)) + | (sym::target_arch, Some(_)) + | (sym::target_endian, Some(_)) + | (sym::target_env, None | Some(_)) + | (sym::target_family, Some(_)) + | (sym::target_os, Some(_)) + | (sym::target_pointer_width, Some(_)) + | (sym::target_vendor, None | Some(_)) + | (sym::target_has_atomic, Some(_)) + | (sym::target_has_atomic_equal_alignment, Some(_)) + | (sym::target_has_atomic_load_store, Some(_)) + | (sym::target_thread_local, None) => disallow(cfg, "--target"), + _ => {} + } + } +} + /// Generate the default configs for a given session pub(crate) fn default_configuration(sess: &Session) -> Cfg { let mut ret = Cfg::default(); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index bf54aae1cfe..4b87f5d62b2 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1827,6 +1827,8 @@ options! { the same values as the target option of the same name"), meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), + metrics_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], + "stores metrics about the errors being emitted by rustc to disk"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index ebd5021dae1..d6c58e9d1be 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -307,9 +307,19 @@ impl ParseSess { node_id: NodeId, diagnostic: BuiltinLintDiag, ) { + self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic) + } + + pub fn opt_span_buffer_lint( + &self, + lint: &'static Lint, + span: Option<MultiSpan>, + node_id: NodeId, + diagnostic: BuiltinLintDiag, + ) { self.buffered_lints.with_lock(|buffered_lints| { buffered_lints.push(BufferedEarlyLint { - span: span.into(), + span, node_id, lint_id: LintId::of(lint), diagnostic, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index be67baf57f6..672dddf871e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -950,10 +950,10 @@ fn default_emitter( t => t, }; match sopts.error_format { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + config::ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); - if let HumanReadableErrorType::AnnotateSnippet(_) = kind { + if let HumanReadableErrorType::AnnotateSnippet = kind { let emitter = AnnotateSnippetEmitter::new( Some(source_map), bundle, @@ -978,13 +978,14 @@ fn default_emitter( Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } } - config::ErrorOutputType::Json { pretty, json_rendered } => Box::new( + config::ErrorOutputType::Json { pretty, json_rendered, color_config } => Box::new( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), source_map, fallback_bundle, pretty, json_rendered, + color_config, ) .registry(Some(registry)) .fluent_bundle(bundle) @@ -1425,20 +1426,23 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> { let fallback_bundle = fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); let emitter: Box<DynEmitter> = match output { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + config::ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .short_message(short), ) } - config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::new( - Box::new(io::BufWriter::new(io::stderr())), - Lrc::new(SourceMap::new(FilePathMapping::empty())), - fallback_bundle, - pretty, - json_rendered, - )), + config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { + Box::new(JsonEmitter::new( + Box::new(io::BufWriter::new(io::stderr())), + Lrc::new(SourceMap::new(FilePathMapping::empty())), + fallback_bundle, + pretty, + json_rendered, + color_config, + )) + } }; emitter } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 94cf21da4ef..32fca6733bb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -276,6 +276,7 @@ symbols! { Path, PathBuf, Pending, + PinCoerceUnsized, Pointer, Poll, ProcMacro, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4e2617c4679..da66ba270b3 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::symbol::{sym, Symbol}; /// Features that control behaviour of rustc, rather than the codegen. @@ -53,136 +54,154 @@ impl Stability { // // Stabilizing a target feature requires t-lang approval. -const ARM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +type ImpliedFeatures = &'static [&'static str]; + +const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", Unstable(sym::arm_target_feature)), - ("aes", Unstable(sym::arm_target_feature)), - ("crc", Unstable(sym::arm_target_feature)), - ("d32", Unstable(sym::arm_target_feature)), - ("dotprod", Unstable(sym::arm_target_feature)), - ("dsp", Unstable(sym::arm_target_feature)), - ("fp-armv8", Unstable(sym::arm_target_feature)), - ("i8mm", Unstable(sym::arm_target_feature)), - ("mclass", Unstable(sym::arm_target_feature)), - ("neon", Unstable(sym::arm_target_feature)), - ("rclass", Unstable(sym::arm_target_feature)), - ("sha2", Unstable(sym::arm_target_feature)), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", Unstable(sym::arm_target_feature)), - ("thumb2", Unstable(sym::arm_target_feature)), - ("trustzone", Unstable(sym::arm_target_feature)), - ("v5te", Unstable(sym::arm_target_feature)), - ("v6", Unstable(sym::arm_target_feature)), - ("v6k", Unstable(sym::arm_target_feature)), - ("v6t2", Unstable(sym::arm_target_feature)), - ("v7", Unstable(sym::arm_target_feature)), - ("v8", Unstable(sym::arm_target_feature)), - ("vfp2", Unstable(sym::arm_target_feature)), - ("vfp3", Unstable(sym::arm_target_feature)), - ("vfp4", Unstable(sym::arm_target_feature)), - ("virtualization", Unstable(sym::arm_target_feature)), + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", Stable), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", Stable), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", Stable), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", Stable), + ("crc", Stable, &[]), // FEAT_DIT - ("dit", Stable), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", Stable), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", Stable), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", Stable), + ("dpb2", Stable, &["dpb"]), // FEAT_F32MM - ("f32mm", Stable), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", Stable), + ("f64mm", Stable, &["sve"]), // FEAT_FCMA - ("fcma", Stable), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", Stable), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", Stable), + ("flagm", Stable, &[]), // FEAT_FP16 - ("fp16", Stable), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("fp16", Stable, &["neon"]), // FEAT_FRINTTS - ("frintts", Stable), + ("frintts", Stable, &[]), // FEAT_I8MM - ("i8mm", Stable), + ("i8mm", Stable, &[]), // FEAT_JSCVT - ("jsconv", Stable), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("jsconv", Stable, &["neon"]), // FEAT_LOR - ("lor", Stable), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", Stable), + ("lse", Stable, &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", Stable), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ("neon", Stable), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", Stable), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", Stable), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", Stable), + ("pan", Stable, &[]), // FEAT_PMUv3 - ("pmuv3", Stable), + ("pmuv3", Stable, &[]), // FEAT_RAND - ("rand", Stable), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", Stable), + ("ras", Stable, &[]), // FEAT_RCPC - ("rcpc", Stable), + ("rcpc", Stable, &[]), // FEAT_RCPC2 - ("rcpc2", Stable), + ("rcpc2", Stable, &["rcpc"]), // FEAT_RDM - ("rdm", Stable), + ("rdm", Stable, &["neon"]), // FEAT_SB - ("sb", Stable), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", Stable), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", Stable), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", Stable), + ("sm4", Stable, &["neon"]), // FEAT_SPE - ("spe", Stable), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", Stable), + ("ssbs", Stable, &[]), // FEAT_SVE - ("sve", Stable), + // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 + // + // LLVM doesn't enable Neon for SVE. ARM indicates that they're separate, but probably always + // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 + // + // "For backwards compatibility, Neon and VFP are required in the latest architectures." + ("sve", Stable, &["neon"]), // FEAT_SVE2 - ("sve2", Stable), + ("sve2", Stable, &["sve"]), // FEAT_SVE2_AES - ("sve2-aes", Stable), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", Stable), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", Stable), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", Stable), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_TME - ("tme", Stable), - ("v8.1a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.2a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.3a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.4a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.5a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.6a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.7a", Unstable(sym::aarch64_ver_target_feature)), + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &[]), // FEAT_VHE - ("vh", Stable), + ("vh", Stable, &[]), // tidy-alphabetical-end ]; @@ -190,224 +209,223 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", Stable), - ("aes", Stable), - ("amx-bf16", Unstable(sym::x86_amx_intrinsics)), - ("amx-complex", Unstable(sym::x86_amx_intrinsics)), - ("amx-fp16", Unstable(sym::x86_amx_intrinsics)), - ("amx-int8", Unstable(sym::x86_amx_intrinsics)), - ("amx-tile", Unstable(sym::x86_amx_intrinsics)), - ("avx", Stable), - ("avx2", Stable), - ("avx512bf16", Unstable(sym::avx512_target_feature)), - ("avx512bitalg", Unstable(sym::avx512_target_feature)), - ("avx512bw", Unstable(sym::avx512_target_feature)), - ("avx512cd", Unstable(sym::avx512_target_feature)), - ("avx512dq", Unstable(sym::avx512_target_feature)), - ("avx512f", Unstable(sym::avx512_target_feature)), - ("avx512fp16", Unstable(sym::avx512_target_feature)), - ("avx512ifma", Unstable(sym::avx512_target_feature)), - ("avx512vbmi", Unstable(sym::avx512_target_feature)), - ("avx512vbmi2", Unstable(sym::avx512_target_feature)), - ("avx512vl", Unstable(sym::avx512_target_feature)), - ("avx512vnni", Unstable(sym::avx512_target_feature)), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature)), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature)), - ("avxifma", Unstable(sym::avx512_target_feature)), - ("avxneconvert", Unstable(sym::avx512_target_feature)), - ("avxvnni", Unstable(sym::avx512_target_feature)), - ("avxvnniint16", Unstable(sym::avx512_target_feature)), - ("avxvnniint8", Unstable(sym::avx512_target_feature)), - ("bmi1", Stable), - ("bmi2", Stable), - ("cmpxchg16b", Stable), - ("ermsb", Unstable(sym::ermsb_target_feature)), - ("f16c", Stable), - ("fma", Stable), - ("fxsr", Stable), - ("gfni", Unstable(sym::avx512_target_feature)), - ("lahfsahf", Unstable(sym::lahfsahf_target_feature)), - ("lzcnt", Stable), - ("movbe", Stable), - ("pclmulqdq", Stable), - ("popcnt", Stable), - ("prfchw", Unstable(sym::prfchw_target_feature)), - ("rdrand", Stable), - ("rdseed", Stable), - ("rtm", Unstable(sym::rtm_target_feature)), - ("sha", Stable), - ("sha512", Unstable(sym::sha512_sm_x86)), - ("sm3", Unstable(sym::sha512_sm_x86)), - ("sm4", Unstable(sym::sha512_sm_x86)), - ("sse", Stable), - ("sse2", Stable), - ("sse3", Stable), - ("sse4.1", Stable), - ("sse4.2", Stable), - ("sse4a", Unstable(sym::sse4a_target_feature)), - ("ssse3", Stable), - ("tbm", Unstable(sym::tbm_target_feature)), - ("vaes", Unstable(sym::avx512_target_feature)), - ("vpclmulqdq", Unstable(sym::avx512_target_feature)), - ("xop", Unstable(sym::xop_target_feature)), - ("xsave", Stable), - ("xsavec", Stable), - ("xsaveopt", Stable), - ("xsaves", Stable), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &[]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end ]; -const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", Unstable(sym::hexagon_target_feature)), - ("hvx-length128b", Unstable(sym::hexagon_target_feature)), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", Unstable(sym::powerpc_target_feature)), - ("power10-vector", Unstable(sym::powerpc_target_feature)), - ("power8-altivec", Unstable(sym::powerpc_target_feature)), - ("power8-vector", Unstable(sym::powerpc_target_feature)), - ("power9-altivec", Unstable(sym::powerpc_target_feature)), - ("power9-vector", Unstable(sym::powerpc_target_feature)), - ("vsx", Unstable(sym::powerpc_target_feature)), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", Unstable(sym::mips_target_feature)), - ("msa", Unstable(sym::mips_target_feature)), - ("virt", Unstable(sym::mips_target_feature)), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", Stable), - ("c", Stable), - ("d", Unstable(sym::riscv_target_feature)), - ("e", Unstable(sym::riscv_target_feature)), - ("f", Unstable(sym::riscv_target_feature)), - ("m", Stable), - ("relax", Unstable(sym::riscv_target_feature)), - ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)), - ("v", Unstable(sym::riscv_target_feature)), - ("zba", Stable), - ("zbb", Stable), - ("zbc", Stable), - ("zbkb", Stable), - ("zbkc", Stable), - ("zbkx", Stable), - ("zbs", Stable), - ("zdinx", Unstable(sym::riscv_target_feature)), - ("zfh", Unstable(sym::riscv_target_feature)), - ("zfhmin", Unstable(sym::riscv_target_feature)), - ("zfinx", Unstable(sym::riscv_target_feature)), - ("zhinx", Unstable(sym::riscv_target_feature)), - ("zhinxmin", Unstable(sym::riscv_target_feature)), - ("zk", Stable), - ("zkn", Stable), - ("zknd", Stable), - ("zkne", Stable), - ("zknh", Stable), - ("zkr", Stable), - ("zks", Stable), - ("zksed", Stable), - ("zksh", Stable), - ("zkt", Stable), + ("a", Stable, &[]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", Unstable(sym::wasm_target_feature)), - ("bulk-memory", Stable), - ("exception-handling", Unstable(sym::wasm_target_feature)), - ("extended-const", Stable), - ("multivalue", Unstable(sym::wasm_target_feature)), - ("mutable-globals", Stable), - ("nontrapping-fptoint", Stable), - ("reference-types", Unstable(sym::wasm_target_feature)), - ("relaxed-simd", Stable), - ("sign-ext", Stable), - ("simd128", Stable), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("multivalue", Unstable(sym::wasm_target_feature), &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Unstable(sym::wasm_target_feature), &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_IMPLICIT_FEATURES: &[(&str, &str)] = &[("relaxed-simd", "simd128")]; - -const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; +const BPF_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", Unstable(sym::csky_target_feature)), - ("2e3", Unstable(sym::csky_target_feature)), - ("3e3r1", Unstable(sym::csky_target_feature)), - ("3e3r2", Unstable(sym::csky_target_feature)), - ("3e3r3", Unstable(sym::csky_target_feature)), - ("3e7", Unstable(sym::csky_target_feature)), - ("7e10", Unstable(sym::csky_target_feature)), - ("cache", Unstable(sym::csky_target_feature)), - ("doloop", Unstable(sym::csky_target_feature)), - ("dsp1e2", Unstable(sym::csky_target_feature)), - ("dspe60", Unstable(sym::csky_target_feature)), - ("e1", Unstable(sym::csky_target_feature)), - ("e2", Unstable(sym::csky_target_feature)), - ("edsp", Unstable(sym::csky_target_feature)), - ("elrw", Unstable(sym::csky_target_feature)), - ("float1e2", Unstable(sym::csky_target_feature)), - ("float1e3", Unstable(sym::csky_target_feature)), - ("float3e4", Unstable(sym::csky_target_feature)), - ("float7e60", Unstable(sym::csky_target_feature)), - ("floate1", Unstable(sym::csky_target_feature)), - ("hard-tp", Unstable(sym::csky_target_feature)), - ("high-registers", Unstable(sym::csky_target_feature)), - ("hwdiv", Unstable(sym::csky_target_feature)), - ("mp", Unstable(sym::csky_target_feature)), - ("mp1e2", Unstable(sym::csky_target_feature)), - ("nvic", Unstable(sym::csky_target_feature)), - ("trust", Unstable(sym::csky_target_feature)), - ("vdsp2e60f", Unstable(sym::csky_target_feature)), - ("vdspv1", Unstable(sym::csky_target_feature)), - ("vdspv2", Unstable(sym::csky_target_feature)), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", Unstable(sym::csky_target_feature)), - ("fpuv2_df", Unstable(sym::csky_target_feature)), - ("fpuv2_sf", Unstable(sym::csky_target_feature)), - ("fpuv3_df", Unstable(sym::csky_target_feature)), - ("fpuv3_hf", Unstable(sym::csky_target_feature)), - ("fpuv3_hi", Unstable(sym::csky_target_feature)), - ("fpuv3_sf", Unstable(sym::csky_target_feature)), - ("hard-float", Unstable(sym::csky_target_feature)), - ("hard-float-abi", Unstable(sym::csky_target_feature)), + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", Unstable(sym::loongarch_target_feature)), - ("f", Unstable(sym::loongarch_target_feature)), - ("frecipe", Unstable(sym::loongarch_target_feature)), - ("lasx", Unstable(sym::loongarch_target_feature)), - ("lbt", Unstable(sym::loongarch_target_feature)), - ("lsx", Unstable(sym::loongarch_target_feature)), - ("lvz", Unstable(sym::loongarch_target_feature)), - ("relax", Unstable(sym::loongarch_target_feature)), - ("ual", Unstable(sym::loongarch_target_feature)), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", Unstable(sym::s390x_target_feature)), - ("vector", Unstable(sym::s390x_target_feature)), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; @@ -430,10 +448,13 @@ pub fn all_known_features() -> impl Iterator<Item = (&'static str, Stability)> { .chain(LOONGARCH_ALLOWED_FEATURES) .chain(IBMZ_ALLOWED_FEATURES) .cloned() + .map(|(f, s, _)| (f, s)) } impl super::spec::Target { - pub fn supported_target_features(&self) -> &'static [(&'static str, Stability)] { + pub fn supported_target_features( + &self, + ) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_ALLOWED_FEATURES, "aarch64" | "arm64ec" => AARCH64_ALLOWED_FEATURES, @@ -458,12 +479,27 @@ impl super::spec::Target { } } - /// Returns a list of target features. Each items first target feature - /// implicitly enables the second one. - pub fn implicit_target_features(&self) -> &'static [(&'static str, &'static str)] { - match &*self.arch { - "wasm32" | "wasm64" => WASM_IMPLICIT_FEATURES, - _ => &[], + pub fn implied_target_features( + &self, + base_features: impl Iterator<Item = Symbol>, + ) -> FxHashSet<Symbol> { + let implied_features = self + .supported_target_features() + .iter() + .map(|(f, _, i)| (Symbol::intern(f), i)) + .collect::<FxHashMap<_, _>>(); + + // implied target features have their own implied target features, so we traverse the + // map until there are no more features to add + let mut features = FxHashSet::default(); + let mut new_features = base_features.collect::<Vec<Symbol>>(); + while let Some(new_feature) = new_features.pop() { + if features.insert(new_feature) { + if let Some(implied_features) = implied_features.get(&new_feature) { + new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + } + } } + features } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 72a4d4c1205..9ab47057859 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -388,39 +388,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_impls.non_blanket_impls().values().flatten().count(); // If there is only one implementation of the trait, suggest using it. // Otherwise, use a placeholder comment for the implementation. - let (message, self_type) = if non_blanket_impl_count == 1 { + let (message, self_types) = if non_blanket_impl_count == 1 { ( "use the fully-qualified path to the only available \ implementation", - format!( + vec![format!( "{}", self.tcx.type_of(impl_def_id).instantiate_identity() - ), + )], + ) + } else if non_blanket_impl_count < 20 { + ( + "use a fully-qualified path to one of the available \ + implementations", + trait_impls + .non_blanket_impls() + .values() + .flatten() + .map(|id| { + format!( + "{}", + self.tcx.type_of(id).instantiate_identity() + ) + }) + .collect::<Vec<String>>(), ) } else { ( "use a fully-qualified path to a specific available \ implementation", - "/* self type */".to_string(), + vec!["/* self type */".to_string()], ) }; - let mut suggestions = - vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; - if let Some(generic_arg) = trait_path_segment.args { - let between_span = - trait_path_segment.ident.span.between(generic_arg.span_ext); - // get rid of :: between Trait and <type> - // must be '::' between them, otherwise the parser won't accept the code - suggestions.push((between_span, "".to_string())); - suggestions - .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); - } else { - suggestions.push(( - trait_path_segment.ident.span.shrink_to_hi(), - ">".to_string(), - )); - } - err.multipart_suggestion( + let suggestions: Vec<_> = self_types + .into_iter() + .map(|self_type| { + let mut suggestions = vec![( + path.span.shrink_to_lo(), + format!("<{self_type} as "), + )]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = trait_path_segment + .ident + .span + .between(generic_arg.span_ext); + // get rid of :: between Trait and <type> + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string())); + suggestions.push(( + generic_arg.span_ext.shrink_to_hi(), + ">".to_string(), + )); + } else { + suggestions.push(( + trait_path_segment.ident.span.shrink_to_hi(), + ">".to_string(), + )); + } + suggestions + }) + .collect(); + err.multipart_suggestions( message, suggestions, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 2ce1b955af5..1cee82f04ea 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -5,10 +5,11 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{ - pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, StringPart, + pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, + StringPart, }; use rustc_hir::def::Namespace; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; use rustc_infer::infer::{InferOk, TypeTrace}; @@ -1624,9 +1625,131 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { - // If we have a single implementation, try to unify it with the trait ref - // that failed. This should uncover a better hint for what *is* implemented. + let alternative_candidates = |def_id: DefId| { + let mut impl_candidates: Vec<_> = self + .tcx + .all_impls(def_id) + // ignore `do_not_recommend` items + .filter(|def_id| { + !self + .tcx + .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) + }) + // Ignore automatically derived impls and `!Trait` impls. + .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) + .filter_map(|header| { + (header.polarity != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id)) + .then(|| header.trait_ref.instantiate_identity()) + }) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + } else { + true + } + }) + .collect(); + + impl_candidates.sort_by_key(|tr| tr.to_string()); + impl_candidates.dedup(); + impl_candidates + }; + + // We'll check for the case where the reason for the mismatch is that the trait comes from + // one crate version and the type comes from another crate version, even though they both + // are from the same crate. + let trait_def_id = trait_ref.def_id(); + if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() + && let found_type = def.did() + && trait_def_id.krate != found_type.krate + && self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate) + { + let name = self.tcx.crate_name(trait_def_id.krate); + let spans: Vec<_> = [trait_def_id, found_type] + .into_iter() + .filter_map(|def_id| self.tcx.extern_crate(def_id)) + .map(|data| { + let dependency = if data.dependency_of == LOCAL_CRATE { + "direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!("dependency of crate `{dep}`") + }; + ( + data.span, + format!("one version of crate `{name}` is used here, as a {dependency}"), + ) + }) + .collect(); + let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into(); + for (sp, label) in spans.into_iter() { + span.push_span_label(sp, label); + } + err.highlighted_span_help( + span, + vec![ + StringPart::normal("you have ".to_string()), + StringPart::highlighted("multiple different versions".to_string()), + StringPart::normal(" of crate `".to_string()), + StringPart::highlighted(format!("{name}")), + StringPart::normal("` in your dependency graph".to_string()), + ], + ); + let candidates = if impl_candidates.is_empty() { + alternative_candidates(trait_def_id) + } else { + impl_candidates.into_iter().map(|cand| cand.trait_ref).collect() + }; + if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| { + if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind() + && let candidate_def_id = def.did() + && let Some(name) = self.tcx.opt_item_name(candidate_def_id) + && let Some(found) = self.tcx.opt_item_name(found_type) + && name == found + && candidate_def_id.krate != found_type.krate + && self.tcx.crate_name(candidate_def_id.krate) + == self.tcx.crate_name(found_type.krate) + { + // A candidate was found of an item with the same name, from two separate + // versions of the same crate, let's clarify. + Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type))) + } else { + None + } + }) { + let mut span: MultiSpan = vec![sp_candidate, sp_found].into(); + span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait"); + span.push_span_label(sp_candidate, "this type implements the required trait"); + span.push_span_label(sp_found, "this type doesn't implement the required trait"); + err.highlighted_span_note( + span, + vec![ + StringPart::normal( + "two types coming from two different versions of the same crate are \ + different types " + .to_string(), + ), + StringPart::highlighted("even if they look the same".to_string()), + ], + ); + } + err.help("you can use `cargo tree` to explore your dependency tree"); + return true; + } + if let [single] = &impl_candidates { + // If we have a single implementation, try to unify it with the trait ref + // that failed. This should uncover a better hint for what *is* implemented. if self.probe(|_| { let ocx = ObligationCtxt::new(self); @@ -1798,43 +1921,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Mentioning implementers of `Copy`, `Debug` and friends is not useful. return false; } - let mut impl_candidates: Vec<_> = self - .tcx - .all_impls(def_id) - // ignore `do_not_recommend` items - .filter(|def_id| { - !self - .tcx - .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) - }) - // Ignore automatically derived impls and `!Trait` impls. - .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) - .filter_map(|header| { - (header.polarity != ty::ImplPolarity::Negative - || self.tcx.is_automatically_derived(def_id)) - .then(|| header.trait_ref.instantiate_identity()) - }) - .filter(|trait_ref| { - let self_ty = trait_ref.self_ty(); - // Avoid mentioning type parameters. - if let ty::Param(_) = self_ty.kind() { - false - } - // Avoid mentioning types that are private to another crate - else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { - // FIXME(compiler-errors): This could be generalized, both to - // be more granular, and probably look past other `#[fundamental]` - // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) - } else { - true - } - }) - .collect(); - - impl_candidates.sort_by_key(|tr| tr.to_string()); - impl_candidates.dedup(); - return report(impl_candidates, err); + return report(alternative_candidates(def_id), err); } // Sort impl candidates so that ordering is consistent for UI tests. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 2085d3da443..9de62031311 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -467,8 +467,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } candidates.vec.push(AsyncClosureCandidate); } - ty::FnDef(..) | ty::FnPtr(..) => { - candidates.vec.push(AsyncClosureCandidate); + // Provide an impl, but only for suitable `fn` pointers. + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { + candidates.vec.push(AsyncClosureCandidate); + } + } + // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). + ty::FnDef(def_id, _) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + candidates.vec.push(AsyncClosureCandidate); + } } _ => {} } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f1675f80717..d90c3bedc70 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -549,7 +549,7 @@ fn fn_abi_sanity_check<'tcx>( // With metadata. Must be unsized and not on the stack. assert!(arg.layout.is_unsized() && !on_stack); // Also, must not be `extern` type. - let tail = cx.tcx.struct_tail_with_normalize(arg.layout.ty, |ty| ty, || {}); + let tail = cx.tcx.struct_tail_for_codegen(arg.layout.ty, cx.param_env()); if matches!(tail.kind(), ty::Foreign(..)) { // These types do not have metadata, so having `meta_attrs` is bogus. // Conceptually, unsized arguments must be copied around, which requires dynamically diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 3ef10f4e43c..1eb03fc3bd6 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -244,7 +244,7 @@ fn layout_of_uncached<'tcx>( metadata } else { - let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + let unsized_part = tcx.struct_tail_for_codegen(pointee, param_env); match unsized_part.kind() { ty::Foreign(..) => { diff --git a/config.example.toml b/config.example.toml index 1a7bdc4737f..1b7de662e84 100644 --- a/config.example.toml +++ b/config.example.toml @@ -87,6 +87,9 @@ # library provided by LLVM. #static-libstdcpp = false +# Enable LLVM to use zstd for compression. +#libzstd = false + # Whether to use Ninja to build LLVM. This runs much faster than make. #ninja = true diff --git a/library/Cargo.lock b/library/Cargo.lock index 223b61456c2..b36399d880e 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.114" +version = "0.1.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb58b199190fcfe0846f55a3b545cd6b07a34bdd5930a476ff856f3ebcc5558a" +checksum = "92afe7344b64cccf3662ca26d5d1c0828ab826f04206b97d856e3625e390e4b5" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 479eb0a2ba7..bdf16257c7c 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,10 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.114", features = ['rustc-dep-of-std'] } - -[target.'cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))'.dependencies] -compiler_builtins = { version = "0.1.114", features = ["no-f16-f128"] } +compiler_builtins = { version = "0.1.118", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 38f50955b12..7de41259599 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -200,7 +200,7 @@ use core::ops::{ AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver, }; -use core::pin::Pin; +use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, addr_of_mut, NonNull, Unique}; use core::task::{Context, Poll}; use core::{borrow, fmt, slice}; @@ -2726,3 +2726,6 @@ impl<T: core::error::Error> core::error::Error for Box<T> { core::error::Error::provide(&**self, request); } } + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Box<T, A> {} diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index cc5f33c3685..88701370c10 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1433,6 +1433,20 @@ pub struct Iter<'a, T: 'a> { iter: slice::Iter<'a, T>, } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl<T> Default for Iter<'_, T> { + /// Creates an empty `binary_heap::Iter`. + /// + /// ``` + /// # use std::collections::binary_heap; + /// let iter: binary_heap::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { iter: Default::default() } + } +} + #[stable(feature = "collection_debug", since = "1.17.0")] impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index d84654e36d7..f6f773cc42a 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2016,6 +2016,20 @@ impl<K, V> Default for Range<'_, K, V> { } } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl<K, V> Default for RangeMut<'_, K, V> { + /// Creates an empty `btree_map::RangeMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::RangeMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + RangeMut { inner: Default::default(), _marker: PhantomData } + } +} + #[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; @@ -2050,6 +2064,20 @@ impl<K, V> ExactSizeIterator for ValuesMut<'_, K, V> { #[stable(feature = "fused", since = "1.26.0")] impl<K, V> FusedIterator for ValuesMut<'_, K, V> {} +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl<K, V> Default for ValuesMut<'_, K, V> { + /// Creates an empty `btree_map::ValuesMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::ValuesMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + ValuesMut { inner: Default::default() } + } +} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl<K, V, A: Allocator + Clone> Iterator for IntoKeys<K, V, A> { type Item = K; diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index 5a5e7f70854..67b5b91c4d4 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -28,6 +28,20 @@ impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> { } } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl<T> Default for Iter<'_, T> { + /// Creates an empty `vec_deque::Iter`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { i1: Default::default(), i2: Default::default() } + } +} + // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<T> Clone for Iter<'_, T> { diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 5061931afb7..2726e3e4252 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -28,6 +28,20 @@ impl<T: fmt::Debug> fmt::Debug for IterMut<'_, T> { } } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl<T> Default for IterMut<'_, T> { + /// Creates an empty `vec_deque::IterMut`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::IterMut<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + IterMut { i1: Default::default(), i2: Default::default() } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 28b08ef5611..3e44adf73f0 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -138,6 +138,7 @@ #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] +#![feature(pin_coerce_unsized_trait)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 13d218e43a7..bdee06154fa 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -256,6 +256,7 @@ use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Rece use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] use core::pin::Pin; +use core::pin::PinCoerceUnsized; use core::ptr::{self, drop_in_place, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; @@ -2177,6 +2178,12 @@ impl<T: ?Sized, A: Allocator> Deref for Rc<T, A> { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Rc<T, A> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Weak<T, A> {} + #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl<T: ?Sized, A: Allocator> DerefPure for Rc<T, A> {} @@ -3691,6 +3698,9 @@ impl<T: ?Sized, A: Allocator> Deref for UniqueRc<T, A> { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for UniqueRc<T> {} + #[unstable(feature = "unique_rc_arc", issue = "112566")] impl<T: ?Sized, A: Allocator> DerefMut for UniqueRc<T, A> { fn deref_mut(&mut self) -> &mut T { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 3ad0dae77db..2c0d19b0ada 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -20,7 +20,7 @@ use core::marker::{PhantomData, Unsize}; use core::mem::{self, align_of_val_raw, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::pin::Pin; +use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; @@ -2142,6 +2142,12 @@ impl<T: ?Sized, A: Allocator> Deref for Arc<T, A> { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Arc<T, A> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized, A: Allocator> PinCoerceUnsized for Weak<T, A> {} + #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl<T: ?Sized, A: Allocator> DerefPure for Arc<T, A> {} diff --git a/library/alloc/tests/arc.rs b/library/alloc/tests/arc.rs index c37a80dca95..dc27c578b57 100644 --- a/library/alloc/tests/arc.rs +++ b/library/alloc/tests/arc.rs @@ -227,3 +227,17 @@ fn make_mut_unsized() { assert_eq!(*data, [11, 21, 31]); assert_eq!(*other_data, [110, 20, 30]); } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::sync::Arc; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Arc + pub fn pin_arc(arg: Pin<Arc<String>>) -> Pin<Arc<dyn MyTrait>> { + arg + } +} diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index 4cacee0414d..faee64b2f67 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -179,3 +179,40 @@ unsafe impl Allocator for ConstAllocator { self } } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::boxed::Box; + use core::pin::Pin; + + trait MyTrait { + fn action(&self) -> &str; + } + impl MyTrait for String { + fn action(&self) -> &str { + &*self + } + } + struct MyStruct; + impl MyTrait for MyStruct { + fn action(&self) -> &str { + "MyStruct" + } + } + + // Pin coercion should work for Box + fn pin_box<T: MyTrait + 'static>(arg: Pin<Box<T>>) -> Pin<Box<dyn MyTrait>> { + arg + } + + #[test] + fn pin_coerce_unsized_box() { + let my_string = "my string"; + let a_string = Box::pin(String::from(my_string)); + let pin_box_str = pin_box(a_string); + assert_eq!(pin_box_str.as_ref().action(), my_string); + let a_struct = Box::pin(MyStruct); + let pin_box_struct = pin_box(a_struct); + assert_eq!(pin_box_struct.as_ref().action(), "MyStruct"); + } +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 89538f272f0..3d4add6fae4 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -40,6 +40,7 @@ #![feature(drain_keep_rest)] #![feature(local_waker)] #![feature(vec_pop_if)] +#![feature(unique_rc_arc)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloc/tests/rc.rs b/library/alloc/tests/rc.rs index 499740e738a..29dbdcf225e 100644 --- a/library/alloc/tests/rc.rs +++ b/library/alloc/tests/rc.rs @@ -205,3 +205,20 @@ fn weak_may_dangle() { // `val` dropped here while still borrowed // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak` } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::rc::{Rc, UniqueRc}; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Rc + pub fn pin_rc(arg: Pin<Rc<String>>) -> Pin<Rc<dyn MyTrait>> { + arg + } + pub fn pin_unique_rc(arg: Pin<UniqueRc<String>>) -> Pin<UniqueRc<dyn MyTrait>> { + arg + } +} diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 0d66c2b52c8..d860f3415d9 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -255,6 +255,7 @@ use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; mod lazy; @@ -2396,3 +2397,21 @@ fn assert_coerce_unsized( let _: Cell<&dyn Send> = c; let _: RefCell<&dyn Send> = d; } + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for UnsafeCell<T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for SyncUnsafeCell<T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for Cell<T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for RefCell<T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<'b, T: ?Sized> PinCoerceUnsized for Ref<'b, T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<'b, T: ?Sized> PinCoerceUnsized for RefMut<'b, T> {} diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index e9eacbcd25a..6f1e0cb7471 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1528,6 +1528,12 @@ extern "rust-intrinsic" { #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] pub fn unaligned_volatile_store<T>(dst: *mut T, val: T); + /// Returns the square root of an `f16` + /// + /// The stabilized version of this intrinsic is + /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is @@ -1540,6 +1546,12 @@ extern "rust-intrinsic" { /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_nounwind] pub fn sqrtf64(x: f64) -> f64; + /// Returns the square root of an `f128` + /// + /// The stabilized version of this intrinsic is + /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1566,6 +1578,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn powif128(a: f128, x: i32) -> f128; + /// Returns the sine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::sin`](../../std/primitive.f16.html#method.sin) + #[rustc_nounwind] + pub fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1578,7 +1596,19 @@ extern "rust-intrinsic" { /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_nounwind] pub fn sinf64(x: f64) -> f64; + /// Returns the sine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::sin`](../../std/primitive.f128.html#method.sin) + #[rustc_nounwind] + pub fn sinf128(x: f128) -> f128; + /// Returns the cosine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::cos`](../../std/primitive.f16.html#method.cos) + #[rustc_nounwind] + pub fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1591,7 +1621,19 @@ extern "rust-intrinsic" { /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_nounwind] pub fn cosf64(x: f64) -> f64; + /// Returns the cosine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::cos`](../../std/primitive.f128.html#method.cos) + #[rustc_nounwind] + pub fn cosf128(x: f128) -> f128; + /// Raises an `f16` to an `f16` power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powf`](../../std/primitive.f16.html#method.powf) + #[rustc_nounwind] + pub fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is @@ -1604,7 +1646,19 @@ extern "rust-intrinsic" { /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_nounwind] pub fn powf64(a: f64, x: f64) -> f64; + /// Raises an `f128` to an `f128` power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powf`](../../std/primitive.f128.html#method.powf) + #[rustc_nounwind] + pub fn powf128(a: f128, x: f128) -> f128; + /// Returns the exponential of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp`](../../std/primitive.f16.html#method.exp) + #[rustc_nounwind] + pub fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1617,7 +1671,19 @@ extern "rust-intrinsic" { /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_nounwind] pub fn expf64(x: f64) -> f64; + /// Returns the exponential of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp`](../../std/primitive.f128.html#method.exp) + #[rustc_nounwind] + pub fn expf128(x: f128) -> f128; + /// Returns 2 raised to the power of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1630,7 +1696,19 @@ extern "rust-intrinsic" { /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_nounwind] pub fn exp2f64(x: f64) -> f64; + /// Returns 2 raised to the power of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f128(x: f128) -> f128; + /// Returns the natural logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ln`](../../std/primitive.f16.html#method.ln) + #[rustc_nounwind] + pub fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1643,7 +1721,19 @@ extern "rust-intrinsic" { /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_nounwind] pub fn logf64(x: f64) -> f64; + /// Returns the natural logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ln`](../../std/primitive.f128.html#method.ln) + #[rustc_nounwind] + pub fn logf128(x: f128) -> f128; + /// Returns the base 10 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log10`](../../std/primitive.f16.html#method.log10) + #[rustc_nounwind] + pub fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1656,7 +1746,19 @@ extern "rust-intrinsic" { /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_nounwind] pub fn log10f64(x: f64) -> f64; + /// Returns the base 10 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log10`](../../std/primitive.f128.html#method.log10) + #[rustc_nounwind] + pub fn log10f128(x: f128) -> f128; + /// Returns the base 2 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log2`](../../std/primitive.f16.html#method.log2) + #[rustc_nounwind] + pub fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1669,7 +1771,19 @@ extern "rust-intrinsic" { /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_nounwind] pub fn log2f64(x: f64) -> f64; + /// Returns the base 2 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log2`](../../std/primitive.f128.html#method.log2) + #[rustc_nounwind] + pub fn log2f128(x: f128) -> f128; + /// Returns `a * b + c` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1682,7 +1796,19 @@ extern "rust-intrinsic" { /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_nounwind] pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + /// Returns the absolute value of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::abs`](../../std/primitive.f16.html#method.abs) + #[rustc_nounwind] + pub fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1695,7 +1821,25 @@ extern "rust-intrinsic" { /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] pub fn fabsf64(x: f64) -> f64; + /// Returns the absolute value of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::abs`](../../std/primitive.f128.html#method.abs) + #[rustc_nounwind] + pub fn fabsf128(x: f128) -> f128; + /// Returns the minimum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf16(x: f16, y: f16) -> f16; /// Returns the minimum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1720,6 +1864,31 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn minnumf64(x: f64, y: f64) -> f64; + /// Returns the minimum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf128(x: f128, y: f128) -> f128; + + /// Returns the maximum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf16(x: f16, y: f16) -> f16; /// Returns the maximum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1744,7 +1913,25 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn maxnumf64(x: f64, y: f64) -> f64; + /// Returns the maximum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf128(x: f128, y: f128) -> f128; + /// Copies the sign from `y` to `x` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1757,7 +1944,19 @@ extern "rust-intrinsic" { /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] pub fn copysignf64(x: f64, y: f64) -> f64; + /// Copies the sign from `y` to `x` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf128(x: f128, y: f128) -> f128; + /// Returns the largest integer less than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::floor`](../../std/primitive.f16.html#method.floor) + #[rustc_nounwind] + pub fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1770,7 +1969,19 @@ extern "rust-intrinsic" { /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_nounwind] pub fn floorf64(x: f64) -> f64; + /// Returns the largest integer less than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::floor`](../../std/primitive.f128.html#method.floor) + #[rustc_nounwind] + pub fn floorf128(x: f128) -> f128; + /// Returns the smallest integer greater than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1783,7 +1994,19 @@ extern "rust-intrinsic" { /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_nounwind] pub fn ceilf64(x: f64) -> f64; + /// Returns the smallest integer greater than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf128(x: f128) -> f128; + /// Returns the integer part of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) + #[rustc_nounwind] + pub fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1796,7 +2019,25 @@ extern "rust-intrinsic" { /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_nounwind] pub fn truncf64(x: f64) -> f64; + /// Returns the integer part of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) + #[rustc_nounwind] + pub fn truncf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1821,7 +2062,25 @@ extern "rust-intrinsic" { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1834,7 +2093,19 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn nearbyintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round`](../../std/primitive.f16.html#method.round) + #[rustc_nounwind] + pub fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1847,7 +2118,19 @@ extern "rust-intrinsic" { /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_nounwind] pub fn roundf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round`](../../std/primitive.f128.html#method.round) + #[rustc_nounwind] + pub fn roundf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number /// with an even least significant digit. /// @@ -1860,6 +2143,12 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn roundevenf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e74900ff747..9d4520d4ee8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -391,7 +391,7 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; -#[unstable(feature = "core_pattern_types", issue = "none")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod pin; #[unstable(feature = "new_range_api", issue = "125687")] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 6a24748fd9e..0c04f47fe7d 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -686,6 +686,182 @@ impl f128 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f128) -> f128 { + intrinsics::maxnumf128(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f128) -> f128 { + intrinsics::minnumf128(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f128) -> f128 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f128) -> f128 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(num_midpoint)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// assert_eq!(1f128.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f128).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f128) -> f128 { + const LO: f128 = f128::MIN_POSITIVE * 2.; + const HI: f128 = f128::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve `a` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 054897b3c96..e5b1148e192 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -720,6 +720,177 @@ impl f16 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f16) -> f16 { + intrinsics::maxnumf16(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f16) -> f16 { + intrinsics::minnumf16(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f16) -> f16 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f16) -> f16 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(num_midpoint)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert_eq!(1f16.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f16).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f16) -> f16 { + const LO: f16 = f16::MIN_POSITIVE * 2.; + const HI: f16 = f16::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve `a` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 08d863f17ca..7710e23edf0 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -797,6 +797,7 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { @@ -845,6 +846,7 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { @@ -1042,6 +1044,7 @@ impl f32 { /// assert_eq!(1f32.midpoint(4.0), 2.5); /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` + #[inline] #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f32) -> f32 { cfg_if! { @@ -1070,13 +1073,13 @@ impl f32 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 5d33eea6d01..a89859be7ef 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -805,6 +805,7 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { @@ -853,6 +854,7 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { @@ -1051,6 +1053,7 @@ impl f64 { /// assert_eq!(1f64.midpoint(4.0), 2.5); /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` + #[inline] #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; @@ -1064,13 +1067,13 @@ impl f64 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index a10c4593342..1f89d960be6 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -6,7 +6,7 @@ /// ``` #[macro_export] #[rustc_builtin_macro(pattern_type)] -#[unstable(feature = "core_pattern_type", issue = "none")] +#[unstable(feature = "core_pattern_type", issue = "123646")] macro_rules! pattern_type { ($($arg:tt)*) => { /* compiler built-in */ diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d752151d10c..0569b8b7624 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1715,10 +1715,56 @@ impl<Ptr: fmt::Pointer> fmt::Pointer for Pin<Ptr> { // for other reasons, though, so we just need to take care not to allow such // impls to land in std. #[stable(feature = "pin", since = "1.33.0")] -impl<Ptr, U> CoerceUnsized<Pin<U>> for Pin<Ptr> where Ptr: CoerceUnsized<U> {} +impl<Ptr, U> CoerceUnsized<Pin<U>> for Pin<Ptr> +where + Ptr: CoerceUnsized<U> + PinCoerceUnsized, + U: PinCoerceUnsized, +{ +} + +#[stable(feature = "pin", since = "1.33.0")] +impl<Ptr, U> DispatchFromDyn<Pin<U>> for Pin<Ptr> +where + Ptr: DispatchFromDyn<U> + PinCoerceUnsized, + U: PinCoerceUnsized, +{ +} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +/// Trait that indicates that this is a pointer or a wrapper for one, where +/// unsizing can be performed on the pointee when it is pinned. +/// +/// # Safety +/// +/// If this type implements `Deref`, then the concrete type returned by `deref` +/// and `deref_mut` must not change without a modification. The following +/// operations are not considered modifications: +/// +/// * Moving the pointer. +/// * Performing unsizing coercions on the pointer. +/// * Performing dynamic dispatch with the pointer. +/// * Calling `deref` or `deref_mut` on the pointer. +/// +/// The concrete type of a trait object is the type that the vtable corresponds +/// to. The concrete type of a slice is an array of the same element type and +/// the length specified in the metadata. The concrete type of a sized type +/// is the type itself. +pub unsafe trait PinCoerceUnsized {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a T {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a mut T {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<T: PinCoerceUnsized> PinCoerceUnsized for Pin<T> {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<T: ?Sized> PinCoerceUnsized for *const T {} #[stable(feature = "pin", since = "1.33.0")] -impl<Ptr, U> DispatchFromDyn<Pin<U>> for Pin<Ptr> where Ptr: DispatchFromDyn<U> {} +unsafe impl<T: ?Sized> PinCoerceUnsized for *mut T {} /// Constructs a <code>[Pin]<[&mut] T></code>, by pinning a `value: T` locally. /// diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 5989bcbcc52..09ebef89fb0 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1244,6 +1244,9 @@ mod prim_f64 {} /// actually implement it. For x86-64 and AArch64, ISA support is not even specified, /// so it will always be a software implementation significantly slower than `f64`. /// +/// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On +/// x86 in particular, these functions do link but their results are always incorrect._ +/// /// *[See also the `std::f128::consts` module](crate::f128::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 4a716a75039..d6be37a76bb 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -3,6 +3,7 @@ use crate::marker::Unsize; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; use crate::ub_checks::assert_unsafe_precondition; @@ -1168,9 +1169,7 @@ impl<T: ?Sized> NonNull<T> { /// `align`. /// /// If it is not possible to align the pointer, the implementation returns - /// `usize::MAX`. It is permissible for the implementation to *always* - /// return `usize::MAX`. Only your algorithm's performance can depend - /// on getting a usable offset here, not its correctness. + /// `usize::MAX`. /// /// The offset is expressed in number of `T` elements, and not bytes. /// @@ -1178,6 +1177,15 @@ impl<T: ?Sized> NonNull<T> { /// beyond the allocation that the pointer points into. It is up to the caller to ensure that /// the returned offset is correct in all terms other than alignment. /// + /// When this is called during compile-time evaluation (which is unstable), the implementation + /// may return `usize::MAX` in cases where that can never happen at runtime. This is because the + /// actual alignment of pointers is not known yet during compile-time, so an offset with + /// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8; + /// N]` might be allocated at an odd or an even address, but at compile-time this is not yet + /// known, so the execution has to be correct for either choice. It is therefore impossible to + /// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual + /// for unstable APIs.) + /// /// # Panics /// /// The function panics if `align` is not a power-of-two. @@ -1724,6 +1732,9 @@ impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Uns #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<T: ?Sized> PinCoerceUnsized for NonNull<T> {} + #[stable(feature = "nonnull", since = "1.25.0")] impl<T: ?Sized> fmt::Debug for NonNull<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index b74d691e454..4810ebe01f9 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -1,6 +1,7 @@ use crate::fmt; use crate::marker::{PhantomData, Unsize}; use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::NonNull; /// A wrapper around a raw non-null `*mut T` that indicates that the possessor @@ -166,6 +167,9 @@ impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsiz #[unstable(feature = "ptr_internals", issue = "none")] impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {} +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for Unique<T> {} + #[unstable(feature = "ptr_internals", issue = "none")] impl<T: ?Sized> fmt::Debug for Unique<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/tests/pin.rs b/library/core/tests/pin.rs index 6f617c8d0c2..7a6af46a743 100644 --- a/library/core/tests/pin.rs +++ b/library/core/tests/pin.rs @@ -29,3 +29,49 @@ fn pin_const() { pin_mut_const(); } + +#[allow(unused)] +mod pin_coerce_unsized { + use core::cell::{Cell, RefCell, UnsafeCell}; + use core::pin::Pin; + use core::ptr::NonNull; + + pub trait MyTrait {} + impl MyTrait for String {} + + // These Pins should continue to compile. + // Do note that these instances of Pin types cannot be used + // meaningfully because all methods require a Deref/DerefMut + // bounds on the pointer type and Cell, RefCell and UnsafeCell + // do not implement Deref/DerefMut. + + pub fn cell(arg: Pin<Cell<Box<String>>>) -> Pin<Cell<Box<dyn MyTrait>>> { + arg + } + pub fn ref_cell(arg: Pin<RefCell<Box<String>>>) -> Pin<RefCell<Box<dyn MyTrait>>> { + arg + } + pub fn unsafe_cell(arg: Pin<UnsafeCell<Box<String>>>) -> Pin<UnsafeCell<Box<dyn MyTrait>>> { + arg + } + + // These sensible Pin coercions are possible. + pub fn pin_mut_ref(arg: Pin<&mut String>) -> Pin<&mut dyn MyTrait> { + arg + } + pub fn pin_ref(arg: Pin<&String>) -> Pin<&dyn MyTrait> { + arg + } + pub fn pin_ptr(arg: Pin<*const String>) -> Pin<*const dyn MyTrait> { + arg + } + pub fn pin_ptr_mut(arg: Pin<*mut String>) -> Pin<*mut dyn MyTrait> { + arg + } + pub fn pin_non_null(arg: Pin<NonNull<String>>) -> Pin<NonNull<dyn MyTrait>> { + arg + } + pub fn nesting_pins(arg: Pin<Pin<&String>>) -> Pin<Pin<&dyn MyTrait>> { + arg + } +} diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index fe601855cc1..2ce284c85e2 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.114" } +compiler_builtins = { version = "0.1.118" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = [ diff --git a/library/std/build.rs b/library/std/build.rs index 9b58dd53ba2..35a5977b6eb 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -11,6 +11,7 @@ fn main() { .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") .parse() .unwrap(); + let is_miri = env::var_os("CARGO_CFG_MIRI").is_some(); println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { @@ -85,7 +86,14 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + // This is a step beyond only having the types and basic functions available. Math functions + // aren't consistently available or correct. + println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)"); + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { + // We can always enable these in Miri as that is not affected by codegen bugs. + _ if is_miri => true, // Selection failure until recent LLVM <https://github.com/llvm/llvm-project/issues/93894> // FIXME(llvm19): can probably be removed at the version bump ("loongarch64", _) => false, @@ -113,6 +121,8 @@ fn main() { }; let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) { + // We can always enable these in Miri as that is not affected by codegen bugs. + _ if is_miri => true, // Unsupported <https://github.com/llvm/llvm-project/issues/94434> ("arm64ec", _) => false, // ABI and precision bugs <https://github.com/rust-lang/rust/issues/125109> @@ -130,10 +140,46 @@ fn main() { _ => false, }; + // These are currently empty, but will fill up as some platforms move from completely + // unreliable to reliable basics but unreliable math. + + // LLVM is currenlty adding missing routines, <https://github.com/llvm/llvm-project/issues/93566> + let has_reliable_f16_math = has_reliable_f16 + && match (target_arch.as_str(), target_os.as_str()) { + // FIXME: Disabled on Miri as the intrinsics are not implemented yet. + _ if is_miri => false, + // Currently nothing special. Hooray! + // This will change as platforms gain better better support for standard ops but math + // lags behind. + _ => true, + }; + + let has_reliable_f128_math = has_reliable_f128 + && match (target_arch.as_str(), target_os.as_str()) { + // FIXME: Disabled on Miri as the intrinsics are not implemented yet. + _ if is_miri => false, + // LLVM lowers `fp128` math to `long double` symbols even on platforms where + // `long double` is not IEEE binary128. See + // <https://github.com/llvm/llvm-project/issues/44744>. + // + // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits + // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` + // (ld is 80-bit extended precision). + ("x86_64", _) => false, + (_, "linux") if target_pointer_width == 64 => true, + _ => false, + }; + if has_reliable_f16 { println!("cargo:rustc-cfg=reliable_f16"); } if has_reliable_f128 { println!("cargo:rustc-cfg=reliable_f128"); } + if has_reliable_f16_math { + println!("cargo:rustc-cfg=reliable_f16_math"); + } + if has_reliable_f128_math { + println!("cargo:rustc-cfg=reliable_f128_math"); + } } diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index a5b00d57cef..f6df6259137 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -12,25 +12,180 @@ pub use core::f128::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } + pub fn floor(self) -> f128 { + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + unsafe { intrinsics::rintf128(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -41,7 +196,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// # #[cfg(reliable_f128)] { /// /// let x = 3.5_f128; /// let y = -3.5_f128; @@ -61,4 +216,1129 @@ impl f128 { // We don't do this now because LLVM has lowering bugs for f128 math. Self::from_bits(self.to_bits() & !(1 << 127)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f128::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f128::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f128 { + if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f128) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f128); + /// assert_eq!(f.copysign(-0.42), -3.5_f128); + /// assert_eq!((-f).copysign(0.42), 3.5_f128); + /// assert_eq!((-f).copysign(-0.42), -3.5_f128); + /// + /// assert!(f128::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f128) -> f128 { + unsafe { intrinsics::copysignf128(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + unsafe { intrinsics::powif128(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f128) -> f128 { + unsafe { intrinsics::powf128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + unsafe { intrinsics::sqrtf128(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f128 { + unsafe { intrinsics::expf128(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 2.0f128; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f128 { + unsafe { intrinsics::exp2f128(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f128 { + unsafe { intrinsics::logf128(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let five = 5.0f128; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f128) -> f128 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let two = 2.0f128; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f128 { + unsafe { intrinsics::log2f128(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let ten = 10.0f128; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f128 { + unsafe { intrinsics::log10f128(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `cbrtf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 8.0f128; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f128 { + unsafe { cmath::cbrtf128(self) } + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `hypotf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// let y = 3.0f128; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f128) -> f128 { + unsafe { cmath::hypotf128(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f128 { + unsafe { intrinsics::sinf128(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0 * std::f128::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f128 { + unsafe { intrinsics::cosf128(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf128` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f128 { + unsafe { cmath::tanf128(self) } + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f128::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f128 { + unsafe { cmath::asinf128(self) } + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f128::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f128 { + unsafe { cmath::acosf128(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 1.0f128; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f128 { + unsafe { cmath::atanf128(self) } + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f128; + /// let y1 = -3.0f128; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f128; + /// let y2 = 3.0f128; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f128::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f128::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f128::EPSILON); + /// assert!(abs_difference_2 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f128) -> f128 { + unsafe { cmath::atan2f128(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f128::sin(x), + /// f128::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f128::EPSILON); + /// assert!(abs_difference_1 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn sin_cos(self) -> (f128, f128) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f128 { + unsafe { cmath::expm1f128(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn ln_1p(self) -> f128 { + unsafe { cmath::log1pf128(self) } + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f128 { + unsafe { cmath::sinhf128(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f128 { + unsafe { cmath::coshf128(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f128 { + unsafe { cmath::tanhf128(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f128 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f128 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 1e-5); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f128 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 5.0f128; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f128 { + unsafe { cmath::tgammaf128(self) } + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgammaf128_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f128, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index 162c8dbad81..7051c051bf7 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -4,6 +4,21 @@ use crate::f128::consts; use crate::num::{FpCategory as Fp, *}; +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; + /// Smallest number const TINY_BITS: u128 = 0x1; @@ -41,7 +56,33 @@ fn test_num_f128() { test_num(10f128, 2f128); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} #[test] fn test_nan() { @@ -191,9 +232,100 @@ fn test_classify() { assert_eq!(1e-4932f128.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} #[test] +#[cfg(reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] fn test_abs() { assert_eq!(f128::INFINITY.abs(), f128::INFINITY); assert_eq!(1f128.abs(), 1f128); @@ -293,6 +425,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -301,11 +451,161 @@ fn test_recip() { assert_eq!(2.0f128.recip(), 0.5); assert_eq!((-0.4f128).recip(), -2.5); assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f128 = consts::PI; @@ -313,8 +613,8 @@ fn test_to_degrees() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -328,19 +628,122 @@ fn test_to_radians() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.698279); - assert_approx_eq!((-332.31f128).to_radians(), -5.799903); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); // check approx rather than exact because round trip for pi doesn't fall on an exactly // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi); + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } #[test] +#[cfg(reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + +#[test] fn test_real_consts() { - // FIXME(f16_f128): add math tests when available use super::consts; let pi: f128 = consts::PI; @@ -351,29 +754,34 @@ fn test_real_consts() { let frac_pi_8: f128 = consts::FRAC_PI_8; let frac_1_pi: f128 = consts::FRAC_1_PI; let frac_2_pi: f128 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f128 = consts::SQRT_2; - // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - // let e: f128 = consts::E; - // let log2_e: f128 = consts::LOG2_E; - // let log10_e: f128 = consts::LOG10_E; - // let ln_2: f128 = consts::LN_2; - // let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f128); - assert_approx_eq!(frac_pi_3, pi / 3f128); - assert_approx_eq!(frac_pi_4, pi / 4f128); - assert_approx_eq!(frac_pi_6, pi / 6f128); - assert_approx_eq!(frac_pi_8, pi / 8f128); - assert_approx_eq!(frac_1_pi, 1f128 / pi); - assert_approx_eq!(frac_2_pi, 2f128 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f128.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f128.ln()); - // assert_approx_eq!(ln_10, 10f128.ln()); + + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(reliable_f128_math)] + { + let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + let sqrt2: f128 = consts::SQRT_2; + let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + let e: f128 = consts::E; + let log2_e: f128 = consts::LOG2_E; + let log10_e: f128 = consts::LOG10_E; + let ln_2: f128 = consts::LN_2; + let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } } #[test] @@ -382,10 +790,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index e3024defed7..10908332762 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -12,25 +12,180 @@ pub use core::f16::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } + pub fn floor(self) -> f16 { + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + unsafe { intrinsics::rintf16(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -60,4 +215,1127 @@ impl f16 { // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available Self::from_bits(self.to_bits() & !(1 << 15)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f16::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f16::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f16 { + if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f16) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f16); + /// assert_eq!(f.copysign(-0.42), -3.5_f16); + /// assert_eq!((-f).copysign(0.42), 3.5_f16); + /// assert_eq!((-f).copysign(-0.42), -3.5_f16); + /// + /// assert!(f16::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f16) -> f16 { + unsafe { intrinsics::copysignf16(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + unsafe { intrinsics::powif16(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f16) -> f16 { + unsafe { intrinsics::powf16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f16 { + unsafe { intrinsics::expf16(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 2.0f16; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f16 { + unsafe { intrinsics::exp2f16(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f16 { + unsafe { intrinsics::logf16(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let five = 5.0f16; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f16) -> f16 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let two = 2.0f16; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f16 { + unsafe { intrinsics::log2f16(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let ten = 10.0f16; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f16 { + unsafe { intrinsics::log10f16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + (unsafe { cmath::cbrtf(self as f32) }) as f16 + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `hypotf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// let y = 3.0f16; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f16) -> f16 { + (unsafe { cmath::hypotf(self as f32, other as f32) }) as f16 + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f16 { + unsafe { intrinsics::sinf16(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0 * std::f16::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f16 { + unsafe { intrinsics::cosf16(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f16 { + (unsafe { cmath::tanf(self as f32) }) as f16 + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f16::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f16 { + (unsafe { cmath::asinf(self as f32) }) as f16 + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f16::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f16 { + (unsafe { cmath::acosf(self as f32) }) as f16 + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 1.0f16; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f16 { + (unsafe { cmath::atanf(self as f32) }) as f16 + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f16; + /// let y1 = -3.0f16; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f16; + /// let y2 = 3.0f16; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f16::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f16::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f16::EPSILON); + /// assert!(abs_difference_2 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f16) -> f16 { + (unsafe { cmath::atan2f(self as f32, other as f32) }) as f16 + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f16::sin(x), + /// f16::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f16::EPSILON); + /// assert!(abs_difference_1 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + pub fn sin_cos(self) -> (f16, f16) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f16 { + (unsafe { cmath::expm1f(self as f32) }) as f16 + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_1p(self) -> f16 { + (unsafe { cmath::log1pf(self as f32) }) as f16 + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f16 { + (unsafe { cmath::sinhf(self as f32) }) as f16 + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f16 { + (unsafe { cmath::coshf(self as f32) }) as f16 + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f16 { + (unsafe { cmath::tanhf(self as f32) }) as f16 + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f16 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f16 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f16 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 5.0f16; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f16 { + (unsafe { cmath::tgammaf(self as f32) }) as f16 + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f16, i32) { + let mut signgamp: i32 = 0; + let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; + (x, signgamp) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index f73bdf68e82..684ee3f3855 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -4,11 +4,21 @@ use crate::f16::consts; use crate::num::{FpCategory as Fp, *}; -// We run out of precision pretty quickly with f16 -// const F16_APPROX_L1: f16 = 0.001; -const F16_APPROX_L2: f16 = 0.01; -// const F16_APPROX_L3: f16 = 0.1; -const F16_APPROX_L4: f16 = 0.5; +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; /// Smallest number const TINY_BITS: u16 = 0x1; @@ -47,7 +57,33 @@ fn test_num_f16() { test_num(10f16, 2f16); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} #[test] fn test_nan() { @@ -197,9 +233,100 @@ fn test_classify() { assert_eq!(1e-5f16.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} #[test] +#[cfg(reliable_f16_math)] fn test_abs() { assert_eq!(f16::INFINITY.abs(), f16::INFINITY); assert_eq!(1f16.abs(), 1f16); @@ -299,6 +426,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -307,20 +452,166 @@ fn test_recip() { assert_eq!(2.0f16.recip(), 0.5); assert_eq!((-0.4f16).recip(), -2.5); assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } #[test] +#[cfg(reliable_f16_math)] +fn test_powi() { + // FIXME(llvm19): LLVM misoptimizes `powi.f16` + // <https://github.com/llvm/llvm-project/issues/98665> + // let nan: f16 = f16::NAN; + // let inf: f16 = f16::INFINITY; + // let neg_inf: f16 = f16::NEG_INFINITY; + // assert_eq!(1.0f16.powi(1), 1.0); + // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + // assert_eq!(8.3f16.powi(0), 1.0); + // assert!(nan.powi(2).is_nan()); + // assert_eq!(inf.powi(3), inf); + // assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + +#[test] fn test_to_degrees() { let pi: f16 = consts::PI; let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521); - assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -334,15 +625,113 @@ fn test_to_radians() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903); - assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } #[test] +#[cfg(reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + +#[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available use super::consts; @@ -355,29 +744,34 @@ fn test_real_consts() { let frac_pi_8: f16 = consts::FRAC_PI_8; let frac_1_pi: f16 = consts::FRAC_1_PI; let frac_2_pi: f16 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f16 = consts::SQRT_2; - // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - // let e: f16 = consts::E; - // let log2_e: f16 = consts::LOG2_E; - // let log10_e: f16 = consts::LOG10_E; - // let ln_2: f16 = consts::LN_2; - // let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f16); - assert_approx_eq!(frac_pi_3, pi / 3f16); - assert_approx_eq!(frac_pi_4, pi / 4f16); - assert_approx_eq!(frac_pi_6, pi / 6f16); - assert_approx_eq!(frac_pi_8, pi / 8f16); - assert_approx_eq!(frac_1_pi, 1f16 / pi); - assert_approx_eq!(frac_2_pi, 2f16 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f16.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f16.ln()); - // assert_approx_eq!(ln_10, 10f16.ln()); + + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(reliable_f16_math)] + { + let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + let sqrt2: f16 = consts::SQRT_2; + let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + let e: f16 = consts::E; + let log2_e: f16 = consts::LOG2_E; + let log10_e: f16 = consts::LOG10_E; + let ln_2: f16 = consts::LN_2; + let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } } #[test] @@ -386,10 +780,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f16).to_bits(), 0x4a40); assert_eq!((1337f16).to_bits(), 0x6539); assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index f11dd50c5e2..0b12e5777c8 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -94,6 +94,40 @@ impl<R: Read> BufReader<R> { pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> { BufReader { inner, buf: Buffer::with_capacity(capacity) } } + + /// Attempt to look ahead `n` bytes. + /// + /// `n` must be less than `capacity`. + /// + /// ## Examples + /// + /// ```rust + /// #![feature(bufreader_peek)] + /// use std::io::{Read, BufReader}; + /// + /// let mut bytes = &b"oh, hello"[..]; + /// let mut rdr = BufReader::with_capacity(6, &mut bytes); + /// assert_eq!(rdr.peek(2).unwrap(), b"oh"); + /// let mut buf = [0; 4]; + /// rdr.read(&mut buf[..]).unwrap(); + /// assert_eq!(&buf, b"oh, "); + /// assert_eq!(rdr.peek(2).unwrap(), b"he"); + /// let mut s = String::new(); + /// rdr.read_to_string(&mut s).unwrap(); + /// assert_eq!(&s, "hello"); + /// ``` + #[unstable(feature = "bufreader_peek", issue = "128405")] + pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { + assert!(n <= self.capacity()); + while n > self.buf.buffer().len() { + if self.buf.pos() > 0 { + self.buf.backshift(); + } + self.buf.read_more(&mut self.inner)?; + debug_assert_eq!(self.buf.pos(), 0); + } + Ok(&self.buf.buffer()[..n]) + } } impl<R: ?Sized> BufReader<R> { diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 796137c0123..ccd67fafb45 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -97,6 +97,27 @@ impl Buffer { self.pos = self.pos.saturating_sub(amt); } + /// Read more bytes into the buffer without discarding any of its contents + pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> { + let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); + let old_init = self.initialized - self.pos; + unsafe { + buf.set_init(old_init); + } + reader.read_buf(buf.unfilled())?; + self.filled += buf.len(); + self.initialized += buf.init_len() - old_init; + Ok(()) + } + + /// Remove bytes that have already been read from the buffer. + pub fn backshift(&mut self) { + self.buf.copy_within(self.pos.., 0); + self.initialized -= self.pos; + self.filled -= self.pos; + self.pos = 0; + } + #[inline] pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index c82228fca4b..9f4d244b547 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -155,7 +155,7 @@ mod break_keyword {} /// const WORDS: &str = "hello convenience!"; /// ``` /// -/// `const` items looks remarkably similar to `static` items, which introduces some confusion as +/// `const` items look remarkably similar to `static` items, which introduces some confusion as /// to which one should be used at which times. To put it simply, constants are inlined wherever /// they're used, making using them identical to simply replacing the name of the `const` with its /// value. Static variables, on the other hand, point to a single location in memory, which all diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 05e33d47bac..7fc1bc46fef 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -339,6 +339,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] +#![feature(pin_coerce_unsized_trait)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(prelude_2024)] @@ -585,7 +586,7 @@ pub mod net; pub mod num; pub mod os; pub mod panic; -#[unstable(feature = "core_pattern_types", issue = "none")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod path; #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index ba519afc62b..1b0d7f3dbf2 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -382,7 +382,7 @@ macro_rules! assert_approx_eq { let diff = (*a - *b).abs(); assert!( diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", lim = $lim ); }}; diff --git a/library/std/src/os/vxworks/mod.rs b/library/std/src/os/vxworks/mod.rs index 0a7ac641dd3..b09aa72f726 100644 --- a/library/std/src/os/vxworks/mod.rs +++ b/library/std/src/os/vxworks/mod.rs @@ -1,6 +1,7 @@ //! VxWorks-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs index 12bb0fbf050..37a2e41641a 100644 --- a/library/std/src/sync/rwlock/tests.rs +++ b/library/std/src/sync/rwlock/tests.rs @@ -21,6 +21,10 @@ fn smoke() { } #[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri +// catches that issue with a chance of around 1/1000. +// See <https://github.com/rust-lang/rust/issues/121950> for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] fn frob() { const N: u32 = 10; const M: usize = if cfg!(miri) { 100 } else { 1000 }; diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 99df503b82d..2997e908fa1 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -28,6 +28,21 @@ extern "C" { pub fn lgamma_r(n: f64, s: &mut i32) -> f64; pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub fn acosf128(n: f128) -> f128; + pub fn asinf128(n: f128) -> f128; + pub fn atanf128(n: f128) -> f128; + pub fn atan2f128(a: f128, b: f128) -> f128; + pub fn cbrtf128(n: f128) -> f128; + pub fn coshf128(n: f128) -> f128; + pub fn expm1f128(n: f128) -> f128; + pub fn hypotf128(x: f128, y: f128) -> f128; + pub fn log1pf128(n: f128) -> f128; + pub fn sinhf128(n: f128) -> f128; + pub fn tanf128(n: f128) -> f128; + pub fn tanhf128(n: f128) -> f128; + pub fn tgammaf128(n: f128) -> f128; + pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { pub fn acosf(n: f32) -> f32; diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index 29809525739..5069ab82ccc 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -8,6 +8,7 @@ use crate::cell::UnsafeCell; use crate::convert::TryInto; use crate::mem::{self, ManuallyDrop}; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; +use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; use crate::slice::SliceIndex; use crate::{cmp, intrinsics, slice}; @@ -751,6 +752,9 @@ where #[unstable(feature = "sgx_platform", issue = "56975")] impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {} +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<T: ?Sized> PinCoerceUnsized for UserRef<T> {} + #[unstable(feature = "sgx_platform", issue = "56975")] impl<T, I> Index<I> for UserRef<[T]> where diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/pal/unix/process/process_unix/tests.rs index e5e1f956bc3..f4d6ac6b4e3 100644 --- a/library/std/src/sys/pal/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/pal/unix/process/process_unix/tests.rs @@ -24,7 +24,20 @@ fn exitstatus_display_tests() { // The purpose of this test is to test our string formatting, not our understanding of the wait // status magic numbers. So restrict these to Linux. if cfg!(target_os = "linux") { + #[cfg(any(target_arch = "mips", target_arch = "mips64"))] + t(0x0137f, "stopped (not terminated) by signal: 19 (SIGPWR)"); + + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + t(0x0137f, "stopped (not terminated) by signal: 19 (SIGCONT)"); + + #[cfg(not(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "sparc", + target_arch = "sparc64" + )))] t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)"); + t(0x0ffff, "continued (WIFCONTINUED)"); } diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs index 6a9d8fab1d4..0477b3d9a70 100644 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use libc::{self, c_char, c_int, RTP_ID}; use crate::io::{self, ErrorKind}; diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 44cb7b7b7ce..0fa610eebb4 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -3,7 +3,7 @@ use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; -#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; use crate::time::Duration; @@ -212,17 +212,31 @@ impl Thread { } } + #[cfg(target_os = "vxworks")] + pub fn set_name(name: &CStr) { + // FIXME(libc): adding real STATUS, ERROR type eventually. + extern "C" { + fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; + } + + // VX_TASK_NAME_LEN is 31 in VxWorks 7. + const VX_TASK_NAME_LEN: usize = 31; + + let mut name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); + let res = unsafe { taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); + } + #[cfg(any( target_env = "newlib", target_os = "l4re", target_os = "emscripten", target_os = "redox", - target_os = "vxworks", target_os = "hurd", target_os = "aix", ))] pub fn set_name(_name: &CStr) { - // Newlib, Emscripten, and VxWorks have no way to set a thread name. + // Newlib and Emscripten have no way to set a thread name. } #[cfg(not(target_os = "espidf"))] @@ -291,6 +305,7 @@ impl Drop for Thread { target_os = "nto", target_os = "solaris", target_os = "illumos", + target_os = "vxworks", target_vendor = "apple", ))] fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { @@ -462,9 +477,11 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { fn vxCpuEnabledGet() -> libc::cpuset_t; } - // always fetches a valid bitmask - let set = unsafe { vxCpuEnabledGet() }; - Ok(NonZero::new_unchecked(set.count_ones() as usize)) + // SAFETY: `vxCpuEnabledGet` always fetches a mask with at least one bit set + unsafe{ + let set = vxCpuEnabledGet(); + Ok(NonZero::new_unchecked(set.count_ones() as usize)) + } } else { // FIXME: implement on Redox, l4re Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index 258a0347451..11316004412 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/126298 +Last change is for: https://github.com/rust-lang/rust/pull/125642 diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index c14709ffb63..43306eab1b1 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -918,7 +918,6 @@ impl Step for Src { // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs` let dst_src = tarball.image_dir().join("lib/rustlib/src/rust"); - let src_files = ["Cargo.lock"]; // This is the reduced set of paths which will become the rust-src component // (essentially libstd and all of its path dependencies). copy_src_dirs( @@ -937,9 +936,6 @@ impl Step for Src { ], &dst_src, ); - for file in src_files.iter() { - builder.copy_link(&builder.src.join(file), &dst_src.join(file)); - } tarball.generate() } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 81e591be699..ffae245dfb5 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -368,9 +368,7 @@ impl Step for Llvm { cfg.define("LLVM_PROFDATA_FILE", path); } - // Disable zstd to avoid a dependency on libzstd.so. - cfg.define("LLVM_ENABLE_ZSTD", "OFF"); - + // Libraries for ELF section compression. if !target.is_windows() { cfg.define("LLVM_ENABLE_ZLIB", "ON"); } else { @@ -824,6 +822,14 @@ fn configure_llvm(builder: &Builder<'_>, target: TargetSelection, cfg: &mut cmak } } + // Libraries for ELF section compression. + if builder.config.llvm_libzstd { + cfg.define("LLVM_ENABLE_ZSTD", "FORCE_ON"); + cfg.define("LLVM_USE_STATIC_ZSTD", "TRUE"); + } else { + cfg.define("LLVM_ENABLE_ZSTD", "OFF"); + } + if let Some(ref linker) = builder.config.llvm_use_linker { cfg.define("LLVM_USE_LINKER", linker); } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index fde1693646a..65d635c0bd6 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -212,11 +212,13 @@ impl Step for GenerateCopyright { let license_metadata = builder.ensure(CollectLicenseMetadata); // Temporary location, it will be moved to the proper one once it's accurate. - let dest = builder.out.join("COPYRIGHT.md"); + let dest = builder.out.join("COPYRIGHT.html"); let mut cmd = builder.tool_cmd(Tool::GenerateCopyright); cmd.env("LICENSE_METADATA", &license_metadata); cmd.env("DEST", &dest); + cmd.env("OUT_DIR", &builder.out); + cmd.env("CARGO", &builder.initial_cargo); cmd.run(builder); dest diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c40aa718e7c..597d7733abe 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -520,6 +520,14 @@ impl Step for Miri { builder.ensure(compile::Std::new(target_compiler, host)); let host_sysroot = builder.sysroot(target_compiler); + // Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared + // properly when rustc changes. Similar to `Builder::cargo`, we skip this in dry runs to + // make sure the relevant compiler has been set up properly. + if !builder.config.dry_run() { + let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("miri_ui"); + builder.clear_if_dirty(&ui_test_dep_dir, &builder.rustc(host_compiler)); + } + // Run `cargo test`. // This is with the Miri crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 14beef20bad..915fcb449e8 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -218,6 +218,7 @@ pub struct Config { pub llvm_thin_lto: bool, pub llvm_release_debuginfo: bool, pub llvm_static_stdcpp: bool, + pub llvm_libzstd: bool, /// `None` if `llvm_from_ci` is true and we haven't yet downloaded llvm. #[cfg(not(test))] llvm_link_shared: Cell<Option<bool>>, @@ -878,6 +879,7 @@ define_config! { plugins: Option<bool> = "plugins", ccache: Option<StringOrBool> = "ccache", static_libstdcpp: Option<bool> = "static-libstdcpp", + libzstd: Option<bool> = "libzstd", ninja: Option<bool> = "ninja", targets: Option<String> = "targets", experimental_targets: Option<String> = "experimental-targets", @@ -1153,6 +1155,7 @@ impl Config { llvm_optimize: true, ninja_in_file: true, llvm_static_stdcpp: false, + llvm_libzstd: false, backtrace: true, rust_optimize: RustOptimize::Bool(true), rust_optimize_tests: true, @@ -1325,7 +1328,11 @@ impl Config { // Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path, // but not if `config.toml` hasn't been created. let mut toml = if !using_default_path || toml_path.exists() { - config.config = Some(toml_path.clone()); + config.config = Some(if cfg!(not(feature = "bootstrap-self-test")) { + toml_path.canonicalize().unwrap() + } else { + toml_path.clone() + }); get_toml(&toml_path) } else { config.config = None; @@ -1787,6 +1794,7 @@ impl Config { plugins, ccache, static_libstdcpp, + libzstd, ninja, targets, experimental_targets, @@ -1821,6 +1829,7 @@ impl Config { set(&mut config.llvm_thin_lto, thin_lto); set(&mut config.llvm_release_debuginfo, release_debuginfo); set(&mut config.llvm_static_stdcpp, static_libstdcpp); + set(&mut config.llvm_libzstd, libzstd); if let Some(v) = link_shared { config.llvm_link_shared.set(Some(v)); } @@ -1871,6 +1880,7 @@ impl Config { check_ci_llvm!(optimize_toml); check_ci_llvm!(thin_lto); check_ci_llvm!(release_debuginfo); + check_ci_llvm!(libzstd); check_ci_llvm!(targets); check_ci_llvm!(experimental_targets); check_ci_llvm!(clang_cl); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 453fb39327d..2062d435bfc 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -986,7 +986,8 @@ impl Build { } /// Execute a command and return its output. - /// This method should be used for all command executions in bootstrap. + /// Note: Ideally, you should use one of the BootstrapCommand::run* functions to + /// execute commands. They internally call this method. #[track_caller] fn run( &self, @@ -1057,20 +1058,28 @@ Executed at: {executed_at}"#, CommandOutput::did_not_start(stdout, stderr) } }; + + let fail = |message: &str| { + if self.is_verbose() { + println!("{message}"); + } else { + println!("Command has failed. Rerun with -v to see more details."); + } + exit!(1); + }; + if !output.is_success() { match command.failure_behavior { BehaviorOnFailure::DelayFail => { if self.fail_fast { - println!("{message}"); - exit!(1); + fail(&message); } let mut failures = self.delayed_failures.borrow_mut(); failures.push(message); } BehaviorOnFailure::Exit => { - println!("{message}"); - exit!(1); + fail(&message); } BehaviorOnFailure::Ignore => { // If failures are allowed, either the error has been printed already diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b8f70fdf6a8..84dac25188e 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -220,4 +220,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "For tarball sources, default value for `rust.channel` will be taken from `src/ci/channel` file.", }, + ChangeInfo { + change_id: 125642, + severity: ChangeSeverity::Info, + summary: "New option `llvm.libzstd` to control whether llvm is built with zstd support.", + }, ]; diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index bfe2f084552..3f7f6214cf6 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -317,6 +317,12 @@ impl<'a> Tarball<'a> { channel::write_commit_hash_file(&self.overlay_dir, &info.sha); channel::write_commit_info_file(&self.overlay_dir, info); } + + // Add config file if present. + if let Some(config) = &self.builder.config.config { + self.add_renamed_file(config, &self.overlay_dir, "builder-config"); + } + for file in self.overlay.legal_and_readme() { self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); } diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 4aa1a3ccc2a..61e9694f1e2 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -62,6 +62,10 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ RUN ./build-clang.sh ENV CC=clang CXX=clang++ +# rustc's LLVM needs zstd. +COPY scripts/zstd.sh /tmp/ +RUN ./zstd.sh + COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -79,6 +83,7 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ --set llvm.thin-lto=true \ --set llvm.ninja=false \ + --set llvm.libzstd=true \ --set rust.jemalloc \ --set rust.use-lld=true \ --set rust.lto=thin \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile index 9025e9bb0a3..19683317126 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile @@ -17,6 +17,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config \ xz-utils \ mingw-w64 \ + zlib1g-dev \ + libzstd-dev \ && rm -rf /var/lib/apt/lists/* COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index 00269e68422..a5458b8645d 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -571,6 +571,19 @@ class TestEnvironment: ) # Start repository server + # Note that we must first enable the repository server daemon. + check_call_with_logging( + [ + ffx_path, + "config", + "set", + "repository.server.enabled", + "true", + ], + env=ffx_env, + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) check_call_with_logging( [ ffx_path, diff --git a/src/ci/docker/scripts/zstd.sh b/src/ci/docker/scripts/zstd.sh new file mode 100755 index 00000000000..a3d37ccc311 --- /dev/null +++ b/src/ci/docker/scripts/zstd.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -ex + +hide_output() { + set +x + on_err=" +echo ERROR: An error was encountered with the build. +cat /tmp/zstd_build.log +exit 1 +" + trap "$on_err" ERR + bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & + PING_LOOP_PID=$! + "$@" &> /tmp/zstd_build.log + trap - ERR + kill $PING_LOOP_PID + rm /tmp/zstd_build.log + set -x +} + +ZSTD=1.5.6 +curl -L https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz | tar xzf - + +cd zstd-$ZSTD +CFLAGS=-fPIC hide_output make -j$(nproc) VERBOSE=1 +hide_output make install + +cd .. +rm -rf zstd-$ZSTD diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7fd1808a1f0..90e30f85bd6 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -282,7 +282,7 @@ target | std | host | notes [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat `armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float -[`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ? | | Armv7-A for VxWorks +[`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks [`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3 [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat [`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat @@ -308,7 +308,7 @@ target | std | host | notes `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] `i686-uwp-windows-msvc` | ✓ | | [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] -[`i686-wrs-vxworks`](platform-support/vxworks.md) | ? | | [^x86_32-floats-return-ABI] +[`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 @@ -334,13 +334,13 @@ target | std | host | notes `powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | -[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ? | | -[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ? | | +[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | +[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) `powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD `powerpc-unknown-freebsd` | | | PowerPC FreeBSD `powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3 -`powerpc64-wrs-vxworks` | ? | | +[`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `powerpc64le-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3, Little Endian [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) @@ -383,7 +383,7 @@ target | std | host | notes `x86_64-uwp-windows-gnu` | ✓ | | `x86_64-uwp-windows-msvc` | ✓ | | [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support -[`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ? | | +[`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`xtensa-esp32-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32 diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index c0a818558e0..f1860346c8f 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -12,6 +12,7 @@ Target triplets available: - `i686-wrs-vxworks` - `armv7-wrs-vxworks-eabihf` - `powerpc-wrs-vxworks` +- `powerpc64-wrs-vxworks` - `powerpc-wrs-vxworks-spe` ## Target maintainers @@ -20,6 +21,12 @@ Target triplets available: ## Requirements +### OS version + +The minimum supported version is VxWorks 7. + +## Building + Rust for each target can be cross-compiled with its specific target vsb configuration. Std support is added but not yet fully tested. ## Building the target @@ -36,6 +43,7 @@ target = [ "i686-wrs-vxworks", "armv7-wrs-vxworks-eabihf", "powerpc-wrs-vxworks", + "powerpc64-wrs-vxworks", "powerpc-wrs-vxworks-spe", ] ``` diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index e4549796b3e..9c7a9f8467f 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -362,9 +362,10 @@ impl Options { } let color = config::parse_color(early_dcx, matches); - let config::JsonConfig { json_rendered, json_unused_externs, .. } = + let config::JsonConfig { json_rendered, json_unused_externs, json_color, .. } = config::parse_json(early_dcx, matches); - let error_format = config::parse_error_format(early_dcx, matches, color, json_rendered); + let error_format = + config::parse_error_format(early_dcx, matches, color, json_color, json_rendered); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); let codegen_options = CodegenOptions::build(early_dcx, matches); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 3e1271f198c..08a4a3f3fb2 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -138,8 +138,8 @@ pub(crate) fn new_dcx( false, ); let emitter: Box<DynEmitter> = match error_format { - ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .sm(source_map.map(|sm| sm as _)) @@ -150,7 +150,7 @@ pub(crate) fn new_dcx( .ui_testing(unstable_opts.ui_testing), ) } - ErrorOutputType::Json { pretty, json_rendered } => { + ErrorOutputType::Json { pretty, json_rendered, color_config } => { let source_map = source_map.unwrap_or_else(|| { Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) }); @@ -161,6 +161,7 @@ pub(crate) fn new_dcx( fallback_bundle, pretty, json_rendered, + color_config, ) .ui_testing(unstable_opts.ui_testing) .diagnostic_width(diagnostic_width) @@ -195,6 +196,7 @@ pub(crate) fn create_config( lint_cap, scrape_examples_options, expanded_args, + remap_path_prefix, .. }: RustdocOptions, RenderOptions { document_private, .. }: &RenderOptions, @@ -247,6 +249,7 @@ pub(crate) fn create_config( describe_lints, crate_name, test, + remap_path_prefix, ..Options::default() }; diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 41eb142dd1e..08d6a5a52b2 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -422,8 +422,8 @@ fn run_test( path_for_rustdoc.to_str().expect("target path must be valid unicode") } }); - if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format { - let (short, color_config) = kind.unzip(); + if let ErrorOutputType::HumanReadable(kind, color_config) = rustdoc_options.error_format { + let short = kind.short(); if short { compiler.arg("--error-format").arg("short"); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 97d97808b93..bb8d39aaf1d 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -924,6 +924,7 @@ pub(crate) struct TagIterator<'a, 'tcx> { data: &'a str, is_in_attribute_block: bool, extra: Option<&'a ExtraInfo<'tcx>>, + is_error: bool, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -950,13 +951,20 @@ struct Indices { impl<'a, 'tcx> TagIterator<'a, 'tcx> { pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self { - Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra } + Self { + inner: data.char_indices().peekable(), + data, + is_in_attribute_block: false, + extra, + is_error: false, + } } - fn emit_error(&self, err: impl Into<DiagMessage>) { + fn emit_error(&mut self, err: impl Into<DiagMessage>) { if let Some(extra) = self.extra { extra.error_invalid_codeblock_attr(err); } + self.is_error = true; } fn skip_separators(&mut self) -> Option<usize> { @@ -1154,6 +1162,9 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { type Item = LangStringToken<'a>; fn next(&mut self) -> Option<Self::Item> { + if self.is_error { + return None; + } let Some(start) = self.skip_separators() else { if self.is_in_attribute_block { self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); @@ -1342,14 +1353,15 @@ impl LangString { } }; - call(&mut TagIterator::new(string, extra)); + let mut tag_iter = TagIterator::new(string, extra); + call(&mut tag_iter); // ignore-foo overrides ignore if !ignores.is_empty() { data.ignore = Ignore::Some(ignores); } - data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags); + data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags) && !tag_iter.is_error; data } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 971120d4991..e490099a92e 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -61,7 +61,7 @@ fn test_lang_string_parse() { ..Default::default() }); // error - t(LangString { original: "{rust}".into(), rust: true, ..Default::default() }); + t(LangString { original: "{rust}".into(), rust: false, ..Default::default() }); t(LangString { original: "{.rust}".into(), rust: true, @@ -233,7 +233,7 @@ fn test_lang_string_parse() { ..Default::default() }); // error - t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() }); + t(LangString { original: "{class=first=second}".into(), rust: false, ..Default::default() }); // error t(LangString { original: "{class=first.second}".into(), @@ -261,7 +261,7 @@ fn test_lang_string_parse() { ..Default::default() }); // error - t(LangString { original: r#"{class=f"irst"}"#.into(), rust: true, ..Default::default() }); + t(LangString { original: r#"{class=f"irst"}"#.into(), rust: false, ..Default::default() }); } #[test] diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 67731f35496..62fbde6f225 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -390,13 +390,18 @@ function preLoadCss(cssUrl) { if (splitAt !== -1) { const implId = savedHash.slice(0, splitAt); const assocId = savedHash.slice(splitAt + 1); - const implElem = document.getElementById(implId); - if (implElem && implElem.parentElement.tagName === "SUMMARY" && - implElem.parentElement.parentElement.tagName === "DETAILS") { - onEachLazy(implElem.parentElement.parentElement.querySelectorAll( + const implElems = document.querySelectorAll( + `details > summary > section[id^="${implId}"]`, + ); + onEachLazy(implElems, implElem => { + const numbered = /^(.+?)-([0-9]+)$/.exec(implElem.id); + if (implElem.id !== implId && (!numbered || numbered[1] !== implId)) { + return false; + } + return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), item => { - const numbered = /([^-]+)-([0-9]+)/.exec(item.id); + const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { openParentDetails(item); item.scrollIntoView(); @@ -404,10 +409,11 @@ function preLoadCss(cssUrl) { setTimeout(() => { window.location.replace("#" + item.id); }, 0); + return true; } }, ); - } + }); } } } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index d1e2b9978f7..f807c3362c4 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -95,7 +95,14 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { } clean::ModuleItem(..) => { - if i.item_id.is_local() && i.visibility(self.tcx) != Some(Visibility::Public) { + if i.item_id.is_local() + && !is_item_reachable( + self.tcx, + self.is_json_output, + self.effective_visibilities, + i.item_id, + ) + { debug!("Stripper: stripping module {:?}", i.name); let old = mem::replace(&mut self.update_retained, false); let ret = strip_item(self.fold_item_recur(i)); diff --git a/src/tools/cargo b/src/tools/cargo -Subproject fa646583675d7c140482bd906145c71b7fb4fc2 +Subproject 0d8d22f83b066503f6b2b755925197e959e58b4 diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml index 7afdd068a99..ce07290d1e1 100644 --- a/src/tools/clippy/.cargo/config.toml +++ b/src/tools/clippy/.cargo/config.toml @@ -13,6 +13,13 @@ target-dir = "target" [unstable] binary-dep-depinfo = true +profile-rustflags = true [profile.dev] split-debuginfo = "unpacked" + +# Add back the containing directory of the packages we have to refer to using --manifest-path +[profile.dev.package.clippy_dev] +rustflags = ["--remap-path-prefix", "=clippy_dev"] +[profile.dev.package.lintcheck] +rustflags = ["--remap-path-prefix", "=lintcheck"] diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 06bf3b6fdbf..0a0538490cc 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -25,6 +25,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings concurrency: # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. @@ -47,25 +48,25 @@ jobs: # Run - name: Build - run: cargo build --tests --features deny-warnings,internal + run: cargo build --tests --features internal - name: Test - run: cargo test --features deny-warnings,internal + run: cargo test --features internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal + run: cargo test --features internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_utils - name: Test rustc_tools_util - run: cargo test --features deny-warnings + run: cargo test working-directory: rustc_tools_util - name: Test clippy_dev - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_dev - name: Test clippy-driver diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 1f4bec92918..10e18e84c89 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -11,6 +11,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings concurrency: # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. @@ -85,34 +86,34 @@ jobs: # Run - name: Build - run: cargo build --tests --features deny-warnings,internal + run: cargo build --tests --features internal - name: Test if: matrix.host == 'x86_64-unknown-linux-gnu' - run: cargo test --features deny-warnings,internal + run: cargo test --features internal - name: Test if: matrix.host != 'x86_64-unknown-linux-gnu' - run: cargo test --features deny-warnings,internal -- --skip dogfood + run: cargo test --features internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features deny-warnings,internal + run: cargo test --features internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_utils - name: Test clippy_config - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_config - name: Test rustc_tools_util - run: cargo test --features deny-warnings + run: cargo test working-directory: rustc_tools_util - name: Test clippy_dev - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_dev - name: Test clippy-driver diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 37f18a4c087..cf0a8bde202 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -16,6 +16,7 @@ on: env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings jobs: clippy_dev: @@ -28,7 +29,7 @@ jobs: # Run - name: Build - run: cargo build --features deny-warnings + run: cargo build working-directory: clippy_dev - name: Test update_lints @@ -38,6 +39,8 @@ jobs: run: cargo dev fmt --check - name: Test cargo dev new lint + env: + RUSTFLAGS: -A unused-imports run: | cargo dev new_lint --name new_early_pass --pass early cargo dev new_lint --name new_late_pass --pass late diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 6a5139b6dc0..3cbda0b3824 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -58,7 +58,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml + run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -86,7 +86,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml + run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 60c03b03d9b..fddc2fd994e 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5830,6 +5830,7 @@ Released 2018-09-13 [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use +[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push @@ -5998,6 +5999,7 @@ Released 2018-09-13 [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable +[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index bb4dc97e748..78409c7a09e 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -30,7 +30,7 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] -ui_test = "0.24" +ui_test = "0.25" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" @@ -51,7 +51,6 @@ tokio = { version = "1", features = ["io-util"] } rustc_tools_util = "0.3.0" [features] -deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] internal = ["clippy_lints/internal", "tempfile"] diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index fb717a2c166..e3d550b1466 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -364,7 +364,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat ## `await-holding-invalid-types` - +The list of types which may not be held across an await point. **Default Value:** `[]` @@ -668,6 +668,8 @@ crate. For example, `pub(crate)` items. ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` +**Default Value:** `current version` + --- **Affected lints:** * [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes) @@ -862,6 +864,8 @@ The maximum number of lines a function or method can have The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. By default there is no limit +**Default Value:** `target_pointer_width * 2` + --- **Affected lints:** * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index e1b2edc8a6f..d5b28e25323 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +itertools = "0.12" rustc-semver = "1.1" serde = { version = "1.0", features = ["derive"] } toml = "0.7.3" @@ -13,9 +14,6 @@ toml = "0.7.3" [dev-dependencies] walkdir = "2.3" -[features] -deny-warnings = [] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 63140a36875..4c2a8255d6b 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -123,7 +123,8 @@ macro_rules! define_Conf { $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? - ($name:ident: $ty:ty = $default:expr), + $(#[lints($($for_lints:ident),* $(,)?)])? + $name:ident: $ty:ty = $default:expr, )*) => { /// Clippy lint configuration pub struct Conf { @@ -201,29 +202,128 @@ macro_rules! define_Conf { } pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> { - let mut sorted = vec![ - $( - { - let deprecation_reason = wrap_option!($($dep)?); - - ClippyConfiguration::new( - stringify!($name), - default_text!(defaults::$name() $(, $default_text)?), - concat!($($doc, '\n',)*), - deprecation_reason, - ) - }, - )+ - ]; - sorted.sort_by(|a, b| a.name.cmp(&b.name)); - sorted + vec![$( + ClippyConfiguration { + name: stringify!($name).replace('_', "-"), + default: default_text!(defaults::$name() $(, $default_text)?), + lints: &[$($(stringify!($for_lints)),*)?], + doc: concat!($($doc, '\n',)*), + deprecation_reason: wrap_option!($($dep)?) + }, + )*] } }; } define_Conf! { - /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// Which crates to allow absolute paths from + #[lints(absolute_paths)] + absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(), + /// The maximum number of segments a path can have before being linted, anything above this will + /// be linted. + #[lints(absolute_paths)] + absolute_paths_max_segments: u64 = 2, + /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block + #[lints(undocumented_unsafe_blocks)] + accept_comment_above_attributes: bool = true, + /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block + #[lints(undocumented_unsafe_blocks)] + accept_comment_above_statement: bool = true, + /// Don't lint when comparing the result of a modulo operation to zero. + #[lints(modulo_arithmetic)] + allow_comparison_to_zero: bool = true, + /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` + #[lints(dbg_macro)] + allow_dbg_in_tests: bool = false, + /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` + #[lints(expect_used)] + allow_expect_in_tests: bool = false, + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + #[lints(uninlined_format_args)] + allow_mixed_uninlined_format_args: bool = true, + /// Whether to allow `r#""#` when `r""` can be used + #[lints(unnecessary_raw_string_hashes)] + allow_one_hash_in_raw_strings: bool = false, + /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` + #[lints(panic)] + allow_panic_in_tests: bool = false, + /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` + #[lints(print_stderr, print_stdout)] + allow_print_in_tests: bool = false, + /// Whether to allow module inception if it's not public. + #[lints(module_inception)] + allow_private_module_inception: bool = false, + /// List of trait paths to ignore when checking renamed function parameters. + /// + /// #### Example + /// + /// ```toml + /// allow-renamed-params-for = [ "std::convert::From" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` + /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. + #[lints(renamed_function_params)] + allow_renamed_params_for: Vec<String> = + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(), + /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` + #[lints(unwrap_used)] + allow_unwrap_in_tests: bool = false, + /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` + #[lints(useless_vec)] + allow_useless_vec_in_tests: bool = false, + /// Additional dotfiles (files or directories starting with a dot) to allow + #[lints(path_ends_with_ext)] + allowed_dotfiles: Vec<String> = Vec::default(), + /// A list of crate names to allow duplicates of + #[lints(multiple_crate_versions)] + allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(), + /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of + /// the list to indicate, that the configured values should be appended to the default + /// configuration of Clippy. By default, any configuration will replace the default value. + #[lints(min_ident_chars)] + allowed_idents_below_min_chars: FxHashSet<String> = + DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(), + /// List of prefixes to allow when determining whether an item's name ends with the module's name. + /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), + /// then don't emit a warning. + /// + /// #### Example + /// + /// ```toml + /// allowed-prefixes = [ "to", "from" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from` + /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included, + /// `TryInto` will also be included) + /// - Use `".."` as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value + #[lints(module_name_repetitions)] + allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect(), + /// The list of unicode scripts allowed to be used in the scope. + #[lints(disallowed_script_idents)] + allowed_scripts: Vec<String> = vec!["Latin".to_string()], + /// List of path segments allowed to have wildcard imports. /// + /// #### Example + /// + /// ```toml + /// allowed-wildcard-imports = [ "utils", "common" ] + /// ``` + /// + /// #### Noteworthy + /// + /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. + /// 2. Paths with any segment that containing the word 'prelude' + /// are already allowed by default. + #[lints(wildcard_imports)] + allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(), /// Suppress checking of the passed type names in all types of operations. /// /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. @@ -238,9 +338,8 @@ define_Conf! { /// /// A type, say `SomeType`, listed in this configuration has the same behavior of /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. - (arithmetic_side_effects_allowed: Vec<String> = <_>::default()), - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed: Vec<String> = <_>::default(), /// Suppress checking of the passed type pair names in binary operations like addition or /// multiplication. /// @@ -255,9 +354,8 @@ define_Conf! { /// ```toml /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] /// ``` - (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()), - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(), /// Suppress checking of the passed type names in unary operations like "negation" (`-`). /// /// #### Example @@ -265,298 +363,78 @@ define_Conf! { /// ```toml /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] /// ``` - (arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default()), - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT. - /// + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(), + /// The maximum allowed size for arrays on the stack + #[lints(large_const_arrays, large_stack_arrays)] + array_size_threshold: u64 = 512_000, /// Suppress lints whenever the suggested change would cause breakage for other crates. - (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH. - /// - /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` - #[default_text = ""] - (msrv: Msrv = Msrv::empty()), + #[lints( + box_collection, + enum_variant_names, + large_types_passed_by_value, + linkedlist, + needless_pass_by_ref_mut, + option_option, + rc_buffer, + rc_mutex, + redundant_allocation, + single_call_fn, + trivially_copy_pass_by_ref, + unnecessary_box_returns, + unnecessary_wraps, + unused_self, + upper_case_acronyms, + vec_box, + wrong_self_convention, + )] + avoid_breaking_exported_api: bool = true, + /// The list of types which may not be held across an await point. + #[lints(await_holding_invalid_type)] + await_holding_invalid_types: Vec<DisallowedPath> = Vec::new(), /// DEPRECATED LINT: BLACKLISTED_NAME. /// /// Use the Disallowed Names lint instead #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)] - (blacklisted_names: Vec<String> = Vec::new()), - /// Lint: COGNITIVE_COMPLEXITY. - /// + blacklisted_names: Vec<String> = Vec::new(), + /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. + #[lints(cargo_common_metadata)] + cargo_ignore_publish: bool = false, + /// Whether to also run the listed lints on private items. + #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] + check_private_items: bool = false, /// The maximum cognitive complexity a function can have - (cognitive_complexity_threshold: u64 = 25), - /// Lint: EXCESSIVE_NESTING. - /// - /// The maximum amount of nesting a block can reside in - (excessive_nesting_threshold: u64 = 0), + #[lints(cognitive_complexity)] + cognitive_complexity_threshold: u64 = 25, /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. /// /// Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] - (cyclomatic_complexity_threshold: u64 = 25), - /// Lint: DISALLOWED_NAMES. - /// + cyclomatic_complexity_threshold: u64 = 25, + /// The list of disallowed macros, written as fully qualified paths. + #[lints(disallowed_macros)] + disallowed_macros: Vec<DisallowedPath> = Vec::new(), + /// The list of disallowed methods, written as fully qualified paths. + #[lints(disallowed_methods)] + disallowed_methods: Vec<DisallowedPath> = Vec::new(), /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. - (disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()), - /// Lint: SEMICOLON_INSIDE_BLOCK. - /// - /// Whether to lint only if it's multiline. - (semicolon_inside_block_ignore_singleline: bool = false), - /// Lint: SEMICOLON_OUTSIDE_BLOCK. - /// - /// Whether to lint only if it's singleline. - (semicolon_outside_block_ignore_multiline: bool = false), - /// Lint: DOC_MARKDOWN. - /// + #[lints(disallowed_names)] + disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), + /// The list of disallowed types, written as fully qualified paths. + #[lints(disallowed_types)] + disallowed_types: Vec<DisallowedPath> = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. For example: /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. - (doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), - /// Lint: TOO_MANY_ARGUMENTS. - /// - /// The maximum number of argument a function or method can have - (too_many_arguments_threshold: u64 = 7), - /// Lint: TYPE_COMPLEXITY. - /// - /// The maximum complexity a type can have - (type_complexity_threshold: u64 = 250), - /// Lint: MANY_SINGLE_CHAR_NAMES. - /// - /// The maximum number of single char bindings a scope may have - (single_char_binding_names_threshold: u64 = 4), - /// Lint: BOXED_LOCAL, USELESS_VEC. - /// - /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap - (too_large_for_stack: u64 = 200), - /// Lint: ENUM_VARIANT_NAMES. - /// - /// The minimum number of enum variants for the lints about variant names to trigger - (enum_variant_name_threshold: u64 = 3), - /// Lint: STRUCT_FIELD_NAMES. - /// - /// The minimum number of struct fields for the lints about field names to trigger - (struct_field_name_threshold: u64 = 3), - /// Lint: LARGE_ENUM_VARIANT. - /// - /// The maximum size of an enum's variant to avoid box suggestion - (enum_variant_size_threshold: u64 = 200), - /// Lint: VERBOSE_BIT_MASK. - /// - /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' - (verbose_bit_mask_threshold: u64 = 1), - /// Lint: DECIMAL_LITERAL_REPRESENTATION. - /// - /// The lower bound for linting decimal literals - (literal_representation_threshold: u64 = 16384), - /// Lint: TRIVIALLY_COPY_PASS_BY_REF. - /// - /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by - /// reference. By default there is no limit - #[default_text = ""] - (trivial_copy_size_limit: Option<u64> = None), - /// Lint: LARGE_TYPES_PASSED_BY_VALUE. - /// - /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. - (pass_by_value_size_limit: u64 = 256), - /// Lint: TOO_MANY_LINES. - /// - /// The maximum number of lines a function or method can have - (too_many_lines_threshold: u64 = 100), - /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. - /// - /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), - /// Lint: LARGE_STACK_FRAMES. - /// - /// The maximum allowed stack size for functions in bytes - (stack_size_threshold: u64 = 512_000), - /// Lint: VEC_BOX. - /// - /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed - (vec_box_size_threshold: u64 = 4096), - /// Lint: TYPE_REPETITION_IN_BOUNDS. - /// - /// The maximum number of bounds a trait can have to be linted - (max_trait_bounds: u64 = 3), - /// Lint: STRUCT_EXCESSIVE_BOOLS. - /// - /// The maximum number of bool fields a struct can have - (max_struct_bools: u64 = 3), - /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. - /// - /// The maximum number of bool parameters a function can have - (max_fn_params_bools: u64 = 3), - /// Lint: WILDCARD_IMPORTS. - /// - /// Whether to allow certain wildcard imports (prelude, super in tests). - (warn_on_all_wildcard_imports: bool = false), - /// Lint: DISALLOWED_MACROS. - /// - /// The list of disallowed macros, written as fully qualified paths. - (disallowed_macros: Vec<DisallowedPath> = Vec::new()), - /// Lint: DISALLOWED_METHODS. - /// - /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec<DisallowedPath> = Vec::new()), - /// Lint: DISALLOWED_TYPES. - /// - /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec<DisallowedPath> = Vec::new()), - /// Lint: UNREADABLE_LITERAL. - /// - /// Should the fraction of a decimal be linted to include separators. - (unreadable_literal_lint_fractions: bool = true), - /// Lint: UPPER_CASE_ACRONYMS. - /// - /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other - (upper_case_acronyms_aggressive: bool = false), - /// Lint: MANUAL_LET_ELSE. - /// - /// Whether the matches should be considered by the lint, and whether there should - /// be filtering for common types. - (matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes), - /// Lint: CARGO_COMMON_METADATA. - /// - /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish: bool = false), - /// Lint: NONSTANDARD_MACRO_BRACES. - /// - /// Enforce the named macros always use the braces specified. - /// - /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro - /// could be used with a full path two `MacroMatcher`s have to be added one with the full path - /// `crate_name::macro_name` and one with just the macro name. - (standard_macro_braces: Vec<MacroMatcher> = Vec::new()), - /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. - /// - /// The list of imports to always rename, a fully qualified path followed by the rename. - (enforced_import_renames: Vec<Rename> = Vec::new()), - /// Lint: DISALLOWED_SCRIPT_IDENTS. - /// - /// The list of unicode scripts allowed to be used in the scope. - (allowed_scripts: Vec<String> = vec!["Latin".to_string()]), - /// Lint: NON_SEND_FIELDS_IN_SEND_TY. - /// + #[lints(doc_markdown)] + doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(), /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. - (enable_raw_pointer_heuristic_for_send: bool = true), - /// Lint: INDEX_REFUTABLE_SLICE. - /// - /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in - /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. - /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. - (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE. - (await_holding_invalid_types: Vec<DisallowedPath> = Vec::new()), - /// Lint: LARGE_INCLUDE_FILE. - /// - /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes - (max_include_file_size: u64 = 1_000_000), - /// Lint: EXPECT_USED. - /// - /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` - (allow_expect_in_tests: bool = false), - /// Lint: UNWRAP_USED. - /// - /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` - (allow_unwrap_in_tests: bool = false), - /// Lint: PANIC. - /// - /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` - (allow_panic_in_tests: bool = false), - /// Lint: DBG_MACRO. - /// - /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` - (allow_dbg_in_tests: bool = false), - /// Lint: PRINT_STDOUT, PRINT_STDERR. - /// - /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` - (allow_print_in_tests: bool = false), - /// Lint: USELESS_VEC. - /// - /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` - (allow_useless_vec_in_tests: bool = false), - /// Lint: RESULT_LARGE_ERR. - /// - /// The maximum size of the `Err`-variant in a `Result` returned from a function - (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST. - /// - /// A list of paths to types that should be treated as if they do not contain interior mutability - (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])), - /// Lint: UNINLINED_FORMAT_ARGS. - /// - /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` - (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING. - /// - /// Whether to suppress a restriction lint in constant code. In same - /// cases the restructured operation might not be unavoidable, as the - /// suggested counterparts are unavailable in constant code. This - /// configuration will cause restriction lints to trigger even - /// if no suggestion can be made. - (suppress_restriction_lint_in_const: bool = false), - /// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS. - /// - /// Whether to **only** check for missing documentation in items visible within the current - /// crate. For example, `pub(crate)` items. - (missing_docs_in_crate_items: bool = false), - /// Lint: LARGE_FUTURES. - /// - /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint - (future_size_threshold: u64 = 16 * 1024), - /// Lint: UNNECESSARY_BOX_RETURNS. - /// - /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint - (unnecessary_box_size: u64 = 128), - /// Lint: MODULE_INCEPTION. - /// - /// Whether to allow module inception if it's not public. - (allow_private_module_inception: bool = false), - /// Lint: MIN_IDENT_CHARS. - /// - /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of - /// the list to indicate, that the configured values should be appended to the default - /// configuration of Clippy. By default, any configuration will replace the default value. - (allowed_idents_below_min_chars: FxHashSet<String> = - DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()), - /// Lint: MIN_IDENT_CHARS. - /// - /// Minimum chars an ident can have, anything below or equal to this will be linted. - (min_ident_chars_threshold: u64 = 1), - /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. - /// - /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block - (accept_comment_above_statement: bool = true), - /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. - /// - /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block - (accept_comment_above_attributes: bool = true), - /// Lint: UNNECESSARY_RAW_STRING_HASHES. - /// - /// Whether to allow `r#""#` when `r""` can be used - (allow_one_hash_in_raw_strings: bool = false), - /// Lint: ABSOLUTE_PATHS. - /// - /// The maximum number of segments a path can have before being linted, anything above this will - /// be linted. - (absolute_paths_max_segments: u64 = 2), - /// Lint: ABSOLUTE_PATHS. - /// - /// Which crates to allow absolute paths from - (absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default()), - /// Lint: PATH_ENDS_WITH_EXT. - /// - /// Additional dotfiles (files or directories starting with a dot) to allow - (allowed_dotfiles: Vec<String> = Vec::default()), - /// Lint: MULTIPLE_CRATE_VERSIONS. - /// - /// A list of crate names to allow duplicates of - (allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default()), - /// Lint: EXPLICIT_ITER_LOOP. - /// + #[lints(non_send_fields_in_send_ty)] + enable_raw_pointer_heuristic_for_send: bool = true, /// Whether to recommend using implicit into iter for reborrowed values. /// /// #### Example @@ -574,77 +452,195 @@ define_Conf! { /// for _ in &*rmvec {} /// for _ in &mut *rmvec {} /// ``` - (enforce_iter_loop_reborrow: bool = false), - /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC. - /// - /// Whether to also run the listed lints on private items. - (check_private_items: bool = false), - /// Lint: PUB_UNDERSCORE_FIELDS. - /// + #[lints(explicit_iter_loop)] + enforce_iter_loop_reborrow: bool = false, + /// The list of imports to always rename, a fully qualified path followed by the rename. + #[lints(missing_enforced_import_renames)] + enforced_import_renames: Vec<Rename> = Vec::new(), + /// The minimum number of enum variants for the lints about variant names to trigger + #[lints(enum_variant_names)] + enum_variant_name_threshold: u64 = 3, + /// The maximum size of an enum's variant to avoid box suggestion + #[lints(large_enum_variant)] + enum_variant_size_threshold: u64 = 200, + /// The maximum amount of nesting a block can reside in + #[lints(excessive_nesting)] + excessive_nesting_threshold: u64 = 0, + /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint + #[lints(large_futures)] + future_size_threshold: u64 = 16 * 1024, + /// A list of paths to types that should be treated as if they do not contain interior mutability + #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] + ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()]), + /// The maximum size of the `Err`-variant in a `Result` returned from a function + #[lints(result_large_err)] + large_error_threshold: u64 = 128, + /// The lower bound for linting decimal literals + #[lints(decimal_literal_representation)] + literal_representation_threshold: u64 = 16384, + /// Whether the matches should be considered by the lint, and whether there should + /// be filtering for common types. + #[lints(manual_let_else)] + matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, + /// The maximum number of bool parameters a function can have + #[lints(fn_params_excessive_bools)] + max_fn_params_bools: u64 = 3, + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + #[lints(large_include_file)] + max_include_file_size: u64 = 1_000_000, + /// The maximum number of bool fields a struct can have + #[lints(struct_excessive_bools)] + max_struct_bools: u64 = 3, + /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in + /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. + /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. + #[lints(index_refutable_slice)] + max_suggested_slice_pattern_length: u64 = 3, + /// The maximum number of bounds a trait can have to be linted + #[lints(type_repetition_in_bounds)] + max_trait_bounds: u64 = 3, + /// Minimum chars an ident can have, anything below or equal to this will be linted. + #[lints(min_ident_chars)] + min_ident_chars_threshold: u64 = 1, + /// Whether to **only** check for missing documentation in items visible within the current + /// crate. For example, `pub(crate)` items. + #[lints(missing_docs_in_private_items)] + missing_docs_in_crate_items: bool = false, + /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` + #[default_text = "current version"] + #[lints( + allow_attributes, + allow_attributes_without_reason, + almost_complete_range, + approx_constant, + assigning_clones, + borrow_as_ptr, + cast_abs_to_unsigned, + checked_conversions, + cloned_instead_of_copied, + collapsible_match, + collapsible_str_replace, + deprecated_cfg_attr, + derivable_impls, + err_expect, + filter_map_next, + from_over_into, + if_then_some_else_none, + index_refutable_slice, + iter_kv_map, + legacy_numeric_constants, + manual_bits, + manual_c_str_literals, + manual_clamp, + manual_hash_one, + manual_is_ascii_check, + manual_let_else, + manual_non_exhaustive, + manual_pattern_char_comparison, + manual_range_contains, + manual_rem_euclid, + manual_retain, + manual_split_once, + manual_str_repeat, + manual_strip, + manual_try_fold, + map_clone, + map_unwrap_or, + match_like_matches_macro, + mem_replace_with_default, + missing_const_for_fn, + needless_borrow, + option_as_ref_deref, + option_map_unwrap_or, + ptr_as_ptr, + redundant_field_names, + redundant_static_lifetimes, + seek_from_current, + seek_rewind, + transmute_ptr_to_ref, + tuple_array_conversions, + type_repetition_in_bounds, + unchecked_duration_subtraction, + uninlined_format_args, + unnecessary_lazy_evaluations, + unnested_or_patterns, + use_self, + )] + msrv: Msrv = Msrv::empty(), + /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. + #[lints(large_types_passed_by_value)] + pass_by_value_size_limit: u64 = 256, /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". - (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported), - /// Lint: MODULO_ARITHMETIC. - /// - /// Don't lint when comparing the result of a modulo operation to zero. - (allow_comparison_to_zero: bool = true), - /// Lint: WILDCARD_IMPORTS. - /// - /// List of path segments allowed to have wildcard imports. - /// - /// #### Example - /// - /// ```toml - /// allowed-wildcard-imports = [ "utils", "common" ] - /// ``` - /// - /// #### Noteworthy - /// - /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. - /// 2. Paths with any segment that containing the word 'prelude' - /// are already allowed by default. - (allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()), - /// Lint: MODULE_NAME_REPETITIONS. - /// - /// List of prefixes to allow when determining whether an item's name ends with the module's name. - /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), - /// then don't emit a warning. - /// - /// #### Example - /// - /// ```toml - /// allowed-prefixes = [ "to", "from" ] - /// ``` - /// - /// #### Noteworthy - /// - /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from` - /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included, - /// `TryInto` will also be included) - /// - Use `".."` as part of the list to indicate that the configured values should be appended to the - /// default configuration of Clippy. By default, any configuration will replace the default value - (allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()), - /// Lint: RENAMED_FUNCTION_PARAMS. - /// - /// List of trait paths to ignore when checking renamed function parameters. - /// - /// #### Example - /// - /// ```toml - /// allow-renamed-params-for = [ "std::convert::From" ] - /// ``` - /// - /// #### Noteworthy - /// - /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` - /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the - /// default configuration of Clippy. By default, any configuration will replace the default value. - (allow_renamed_params_for: Vec<String> = - DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), - /// Lint: MACRO_METAVARS_IN_UNSAFE. + #[lints(pub_underscore_fields)] + pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether to lint only if it's multiline. + #[lints(semicolon_inside_block)] + semicolon_inside_block_ignore_singleline: bool = false, + /// Whether to lint only if it's singleline. + #[lints(semicolon_outside_block)] + semicolon_outside_block_ignore_multiline: bool = false, + /// The maximum number of single char bindings a scope may have + #[lints(many_single_char_names)] + single_char_binding_names_threshold: u64 = 4, + /// The maximum allowed stack size for functions in bytes + #[lints(large_stack_frames)] + stack_size_threshold: u64 = 512_000, + /// Enforce the named macros always use the braces specified. /// + /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro + /// could be used with a full path two `MacroMatcher`s have to be added one with the full path + /// `crate_name::macro_name` and one with just the macro name. + #[lints(nonstandard_macro_braces)] + standard_macro_braces: Vec<MacroMatcher> = Vec::new(), + /// The minimum number of struct fields for the lints about field names to trigger + #[lints(struct_field_names)] + struct_field_name_threshold: u64 = 3, + /// Whether to suppress a restriction lint in constant code. In same + /// cases the restructured operation might not be unavoidable, as the + /// suggested counterparts are unavailable in constant code. This + /// configuration will cause restriction lints to trigger even + /// if no suggestion can be made. + #[lints(indexing_slicing)] + suppress_restriction_lint_in_const: bool = false, + /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + #[lints(boxed_local, useless_vec)] + too_large_for_stack: u64 = 200, + /// The maximum number of argument a function or method can have + #[lints(too_many_arguments)] + too_many_arguments_threshold: u64 = 7, + /// The maximum number of lines a function or method can have + #[lints(too_many_lines)] + too_many_lines_threshold: u64 = 100, + /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by + /// reference. By default there is no limit + #[default_text = "target_pointer_width * 2"] + #[lints(trivially_copy_pass_by_ref)] + trivial_copy_size_limit: Option<u64> = None, + /// The maximum complexity a type can have + #[lints(type_complexity)] + type_complexity_threshold: u64 = 250, + /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint + #[lints(unnecessary_box_returns)] + unnecessary_box_size: u64 = 128, + /// Should the fraction of a decimal be linted to include separators. + #[lints(unreadable_literal)] + unreadable_literal_lint_fractions: bool = true, + /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other + #[lints(upper_case_acronyms)] + upper_case_acronyms_aggressive: bool = false, + /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed + #[lints(vec_box)] + vec_box_size_threshold: u64 = 4096, + /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + #[lints(verbose_bit_mask)] + verbose_bit_mask_threshold: u64 = 1, + /// Whether to allow certain wildcard imports (prelude, super in tests). + #[lints(wildcard_imports)] + warn_on_all_wildcard_imports: bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. - (warn_unsafe_macro_metavars_in_private_macros: bool = false), + #[lints(macro_metavars_in_unsafe)] + warn_unsafe_macro_metavars_in_private_macros: bool = false, } /// Search for the configuration file. diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index ff7fa7241cb..d2246f12029 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -1,5 +1,4 @@ -#![feature(rustc_private, let_chains)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(rustc_private, array_windows, let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/clippy_config/src/metadata.rs b/src/tools/clippy/clippy_config/src/metadata.rs index 400887185e8..7cbd92a9b71 100644 --- a/src/tools/clippy/clippy_config/src/metadata.rs +++ b/src/tools/clippy/clippy_config/src/metadata.rs @@ -1,11 +1,12 @@ -use std::fmt::{self, Write}; +use itertools::Itertools; +use std::fmt; #[derive(Debug, Clone, Default)] pub struct ClippyConfiguration { pub name: String, pub default: String, - pub lints: Vec<String>, - pub doc: String, + pub lints: &'static [&'static str], + pub doc: &'static str, pub deprecation_reason: Option<&'static str>, } @@ -13,61 +14,23 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "- `{}`: {}", self.name, self.doc)?; if !self.default.is_empty() { - write!(f, " (default: `{}`)", self.default)?; + write!(f, "\n\n (default: `{}`)", self.default)?; } Ok(()) } } impl ClippyConfiguration { - pub fn new( - name: &'static str, - default: String, - doc_comment: &'static str, - deprecation_reason: Option<&'static str>, - ) -> Self { - let (mut lints, doc) = parse_config_field_doc(doc_comment) - .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); - - lints.sort(); - - Self { - name: to_kebab(name), - lints, - doc, - default, - deprecation_reason, - } - } - pub fn to_markdown_paragraph(&self) -> String { - let mut out = format!( - "## `{}`\n{}\n\n", + format!( + "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n", self.name, - self.doc - .lines() - .map(|line| line.strip_prefix(" ").unwrap_or(line)) - .collect::<Vec<_>>() - .join("\n"), - ); - - if !self.default.is_empty() { - write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap(); - } - - write!( - out, - "---\n**Affected lints:**\n{}\n\n", - self.lints - .iter() - .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) - .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) - .collect::<Vec<_>>() - .join("\n"), + self.doc.lines().map(|x| x.strip_prefix(' ').unwrap_or(x)).join("\n"), + self.default, + self.lints.iter().format_with("\n", |name, f| f(&format_args!( + "* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})" + ))), ) - .unwrap(); - - out } pub fn to_markdown_link(&self) -> String { @@ -75,47 +38,3 @@ impl ClippyConfiguration { format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name) } } - -/// This parses the field documentation of the config struct. -/// -/// ```rust, ignore -/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") -/// ``` -/// -/// Would yield: -/// ```rust, ignore -/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") -/// ``` -fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> { - const DOC_START: &str = " Lint: "; - if doc_comment.starts_with(DOC_START) - && let Some(split_pos) = doc_comment.find('.') - { - let mut doc_comment = doc_comment.to_string(); - let mut documentation = doc_comment.split_off(split_pos); - - // Extract lints - doc_comment.make_ascii_lowercase(); - let lints: Vec<String> = doc_comment - .split_off(DOC_START.len()) - .lines() - .next() - .unwrap() - .split(", ") - .map(str::to_string) - .collect(); - - // Format documentation correctly - // split off leading `.` from lint name list and indent for correct formatting - documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); - - Some((lints, documentation)) - } else { - None - } -} - -/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` -fn to_kebab(config_name: &str) -> String { - config_name.replace('_', "-") -} diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 4104e7d94f1..a5d72c3a559 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -13,9 +13,6 @@ opener = "0.6" shell-escape = "0.1" walkdir = "2.3" -[features] -deny-warnings = [] - [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 25623144181..5fc4365c6e7 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,30 +1,65 @@ use crate::clippy_project_root; use itertools::Itertools; +use rustc_lexer::{tokenize, TokenKind}; use shell_escape::escape; use std::ffi::{OsStr, OsString}; -use std::path::Path; +use std::ops::ControlFlow; +use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::{fs, io}; use walkdir::WalkDir; -#[derive(Debug)] -pub enum CliError { +pub enum Error { CommandFailed(String, String), - IoError(io::Error), + Io(io::Error), RustfmtNotInstalled, - WalkDirError(walkdir::Error), + WalkDir(walkdir::Error), IntellijSetupActive, + Parse(PathBuf, usize, String), + CheckFailed, } -impl From<io::Error> for CliError { +impl From<io::Error> for Error { fn from(error: io::Error) -> Self { - Self::IoError(error) + Self::Io(error) } } -impl From<walkdir::Error> for CliError { +impl From<walkdir::Error> for Error { fn from(error: walkdir::Error) -> Self { - Self::WalkDirError(error) + Self::WalkDir(error) + } +} + +impl Error { + fn display(&self) { + match self { + Self::CheckFailed => { + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + }, + Self::CommandFailed(command, stderr) => { + eprintln!("error: command `{command}` failed!\nstderr: {stderr}"); + }, + Self::Io(err) => { + eprintln!("error: {err}"); + }, + Self::RustfmtNotInstalled => { + eprintln!("error: rustfmt nightly is not installed."); + }, + Self::WalkDir(err) => { + eprintln!("error: {err}"); + }, + Self::IntellijSetupActive => { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ + Not formatting because that would format the local repo as well!\n\ + Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." + ); + }, + Self::Parse(path, line, msg) => { + eprintln!("error parsing `{}:{line}`: {msg}", path.display()); + }, + } } } @@ -34,75 +69,244 @@ struct FmtContext { rustfmt_path: String, } -// the "main" function of cargo dev fmt -pub fn run(check: bool, verbose: bool) { - fn try_run(context: &FmtContext) -> Result<bool, CliError> { - let mut success = true; - - let project_root = clippy_project_root(); - - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to - // format because rustfmt would also format the entire rustc repo as it is a local - // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) - .expect("Failed to read clippy Cargo.toml") - .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") - { - return Err(CliError::IntellijSetupActive); - } - - rustfmt_test(context)?; +struct ClippyConf<'a> { + name: &'a str, + attrs: &'a str, + lints: Vec<&'a str>, + field: &'a str, +} - success &= cargo_fmt(context, project_root.as_path())?; - success &= cargo_fmt(context, &project_root.join("clippy_dev"))?; - success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - success &= cargo_fmt(context, &project_root.join("lintcheck"))?; +fn offset_to_line(text: &str, offset: usize) -> usize { + match text.split('\n').try_fold((1usize, 0usize), |(line, pos), s| { + let pos = pos + s.len() + 1; + if pos > offset { + ControlFlow::Break(line) + } else { + ControlFlow::Continue((line + 1, pos)) + } + }) { + ControlFlow::Break(x) | ControlFlow::Continue((x, _)) => x, + } +} - let chunks = WalkDir::new(project_root.join("tests")) - .into_iter() - .filter_map(|entry| { - let entry = entry.expect("failed to find tests"); - let path = entry.path(); +/// Formats the configuration list in `clippy_config/src/conf.rs` +#[expect(clippy::too_many_lines)] +fn fmt_conf(check: bool) -> Result<(), Error> { + #[derive(Clone, Copy)] + enum State { + Start, + Docs, + Pound, + OpenBracket, + Attr(u32), + Lints, + EndLints, + Field, + } - if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { - None - } else { - Some(entry.into_path().into_os_string()) - } - }) - .chunks(250); + let path: PathBuf = [ + clippy_project_root().as_path(), + "clippy_config".as_ref(), + "src".as_ref(), + "conf.rs".as_ref(), + ] + .into_iter() + .collect(); + let text = fs::read_to_string(&path)?; - for chunk in &chunks { - success &= rustfmt(context, chunk)?; - } + let (pre, conf) = text + .split_once("define_Conf! {\n") + .expect("can't find config definition"); + let (conf, post) = conf.split_once("\n}\n").expect("can't find config definition"); + let conf_offset = pre.len() + 15; - Ok(success) - } + let mut pos = 0u32; + let mut attrs_start = 0; + let mut attrs_end = 0; + let mut field_start = 0; + let mut lints = Vec::new(); + let mut name = ""; + let mut fields = Vec::new(); + let mut state = State::Start; - fn output_err(err: CliError) { - match err { - CliError::CommandFailed(command, stderr) => { - eprintln!("error: A command failed! `{command}`\nstderr: {stderr}"); + for (i, t) in tokenize(conf) + .map(|x| { + let start = pos; + pos += x.len; + (start as usize, x) + }) + .filter(|(_, t)| !matches!(t.kind, TokenKind::Whitespace)) + { + match (state, t.kind) { + (State::Start, TokenKind::LineComment { doc_style: Some(_) }) => { + attrs_start = i; + attrs_end = i + t.len as usize; + state = State::Docs; }, - CliError::IoError(err) => { - eprintln!("error: {err}"); + (State::Start, TokenKind::Pound) => { + attrs_start = i; + attrs_end = i; + state = State::Pound; }, - CliError::RustfmtNotInstalled => { - eprintln!("error: rustfmt nightly is not installed."); + (State::Docs, TokenKind::LineComment { doc_style: Some(_) }) => attrs_end = i + t.len as usize, + (State::Docs, TokenKind::Pound) => state = State::Pound, + (State::Pound, TokenKind::OpenBracket) => state = State::OpenBracket, + (State::OpenBracket, TokenKind::Ident) => { + state = if conf[i..i + t.len as usize] == *"lints" { + State::Lints + } else { + State::Attr(0) + }; }, - CliError::WalkDirError(err) => { - eprintln!("error: {err}"); + (State::Attr(0), TokenKind::CloseBracket) => { + attrs_end = i + 1; + state = State::Docs; }, - CliError::IntellijSetupActive => { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`. -Not formatting because that would format the local repo as well! -Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." - ); + (State::Attr(x), TokenKind::OpenParen | TokenKind::OpenBracket | TokenKind::OpenBrace) => { + state = State::Attr(x + 1); + }, + (State::Attr(x), TokenKind::CloseParen | TokenKind::CloseBracket | TokenKind::CloseBrace) => { + state = State::Attr(x - 1); + }, + (State::Lints, TokenKind::Ident) => lints.push(&conf[i..i + t.len as usize]), + (State::Lints, TokenKind::CloseBracket) => state = State::EndLints, + (State::EndLints | State::Docs, TokenKind::Ident) => { + field_start = i; + name = &conf[i..i + t.len as usize]; + state = State::Field; + }, + (State::Field, TokenKind::LineComment { doc_style: Some(_) }) => { + #[expect(clippy::drain_collect)] + fields.push(ClippyConf { + name, + lints: lints.drain(..).collect(), + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..i].trim_end(), + }); + attrs_start = i; + attrs_end = i + t.len as usize; + state = State::Docs; + }, + (State::Field, TokenKind::Pound) => { + #[expect(clippy::drain_collect)] + fields.push(ClippyConf { + name, + lints: lints.drain(..).collect(), + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..i].trim_end(), + }); + attrs_start = i; + attrs_end = i; + state = State::Pound; + }, + (State::Field | State::Attr(_), _) + | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, + _ => { + return Err(Error::Parse( + path, + offset_to_line(&text, conf_offset + i), + format!("unexpected token `{}`", &conf[i..i + t.len as usize]), + )); }, } } + if !matches!(state, State::Field) { + return Err(Error::Parse( + path, + offset_to_line(&text, conf_offset + conf.len()), + "incomplete field".into(), + )); + } + fields.push(ClippyConf { + name, + lints, + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..].trim_end(), + }); + + for field in &mut fields { + field.lints.sort_unstable(); + } + fields.sort_by_key(|x| x.name); + + let new_text = format!( + "{pre}define_Conf! {{\n{}}}\n{post}", + fields.iter().format_with("", |field, f| { + if field.lints.is_empty() { + f(&format_args!(" {}\n {}\n", field.attrs, field.field)) + } else if field.lints.iter().map(|x| x.len() + 2).sum::<usize>() < 120 - 14 { + f(&format_args!( + " {}\n #[lints({})]\n {}\n", + field.attrs, + field.lints.iter().join(", "), + field.field, + )) + } else { + f(&format_args!( + " {}\n #[lints({}\n )]\n {}\n", + field.attrs, + field + .lints + .iter() + .format_with("", |x, f| f(&format_args!("\n {x},"))), + field.field, + )) + } + }) + ); + + if text != new_text { + if check { + return Err(Error::CheckFailed); + } + fs::write(&path, new_text.as_bytes())?; + } + Ok(()) +} + +fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { + let project_root = clippy_project_root(); + + // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to + // format because rustfmt would also format the entire rustc repo as it is a local + // dependency + if fs::read_to_string(project_root.join("Cargo.toml")) + .expect("Failed to read clippy Cargo.toml") + .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + { + return Err(Error::IntellijSetupActive); + } + + check_for_rustfmt(context)?; + + cargo_fmt(context, project_root.as_path())?; + cargo_fmt(context, &project_root.join("clippy_dev"))?; + cargo_fmt(context, &project_root.join("rustc_tools_util"))?; + cargo_fmt(context, &project_root.join("lintcheck"))?; + + let chunks = WalkDir::new(project_root.join("tests")) + .into_iter() + .filter_map(|entry| { + let entry = entry.expect("failed to find tests"); + let path = entry.path(); + + if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { + None + } else { + Some(entry.into_path().into_os_string()) + } + }) + .chunks(250); + + for chunk in &chunks { + rustfmt(context, chunk)?; + } + Ok(()) +} + +// the "main" function of cargo dev fmt +pub fn run(check: bool, verbose: bool) { let output = Command::new("rustup") .args(["which", "rustfmt"]) .stderr(Stdio::inherit()) @@ -120,21 +324,10 @@ Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." verbose, rustfmt_path, }; - let result = try_run(&context); - let code = match result { - Ok(true) => 0, - Ok(false) => { - eprintln!(); - eprintln!("Formatting check failed."); - eprintln!("Run `cargo dev fmt` to update formatting."); - 1 - }, - Err(err) => { - output_err(err); - 1 - }, - }; - process::exit(code); + if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) { + e.display(); + process::exit(1); + } } fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String { @@ -148,12 +341,12 @@ fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[imp ) } -fn exec( +fn exec_fmt_command( context: &FmtContext, program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>], -) -> Result<bool, CliError> { +) -> Result<(), Error> { if context.verbose { println!("{}", format_command(&program, &dir, args)); } @@ -166,28 +359,28 @@ fn exec( .unwrap(); let success = output.status.success(); - if !context.check && !success { - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - return Err(CliError::CommandFailed( - format_command(&program, &dir, args), - String::from(stderr), - )); + match (context.check, success) { + (_, true) => Ok(()), + (true, false) => Err(Error::CheckFailed), + (false, false) => { + let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); + Err(Error::CommandFailed( + format_command(&program, &dir, args), + String::from(stderr), + )) + }, } - - Ok(success) } -fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> { +fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> { let mut args = vec!["fmt", "--all"]; if context.check { args.push("--check"); } - let success = exec(context, "cargo", path, &args)?; - - Ok(success) + exec_fmt_command(context, "cargo", path, &args) } -fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { +fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> { let program = "rustfmt"; let dir = std::env::current_dir()?; let args = &["--version"]; @@ -204,23 +397,20 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { .unwrap_or("") .starts_with("error: 'rustfmt' is not installed") { - Err(CliError::RustfmtNotInstalled) + Err(Error::RustfmtNotInstalled) } else { - Err(CliError::CommandFailed( + Err(Error::CommandFailed( format_command(program, &dir, args), std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), )) } } -fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> { +fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<(), Error> { let mut args = Vec::new(); if context.check { args.push(OsString::from("--check")); } args.extend(paths); - - let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?; - - Ok(success) + exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 3aa43dbe23e..ad385d5fbd2 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,6 +1,5 @@ #![feature(let_chains)] #![feature(rustc_private)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 366b52b25df..755b04b0b23 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -74,7 +73,7 @@ fn main() { new_name, uplift, } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), } } @@ -223,7 +222,7 @@ enum DevCommand { name: String, #[arg(long, short)] /// The reason for deprecation - reason: Option<String>, + reason: String, }, } diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index 4a4261d1a1e..19560b31fd3 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -1,10 +1,14 @@ -use std::ffi::OsStr; -use std::num::ParseIntError; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; use std::{env, thread}; +#[cfg(windows)] +const PYTHON: &str = "python"; + +#[cfg(not(windows))] +const PYTHON: &str = "python3"; + /// # Panics /// /// Panics if the python commands could not be spawned @@ -25,7 +29,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! { } if let Some(url) = url.take() { thread::spawn(move || { - Command::new("python3") + Command::new(PYTHON) .arg("-m") .arg("http.server") .arg(port.to_string()) @@ -58,8 +62,3 @@ fn mtime(path: impl AsRef<Path>) -> SystemTime { .unwrap_or(SystemTime::UNIX_EPOCH) } } - -#[allow(clippy::missing_errors_doc)] -pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> { - arg.to_string_lossy().parse::<u16>().map(|_| ()) -} diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 45353901c98..15578d69c3a 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,13 +1,12 @@ use crate::clippy_project_root; use aho_corasick::AhoCorasickBuilder; -use indoc::writedoc; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, SeekFrom, Write as _}; +use std::io::{self, Read, Seek, Write as _}; use std::ops::Range; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; @@ -77,12 +76,8 @@ fn generate_lint_files( for lint in usable_lints .iter() .map(|l| &*l.name) - .chain(deprecated_lints.iter().map(|l| &*l.name)) - .chain( - renamed_lints - .iter() - .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)), - ) + .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) .sorted() { writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); @@ -108,11 +103,6 @@ fn generate_lint_files( update_mode, &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), ); - process_file( - "clippy_lints/src/lib.deprecated.rs", - update_mode, - &gen_deprecated(deprecated_lints), - ); let content = gen_deprecated_lints_test(deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); @@ -205,7 +195,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { let ext = f.path().extension(); (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("renamed_lints.rs")) + && name != Some(OsStr::new("deprecated_lints.rs")) }) { rewrite_file(file.path(), |s| { @@ -213,6 +203,19 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { }); } + let version = crate::new_lint::get_stabilization_version(); + rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { + insert_at_marker( + s, + "// end renamed lints. used by `cargo dev rename_lint`", + &format!( + "#[clippy::version = \"{version}\"]\n \ + (\"{}\", \"{}\"),\n ", + lint.old_name, lint.new_name, + ), + ) + }); + renamed_lints.push(lint); renamed_lints.sort_by(|lhs, rhs| { lhs.new_name @@ -222,11 +225,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { .then_with(|| lhs.old_name.cmp(&rhs.old_name)) }); - write_file( - Path::new("clippy_lints/src/renamed_lints.rs"), - &gen_renamed_lints_list(&renamed_lints), - ); - if uplift { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( @@ -293,7 +291,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) + { rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); } @@ -304,7 +303,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { println!("note: `cargo uitest` still needs to be run to update the test results"); } -const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// Runs the `deprecate` command /// /// This does the following: @@ -314,33 +312,16 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: Option<&str>) { - fn finish( - (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>), - name: &str, - reason: &str, - ) { - deprecated_lints.push(DeprecatedLint { - name: name.to_string(), - reason: reason.to_string(), - declaration_range: Range::default(), - }); - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - - if reason == DEFAULT_DEPRECATION_REASON { - println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); - } - println!("note: you must run `cargo uitest` to update the test results"); - } - - let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON); - let name_lower = name.to_lowercase(); - let name_upper = name.to_uppercase(); +pub fn deprecate(name: &str, reason: &str) { + let prefixed_name = if name.starts_with("clippy::") { + name.to_owned() + } else { + format!("clippy::{name}") + }; + let stripped_name = &prefixed_name[8..]; - let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { + let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { eprintln!("error: failed to find lint `{name}`"); return; }; @@ -357,13 +338,27 @@ pub fn deprecate(name: &str, reason: Option<&str>) { let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) { - declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap(); - finish((lints, deprecated_lints, renamed_lints), name, reason); - return; - } + if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { + let version = crate::new_lint::get_stabilization_version(); + rewrite_file(deprecated_lints_path, |s| { + insert_at_marker( + s, + "// end deprecated lints. used by `cargo dev deprecate_lint`", + &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), + ) + }); + + deprecated_lints.push(DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + }); - eprintln!("error: lint not found"); + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } } fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> { @@ -377,14 +372,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io // Some lints have their own directories, delete them if path.is_dir() { - fs::remove_dir_all(path).ok(); + let _ = fs::remove_dir_all(path); return; } // Remove all related test files - fs::remove_file(path.with_extension("rs")).ok(); - fs::remove_file(path.with_extension("stderr")).ok(); - fs::remove_file(path.with_extension("fixed")).ok(); + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); } fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { @@ -427,7 +422,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io lint_mod_path.set_file_name(name); lint_mod_path.set_extension("rs"); - fs::remove_file(lint_mod_path).ok(); + let _ = fs::remove_file(lint_mod_path); } let mut content = @@ -465,37 +460,6 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io Ok(false) } -fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> { - let mut file = OpenOptions::new().write(true).open(path)?; - - file.seek(SeekFrom::End(0))?; - - let version = crate::new_lint::get_stabilization_version(); - let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON { - "TODO" - } else { - reason - }; - - writedoc!( - file, - " - - declare_deprecated_lint! {{ - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// {deprecation_reason} - #[clippy::version = \"{version}\"] - pub {name}, - \"{reason}\" - }} - - " - ) -} - /// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there /// were no replacements. fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> { @@ -604,14 +568,12 @@ impl Lint { struct DeprecatedLint { name: String, reason: String, - declaration_range: Range<usize>, } impl DeprecatedLint { - fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self { + fn new(name: &str, reason: &str) -> Self { Self { - name: name.to_lowercase(), + name: remove_line_splices(name), reason: remove_line_splices(reason), - declaration_range, } } } @@ -629,28 +591,6 @@ impl RenamedLint { } } -/// Generates the `register_removed` code -#[must_use] -fn gen_deprecated(lints: &[DeprecatedLint]) -> String { - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("{\n"); - for lint in lints { - let _: fmt::Result = write!( - output, - concat!( - " store.register_removed(\n", - " \"clippy::{}\",\n", - " \"{}\",\n", - " );\n" - ), - lint.name, lint.reason, - ); - } - output.push_str("}\n"); - - output -} - /// Generates the code for registering lints #[must_use] fn gen_declared_lints<'a>( @@ -680,7 +620,7 @@ fn gen_declared_lints<'a>( fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { let mut res: String = GENERATED_FILE_COMMENT.into(); for lint in lints { - writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); } res.push_str("\nfn main() {}\n"); res @@ -699,27 +639,13 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { seen_lints.clear(); for lint in lints { if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); } } res.push_str("\nfn main() {}\n"); res } -fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { - const HEADER: &str = "\ - // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ - #[rustfmt::skip]\n\ - pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; - - let mut res = String::from(HEADER); - for lint in lints { - writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); - } - res.push_str("];\n"); - res -} - /// Gathers all lints defined in `clippy_lints/src` fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { let mut lints = Vec::with_capacity(1000); @@ -744,10 +670,10 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { module.strip_suffix(".rs").unwrap_or(&module) }; - match module { - "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), - "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), - _ => parse_contents(&contents, module, &mut lints), + if module == "deprecated_lints" { + parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); + } else { + parse_contents(&contents, module, &mut lints); } } (lints, deprecated_lints, renamed_lints) @@ -848,54 +774,37 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { } /// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); +fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) { + let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { + return; + }; + let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { + return; + }; - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint", - ) { - let start = range.start; + for line in deprecated_src.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len as usize; + offset = range.end; - let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| { - !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }) + LintDeclSearchResult { + token_kind: t.kind, + content: &line[range.clone()], + range, + } }); + let (name, reason) = match_tokens!( iter, - // !{ - Bang OpenBrace - // #[clippy::version = "version"] - Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket - // pub LINT_NAME, - Ident Ident(name) Comma - // "description" - Literal{kind: LiteralKind::Str{..},..}(reason) + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma ); - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - }) = iter.next() - { - lints.push(DeprecatedLint::new(name, reason, start..range.end)); - } + deprecated.push(DeprecatedLint::new(name, reason)); } -} - -fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) { - for line in contents.lines() { + for line in renamed_src.lines() { let mut offset = 0usize; let mut iter = tokenize(line).map(|t| { let range = offset..offset + t.len as usize; @@ -915,7 +824,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) { // "new_name"), Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma ); - lints.push(RenamedLint::new(old_name, new_name)); + renamed.push(RenamedLint::new(old_name, new_name)); } } @@ -1015,6 +924,12 @@ fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { panic!("failed to {action} file `{}`: {error}", name.display()) } +fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option<String> { + let i = text.find(marker)?; + let (pre, post) = text.split_at(i); + Some([pre, new_text, post].into_iter().collect()) +} + fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) { let mut file = OpenOptions::new() .write(true) @@ -1085,31 +1000,6 @@ mod tests { } #[test] - fn test_parse_deprecated_contents() { - static DEPRECATED_CONTENTS: &str = r#" - /// some doc comment - declare_deprecated_lint! { - #[clippy::version = "I'm a version"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" - } - "#; - - let mut result = Vec::new(); - parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result); - for r in &mut result { - r.declaration_range = Range::default(); - } - - let expected = vec![DeprecatedLint::new( - "should_assert_eq", - "\"`assert!()` will be more flexible with RFC 2011\"", - Range::default(), - )]; - assert_eq!(expected, result); - } - - #[test] fn test_usable_lints() { let lints = vec![ Lint::new( @@ -1177,34 +1067,4 @@ mod tests { ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } - - #[test] - fn test_gen_deprecated() { - let lints = vec![ - DeprecatedLint::new( - "should_assert_eq", - "\"has been superseded by should_assert_eq2\"", - Range::default(), - ), - DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()), - ]; - - let expected = GENERATED_FILE_COMMENT.to_string() - + &[ - "{", - " store.register_removed(", - " \"clippy::should_assert_eq\",", - " \"has been superseded by should_assert_eq2\",", - " );", - " store.register_removed(", - " \"clippy::another_deprecated\",", - " \"will be removed\",", - " );", - "}", - ] - .join("\n") - + "\n"; - - assert_eq!(expected, gen_deprecated(&lints)); - } } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index eb04c006f89..99ed93468a0 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -32,7 +32,6 @@ url = "2.2" walkdir = "2.3" [features] -deny-warnings = ["clippy_config/deny-warnings", "clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default internal = ["serde_json", "tempfile", "regex"] diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index e6d52bcef71..3b4cc113480 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -97,7 +97,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - format!("approximate value of `{module}::consts::{}` found", &name), + format!("approximate value of `{module}::consts::{name}` found"), None, "consider using the constant directly", ); diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index cfa25005a05..fefd8195f8e 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -52,13 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for AsConversions { && !in_external_macro(cx.sess(), expr.span) && !is_from_proc_macro(cx, expr) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, AS_CONVERSIONS, expr.span, "using a potentially dangerous silent `as` conversion", - None, - "consider using a safe wrapper for this conversion", + |diag| { + diag.help("consider using a safe wrapper for this conversion"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index 0db1456d40b..69a8eb7d94e 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -1,6 +1,6 @@ use std::fmt; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; use rustc_ast::{InlineAsm, Item, ItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; @@ -49,14 +49,10 @@ fn check_asm_syntax( }; if style == check_for { - span_lint_and_help( - cx, - lint, - span, - format!("{style} x86 assembly syntax used"), - None, - format!("use {} x86 assembly syntax", !style), - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, lint, span, format!("{style} x86 assembly syntax used"), |diag| { + diag.help(format!("use {} x86 assembly syntax", !style)); + }); } } } @@ -98,13 +94,13 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); impl EarlyLintPass for InlineAsmX86IntelSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Intel); + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Intel); + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); } } } @@ -146,13 +142,13 @@ declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); impl EarlyLintPass for InlineAsmX86AttSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Att); + check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Att); } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Att); + check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, item.span, AsmStyle::Att); } } } diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index ed4cdce8cb8..7eaac80f969 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_inside_always_const_context; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return; }; - let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { + let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index 7217686dcca..f1cb4a05af8 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; @@ -68,39 +68,28 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } - let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; - let mut app = Applicability::MachineApplicable; - match method_segment.ident.as_str() { + let (message, replacement) = match method_segment.ident.as_str() { "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { - span_lint_and_sugg( - cx, - ASSERTIONS_ON_RESULT_STATES, - macro_call.span, - "called `assert!` with `Result::is_ok`", - "replace with", - format!( - "{}.unwrap(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + ("called `assert!` with `Result::is_ok`", "unwrap") }, "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { - span_lint_and_sugg( - cx, - ASSERTIONS_ON_RESULT_STATES, - macro_call.span, - "called `assert!` with `Result::is_err`", - "replace with", - format!( - "{}.unwrap_err(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + ("called `assert!` with `Result::is_err`", "unwrap_err") }, - _ => (), + _ => return, }; + span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| { + let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; + let mut app = Applicability::MachineApplicable; + diag.span_suggestion( + macro_call.span, + "replace with", + format!( + "{}.{replacement}(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + ), + app, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 03f777600f0..6e336efbb90 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -227,9 +227,22 @@ fn build_sugg<'tcx>( match call_kind { CallKind::Method => { let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", app) + // If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make + // the generated code a bit simpler. In other cases, we don't do this special case, to avoid + // having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437). + + let ty = cx.typeck_results().expr_ty(ref_expr); + if ty.is_ref() { + // Apply special case, remove `*` + // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // Keep the original lhs + // `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", app) + } } else { + // Keep the original lhs // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app) } @@ -249,8 +262,16 @@ fn build_sugg<'tcx>( }, CallKind::Ufcs => { let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", app) + // See special case of removing `*` in method handling above + let ty = cx.typeck_results().expr_ty(ref_expr); + if ty.is_ref() { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)` + // mut_addr_deref is used to avoid unnecessary parentheses around `*lhs` + Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref() + } } else { // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr() diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs index df9994086cd..a5a7b9f74a6 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs @@ -1,5 +1,5 @@ use super::ALLOW_ATTRIBUTES; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; @@ -13,14 +13,14 @@ pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) { && let Some(ident) = attr.ident() && !is_from_proc_macro(cx, attr) { - span_lint_and_sugg( - cx, - ALLOW_ATTRIBUTES, - ident.span, - "#[allow] attribute found", - "replace it with", - "expect".into(), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| { + diag.span_suggestion( + ident.span, + "replace it with", + "expect", + Applicability::MachineApplicable, + ); + }); } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 4b42616a636..4ab97118df1 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -1,5 +1,5 @@ use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON}; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_lint::{LateContext, LintContext}; @@ -21,12 +21,14 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet return; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, format!("`{}` attribute without specifying a reason", name.as_str()), - None, - "try adding a reason at the end with `, reason = \"..\"`", + |diag| { + diag.help("try adding a reason at the end with `, reason = \"..\"`"); + }, ); } diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index 561ca9bd986..612712d1684 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{in_constant, is_else_clause}; +use clippy_utils::{is_else_clause, is_in_const_context}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { && let Some(else_lit) = as_int_bool_lit(else_) && then_lit != else_lit && !expr.span.from_expansion() - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index a1c6c0b608f..a2f48c18170 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -477,14 +477,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { cx: self.cx, }; if let Ok(expr) = h2q.run(e) { - if h2q.terminals.len() > 8 { - // QMC has exponentially slow behavior as the number of terminals increases - // 8 is reasonable, it takes approximately 0.2 seconds. - // See #825 + let stats = terminal_stats(&expr); + if stats.ops > 7 { + // QMC has exponentially slow behavior as the number of ops increases. + // See #825, #13206 return; } - - let stats = terminal_stats(&expr); let mut simplified = expr.simplify(); for simple in Bool::Not(Box::new(expr)).simplify() { match simple { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index ff460a3fd8e..bd3acc06f4b 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use clippy_utils::source::snippet_opt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; @@ -21,7 +21,7 @@ pub(super) fn check( cast_to_hir: &rustc_hir::Ty<'_>, msrv: &Msrv, ) { - if !should_lint(cx, expr, cast_from, cast_to, msrv) { + if !should_lint(cx, cast_from, cast_to, msrv) { return; } @@ -70,9 +70,9 @@ pub(super) fn check( ); } -fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { +fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs index 5bc8692c289..464eabe5d9a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs @@ -1,6 +1,6 @@ use super::CAST_NAN_TO_INT; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, } fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - match constant(cx, cx.typeck_results(), e) { + match ConstEvalCtxt::new(cx).eval(e) { // FIXME(f16_f128): add these types when nan checks are available on all platforms Some(Constant::F64(n)) => n.is_nan(), Some(Constant::F32(n)) => n.is_nan(), diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 7c5acd1a678..102fe25fc67 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; use clippy_utils::source::snippet; @@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { - if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) { Some(c) } else { None diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 8bbd41b0db1..9daf237344a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,7 +1,7 @@ use std::convert::Infallible; use std::ops::ControlFlow; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{method_chain_args, sext}; @@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>( ) -> Option<i128> { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Int(ity) = *ty.kind() { return Some(sext(cx.tcx, n, ity)); @@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>( ) -> Option<u128> { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Uint(_ity) = *ty.kind() { return Some(n); diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 826589bf303..75de53f73ee 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -14,21 +14,24 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => { /* continue to checks */ }, } - match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { - let mut applicability = Applicability::MaybeIncorrect; - let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - FN_TO_NUMERIC_CAST_ANY, - expr.span, - format!("casting function pointer `{from_snippet}` to `{cast_to}`"), - "did you mean to invoke the function?", - format!("{from_snippet}() as {cast_to}"), - applicability, - ); - }, - _ => {}, + span_lint_and_then( + cx, + FN_TO_NUMERIC_CAST_ANY, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_with_style( + expr.span, + "did you mean to invoke the function?", + format!("{from_snippet}() as {cast_to}"), + applicability, + SuggestionStyle::ShowAlways, + ); + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index 5071af5ecb9..3c1c7d2dc3a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::{in_constant, is_integer_literal, std_or_core}; +use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::ZERO_PTR; pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { if let TyKind::Ptr(ref mut_ty) = to.kind && is_integer_literal(from, 0) - && !in_constant(cx, from.hir_id) + && !is_in_const_context(cx) && let Some(std_or_core) = std_or_core(cx) { let (msg, sugg_fn) = match mut_ty.mutbl { diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 0b1ab5411bf..1711565fca8 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -4,7 +4,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; +use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { _ => return, } && !in_external_macro(cx.sess(), item.span) - && !in_constant(cx, item.hir_id) + && !is_in_const_context(cx) && self.msrv.meets(msrvs::TRY_FROM) && let Some(cv) = match op2 { // todo: check for case signed -> larger unsigned == only x >= 0 diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 2c23c0b4f15..5d78744e9b5 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq}; +use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 27c00948a8f..b49a977dbea 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use rustc_errors::Applicability; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -39,14 +39,24 @@ impl LateLintPass<'_> for CreateDir { && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id) { - span_lint_and_sugg( + span_lint_and_then( cx, CREATE_DIR, expr.span, "calling `std::fs::create_dir` where there may be a better way", - "consider calling `std::fs::create_dir_all` instead", - format!("create_dir_all({})", snippet(cx, arg.span, "..")), - Applicability::MaybeIncorrect, + |diag| { + let mut app = Applicability::MaybeIncorrect; + diag.span_suggestion_with_style( + expr.span, + "consider calling `std::fs::create_dir_all` instead", + format!( + "create_dir_all({})", + snippet_with_applicability(cx, arg.span, "..", &mut app) + ), + app, + SuggestionStyle::ShowAlways, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 788c6f3ada2..93c8fff05e9 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::source::snippet_with_applicability; @@ -65,61 +65,67 @@ impl LateLintPass<'_> for DbgMacro { // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) { - let mut applicability = Applicability::MachineApplicable; - - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => return, - }; - self.prev_ctxt = cur_syntax_ctxt; - span_lint_and_sugg( + span_lint_and_then( cx, DBG_MACRO, - sugg_span, + macro_call.span, "the `dbg!` macro is intended as a debugging tool", - "remove the invocation before committing it to a version control system", - suggestion, - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => unreachable!(), + }; + + diag.span_suggestion( + sugg_span, + "remove the invocation before committing it to a version control system", + suggestion, + applicability, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 69f9eb6842b..3fb083dd833 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -14,8 +14,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, @@ -741,6 +739,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unused_async::UNUSED_ASYNC_INFO, crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, crate::unused_peekable::UNUSED_PEEKABLE_INFO, + crate::unused_result_ok::UNUSED_RESULT_OK_INFO, crate::unused_rounding::UNUSED_ROUNDING_INFO, crate::unused_self::UNUSED_SELF_INFO, crate::unused_unit::UNUSED_UNIT_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index 72fa05be3cc..0b7279f2b36 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -221,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(ToString::to_string) .collect::<Vec<_>>() .join(", "); - format!("{adt_def_ty_name}::<{}>", &tys_str) + format!("{adt_def_ty_name}::<{tys_str}>") } else { binding_type.to_string() }; diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 9af73db6849..a74b3a8c836 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -92,20 +92,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), ty::Float(FloatTy::F64) => ("f64", true), - // Default numeric fallback never results in other types. _ => return, }; - - let src = if let Some(src) = snippet_opt(self.cx, lit.span) { - src - } else { - match lit.node { - LitKind::Int(src, _) => format!("{src}"), - LitKind::Float(src, _) => format!("{src}"), - _ => return, - } - }; - let sugg = numeric_literal::format(&src, Some(suffix), is_float); span_lint_hir_and_then( self.cx, DEFAULT_NUMERIC_FALLBACK, @@ -113,6 +101,17 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { lit.span, "default numeric fallback might occur", |diag| { + let src = if let Some(src) = snippet_opt(self.cx, lit.span) { + src + } else { + match lit.node { + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), + _ => unreachable!("Default numeric fallback never results in other types"), + } + }; + + let sugg = numeric_literal::format(&src, Some(suffix), is_float); diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); }, ); diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 3fa9bad0d03..9f020d3081c 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -56,16 +56,18 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { && is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, DEFAULT_UNION_REPRESENTATION, item.span, "this union has the default representation", - None, - format!( - "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", - cx.tcx.def_path_str(item.owner_id) - ), + |diag| { + diag.help(format!( + "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", + cx.tcx.def_path_str(item.owner_id) + )); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index a0900f46f6a..0066ed64325 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -1,243 +1,181 @@ -// NOTE: Entries should be created with `cargo dev deprecate` - -/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This -/// enables the simple extraction of the metadata without changing the current deprecation -/// declaration. -pub struct ClippyDeprecatedLint { - #[allow(dead_code)] - pub desc: &'static str, -} - -#[macro_export] -macro_rules! declare_deprecated_lint { - { $(#[$attr:meta])* pub $name: ident, $reason: literal} => { - $(#[$attr])* - #[allow(dead_code)] - pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint { - desc: $reason - }; - } -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `assert!(a == b)` and recommend - /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. +// This file is managed by `cargo dev rename_lint` and `cargo dev deprecate_lint`. +// Prefer to use those when possible. + +macro_rules! declare_with_version { + ($name:ident($name_version:ident): &[$ty:ty] = &[$( + #[clippy::version = $version:literal] + $e:expr, + )*]) => { + pub static $name: &[$ty] = &[$($e),*]; + #[allow(unused)] + pub static $name_version: &[&str] = &[$($version),*]; + }; +} + +#[rustfmt::skip] +declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ #[clippy::version = "pre 1.29.0"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::extend`, which was slower than - /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] - pub EXTEND_FROM_SLICE, - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// `Range::step_by(0)` used to be linted since it's - /// an infinite iterator, which is better expressed by `iter::repeat`, - /// but the method has been removed for `Iterator::step_by` which panics - /// if given a zero + ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), #[clippy::version = "pre 1.29.0"] - pub RANGE_STEP_BY_ZERO, - "`iterator.step_by(0)` panics nowadays" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::as_slice`, which was unstable with good - /// stable alternatives. `Vec::as_slice` has now been stabilized. + ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), #[clippy::version = "pre 1.29.0"] - pub UNSTABLE_AS_SLICE, - "`Vec::as_slice` has been stabilized in 1.7" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::as_mut_slice`, which was unstable with good - /// stable alternatives. `Vec::as_mut_slice` has now been stabilized. + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - pub UNSTABLE_AS_MUT_SLICE, - "`Vec::as_mut_slice` has been stabilized in 1.7" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint should never have applied to non-pointer types, as transmuting - /// between non-pointer types of differing alignment is well-defined behavior (it's semantically - /// equivalent to a memcpy). This lint has thus been refactored into two separate lints: - /// cast_ptr_alignment and transmute_ptr_to_ptr. + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - pub MISALIGNED_TRANSMUTE, - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint is too subjective, not having a good reason for being in clippy. - /// Additionally, compound assignment operators may be overloaded separately from their non-assigning - /// counterparts, so this lint may suggest a change in behavior or the code may not compile. + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), #[clippy::version = "1.30.0"] - pub ASSIGN_OPS, - "using compound assignment operators (e.g., `+=`) is harmless" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The original rule will only lint for `if let`. After - /// making it support to lint `match`, naming as `if let` is not suitable for it. - /// So, this lint is deprecated. + ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] - pub IF_LET_REDUNDANT_PATTERN_MATCHING, - "this lint has been changed to redundant_pattern_matching" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint used to suggest replacing `let mut vec = - /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The - /// replacement has very different performance characteristics so the lint is - /// deprecated. - #[clippy::version = "pre 1.29.0"] - pub UNSAFE_VECTOR_INITIALIZATION, - "the replacement suggested by this lint had substantially different behavior" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by #[must_use] in rustc. + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "1.39.0"] - pub UNUSED_COLLECT, - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// Associated-constants are now preferred. + ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), #[clippy::version = "1.44.0"] - pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The regex! macro does not exist anymore. + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "1.47.0"] - pub REGEX_MACRO, - "the regex! macro has been removed from the regex crate in 2018" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `manual_find_map`, a - /// more specific lint. - #[clippy::version = "1.51.0"] - pub FIND_MAP, - "this lint has been replaced by `manual_find_map`, a more specific lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `manual_filter_map`, a - /// more specific lint. - #[clippy::version = "1.53.0"] - pub FILTER_MAP, - "this lint has been replaced by `manual_filter_map`, a more specific lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The `avoid_breaking_exported_api` config option was added, which - /// enables the `enum_variant_names` lint for public items. + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), #[clippy::version = "1.54.0"] - pub PUB_ENUM_VARIANT_NAMES, - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The `avoid_breaking_exported_api` config option was added, which - /// enables the `wrong_self_conversion` lint for public items. + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "1.54.0"] - pub WRONG_PUB_SELF_CONVENTION, - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos. - /// - /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), + // end deprecated lints. used by `cargo dev deprecate_lint` +]} + +#[rustfmt::skip] +declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ + #[clippy::version = ""] + ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), + #[clippy::version = ""] + ("clippy::blacklisted_name", "clippy::disallowed_names"), + #[clippy::version = ""] + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::box_vec", "clippy::box_collection"), + #[clippy::version = ""] + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + #[clippy::version = ""] + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + #[clippy::version = ""] + ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), + #[clippy::version = ""] + ("clippy::disallowed_method", "clippy::disallowed_methods"), + #[clippy::version = ""] + ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = ""] + ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), + #[clippy::version = "1.51.0"] + ("clippy::find_map", "clippy::manual_find_map"), + #[clippy::version = "1.53.0"] + ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = ""] + ("clippy::identity_conversion", "clippy::useless_conversion"), + #[clippy::version = "pre 1.29.0"] + ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), + #[clippy::version = ""] + ("clippy::if_let_some_result", "clippy::match_result_ok"), + #[clippy::version = ""] + ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), + #[clippy::version = ""] + ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), + #[clippy::version = ""] + ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), + #[clippy::version = ""] + ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = ""] + ("clippy::new_without_default_derive", "clippy::new_without_default"), + #[clippy::version = ""] + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + #[clippy::version = ""] + ("clippy::option_expect_used", "clippy::expect_used"), + #[clippy::version = ""] + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + #[clippy::version = ""] + ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), + #[clippy::version = ""] + ("clippy::ref_in_deref", "clippy::needless_borrow"), + #[clippy::version = ""] + ("clippy::result_expect_used", "clippy::expect_used"), + #[clippy::version = ""] + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + #[clippy::version = ""] + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + #[clippy::version = ""] + ("clippy::stutter", "clippy::module_name_repetitions"), + #[clippy::version = ""] + ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), + #[clippy::version = ""] + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] + ("clippy::zero_width_space", "clippy::invisible_characters"), + #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), + #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), #[clippy::version = "1.80.0"] - pub MAYBE_MISUSED_CFG, - "this lint has been replaced by `unexpected_cfgs`" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes. - /// - /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), #[clippy::version = "1.80.0"] - pub MISMATCHED_TARGET_OS, - "this lint has been replaced by `unexpected_cfgs`" -} + ("clippy::mismatched_target_os", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + #[clippy::version = ""] + ("clippy::undropped_manually_drops", "undropped_manually_drops"), + #[clippy::version = ""] + ("clippy::unknown_clippy_lints", "unknown_lints"), + #[clippy::version = ""] + ("clippy::unused_label", "unused_labels"), + #[clippy::version = ""] + ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), + #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + // end renamed lints. used by `cargo dev rename_lint` +]} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 253f9959e13..d0cb2488468 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_manually_drop}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy, ExprUseNode, @@ -947,7 +947,7 @@ fn report<'tcx>( let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_mid_ty_refs(ty); + let (_, ref_count) = peel_middle_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 5b6a5b08aa9..d7b3a7c74f3 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, @@ -768,7 +768,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize ); } }, - FootnoteReference(text) | Text(text) => { + Text(text) => { paragraph_range.end = range.end; let range_ = range.clone(); ticks_unbalanced |= text.contains('`') @@ -812,7 +812,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize } text_to_check.push((text, range, code_level)); } - }, + } + FootnoteReference(_) => {} } } headers @@ -857,7 +858,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { "assert" | "assert_eq" | "assert_ne" ) { - self.is_const = in_constant(self.cx, expr.hir_id); + self.is_const = self.cx.tcx.hir().is_inside_const_context(expr.hir_id); self.panic_span = Some(macro_call.span); } } diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 4a6ffcd9a78..c7dd7292a14 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; @@ -126,14 +126,14 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { }, _ => return, }; - span_lint_and_note( - cx, - lint, - expr.span, - msg, - note_span, - format!("argument has type `{arg_ty}`"), - ); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let note = format!("argument has type `{arg_ty}`"); + if let Some(span) = note_span { + diag.span_note(span, note); + } else { + diag.note(note); + } + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs index 7a6dc469727..02f9c2c3648 100644 --- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -1,6 +1,6 @@ //! Lint on if expressions with an else if, but without a final else branch. -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -54,13 +54,15 @@ impl EarlyLintPass for ElseIfWithoutElse { && let ExprKind::If(_, _, None) = els.kind && !in_external_macro(cx.sess(), item.span) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, ELSE_IF_WITHOUT_ELSE, els.span, "`if` expression with an `else if`, but without a final `else`", - None, - "add an `else` block here", + |diag| { + diag.help("add an `else` block here"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs index c5fc72b5e2d..b66dd2108fc 100644 --- a/src/tools/clippy/clippy_lints/src/empty_drop.rs +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::peel_blocks; use rustc_errors::Applicability; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; @@ -50,15 +50,14 @@ impl LateLintPass<'_> for EmptyDrop { && block.stmts.is_empty() && block.expr.is_none() { - span_lint_and_sugg( - cx, - EMPTY_DROP, - item.span, - "empty drop implementation", - "try removing this impl", - String::new(), - Applicability::MaybeIncorrect, - ); + span_lint_and_then(cx, EMPTY_DROP, item.span, "empty drop implementation", |diag| { + diag.span_suggestion_hidden( + item.span, + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index 5bba9c562b9..209104c5385 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -7,7 +7,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Symbol; -use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -141,52 +140,6 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix _ => return, }; - let mut help = None; - - 'build_help: { - // all lints disallowed, don't give help here - if [&[lint], other_lints.as_slice()] - .concat() - .iter() - .all(|lint| !lint.allowed(cx, expr)) - { - break 'build_help; - } - - // ne_bytes and all other lints allowed - if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { - help = Some(Cow::Borrowed("specify the desired endianness explicitly")); - break 'build_help; - } - - // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but - // le_bytes is not - if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { - help = Some(Cow::Borrowed("use the native endianness instead")); - break 'build_help; - } - - let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); - let len = allowed_lints.clone().count(); - - let mut help_str = "use ".to_owned(); - - for (i, lint) in allowed_lints.enumerate() { - let only_one = len == 1; - if !only_one { - help_str.push_str("either of "); - } - - help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); - - if i != len && !only_one { - help_str.push_str("or "); - } - } - - help = Some(Cow::Owned(help_str + "instead")); - } - span_lint_and_then( cx, lint.as_lint(), @@ -198,9 +151,47 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix if prefix == Prefix::To { " method" } else { "" }, ), move |diag| { - if let Some(help) = help { - diag.help(help); + // all lints disallowed, don't give help here + if [&[lint], other_lints.as_slice()] + .concat() + .iter() + .all(|lint| !lint.allowed(cx, expr)) + { + return; + } + + // ne_bytes and all other lints allowed + if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { + diag.help("specify the desired endianness explicitly"); + return; + } + + // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but + // le_bytes is not + if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { + diag.help("use the native endianness instead"); + return; + } + + let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); + let len = allowed_lints.clone().count(); + + let mut help_str = "use ".to_owned(); + + for (i, lint) in allowed_lints.enumerate() { + let only_one = len == 1; + if !only_one { + help_str.push_str("either of "); + } + + help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); + + if i != len && !only_one { + help_str.push_str("or "); + } } + help_str.push_str("instead"); + diag.help(help_str); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 30eb643c42e..e54cd248ead 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .const_eval_poly(def_id.to_def_id()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr().discr_type().to_ty(cx.tcx); diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 0ed7859418b..5a7226d590c 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt, - TypeVisitableExt, TypeckResults, + self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -74,159 +73,184 @@ declare_clippy_lint! { declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]); impl<'tcx> LateLintPass<'tcx> for EtaReduction { - #[allow(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let body = if let ExprKind::Closure(c) = expr.kind - && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) - && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) - && !expr.span.from_expansion() - { - cx.tcx.hir().body(c.body) - } else { - return; - }; - - if body.value.span.from_expansion() { - if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); - } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind { + for arg in args { + check_clousure(cx, Some(receiver), arg); } - // skip `foo(|| macro!())` - return; } + if let ExprKind::Call(func, args) = expr.kind { + check_clousure(cx, None, func); + for arg in args { + check_clousure(cx, None, arg); + } + } + } +} - let typeck = cx.typeck_results(); - let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { - closure_subs.as_closure() - } else { - return; - }; +#[allow(clippy::too_many_lines)] +fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { + let body = if let ExprKind::Closure(c) = expr.kind + && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) + && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) + && !expr.span.from_expansion() + { + cx.tcx.hir().body(c.body) + } else { + return; + }; - if is_adjusted(cx, body.value) { - return; + if body.value.span.from_expansion() { + if body.params.is_empty() { + if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } } + // skip `foo(|| macro!())` + return; + } - match body.value.kind { - ExprKind::Call(callee, args) - if matches!( - callee.kind, - ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) - ) => + if is_adjusted(cx, body.value) { + return; + } + + let typeck = cx.typeck_results(); + let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { + closure_subs.as_closure() + } else { + return; + }; + let closure_sig = cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(); + match body.value.kind { + ExprKind::Call(callee, args) + if matches!( + callee.kind, + ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) + ) => + { + let callee_ty_raw = typeck.expr_ty(callee); + let callee_ty = callee_ty_raw.peel_refs(); + if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + || !check_inputs(typeck, body.params, None, args) { - let callee_ty_raw = typeck.expr_ty(callee); - let callee_ty = callee_ty_raw.peel_refs(); - if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) - || !check_inputs(typeck, body.params, None, args) - { - return; - } - let callee_ty_adjusted = typeck - .expr_adjustments(callee) - .last() - .map_or(callee_ty, |a| a.target.peel_refs()); + return; + } + let callee_ty_adjusted = typeck + .expr_adjustments(callee) + .last() + .map_or(callee_ty, |a| a.target.peel_refs()); - let sig = match callee_ty_adjusted.kind() { - ty::FnDef(def, _) => { - // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` - if cx.tcx.has_attr(*def, sym::track_caller) { - return; - } + let sig = match callee_ty_adjusted.kind() { + ty::FnDef(def, _) => { + // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` + if cx.tcx.has_attr(*def, sym::track_caller) { + return; + } - cx.tcx.fn_sig(def).skip_binder().skip_binder() - }, - ty::FnPtr(sig) => sig.skip_binder(), - ty::Closure(_, subs) => cx - .tcx - .signature_unclosure(subs.as_closure().sig(), Safety::Safe) - .skip_binder(), - _ => { - if typeck.type_dependent_def_id(body.value.hir_id).is_some() - && let subs = typeck.node_args(body.value.hir_id) - && let output = typeck.expr_ty(body.value) - && let ty::Tuple(tys) = *subs.type_at(1).kind() - { - cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust) - } else { - return; - } - }, - }; - if check_sig(cx, closure, sig) - && let generic_args = typeck.node_args(callee.hir_id) - // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not - // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result - // in a type which is `'static`. - // For now ignore all callee types which reference a type parameter. - && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) - { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).map_or(false, |l| { - // FIXME: Do we really need this `local_used_in` check? - // Isn't it checking something like... `callee(callee)`? - // If somehow this check is needed, add some test for it, - // 'cuz currently nothing changes after deleting this check. - local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) - }) { - match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { - // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { - snippet = format!("&{snippet}"); - }, - _ => (), - } + cx.tcx.fn_sig(def).skip_binder().skip_binder() + }, + ty::FnPtr(sig) => sig.skip_binder(), + ty::Closure(_, subs) => cx + .tcx + .signature_unclosure(subs.as_closure().sig(), Safety::Safe) + .skip_binder(), + _ => { + if typeck.type_dependent_def_id(body.value.hir_id).is_some() + && let subs = typeck.node_args(body.value.hir_id) + && let output = typeck.expr_ty(body.value) + && let ty::Tuple(tys) = *subs.type_at(1).kind() + { + cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust) + } else { + return; + } + }, + }; + if let Some(outer) = outer_receiver + && ty_has_static(sig.output()) + && let generic_args = typeck.node_args(outer.hir_id) + // HACK: Given a closure in `T.method(|| f())`, where `fn f() -> U where U: 'static`, `T.method(f)` + // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when + // T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic. + // So we have a hack like this. + && generic_args.len() > 0 + { + return; + } + if check_sig(closure_sig, sig) + && let generic_args = typeck.node_args(callee.hir_id) + // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not + // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result + // in a type which is `'static`. + // For now ignore all callee types which reference a type parameter. + && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { + if let Some(mut snippet) = snippet_opt(cx, callee.span) { + if path_to_local(callee).map_or(false, |l| { + // FIXME: Do we really need this `local_used_in` check? + // Isn't it checking something like... `callee(callee)`? + // If somehow this check is needed, add some test for it, + // 'cuz currently nothing changes after deleting this check. + local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) + }) { + match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { + // Mutable closure is used after current expr; we cannot consume it. + Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), + Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + snippet = format!("&{snippet}"); + }, + _ => (), } - diag.span_suggestion( - expr.span, - "replace the closure with the function itself", - snippet, - Applicability::MachineApplicable, - ); } - }); - } - }, - ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { - if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) - && !cx.tcx.has_attr(method_def_id, sym::track_caller) - && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) - { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - expr.span, - "redundant closure", - |diag| { - let args = typeck.node_args(body.value.hir_id); - let caller = self_.hir_id.owner.def_id; - let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); - diag.span_suggestion( - expr.span, - "replace the closure with the method itself", - format!("{}::{}", type_name, path.ident.name), - Applicability::MachineApplicable, - ); - }, - ); - } - }, - _ => (), - } + diag.span_suggestion( + expr.span, + "replace the closure with the function itself", + snippet, + Applicability::MachineApplicable, + ); + } + }); + } + }, + ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { + if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) + && !cx.tcx.has_attr(method_def_id, sym::track_caller) + && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) + { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + expr.span, + "redundant closure", + |diag| { + let args = typeck.node_args(body.value.hir_id); + let caller = self_.hir_id.owner.def_id; + let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); + diag.span_suggestion( + expr.span, + "replace the closure with the method itself", + format!("{}::{}", type_name, path.ident.name), + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => (), } } @@ -251,12 +275,8 @@ fn check_inputs( }) } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, call_sig: FnSig<'_>) -> bool { - call_sig.safety == Safety::Safe - && !has_late_bound_to_non_late_bound_regions( - cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(), - call_sig, - ) +fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool { + call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig) } /// This walks through both signatures and checks for any time a late-bound region is expected by an @@ -265,7 +285,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, c /// This is needed because rustc is unable to late bind early-bound regions in a function signature. fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool { fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool { - matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..)) + from_region.is_bound() && !to_region.is_bound() } fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool { @@ -318,3 +338,8 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' .zip(to_sig.inputs_and_output) .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty)) } + +fn ty_has_static(ty: Ty<'_>) -> bool { + ty.walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static())) +} diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 0f4176ec73b..9bf3baba4b5 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -88,11 +88,11 @@ impl LateLintPass<'_> for ExhaustiveItems { && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { - let suggestion_span = item.span.shrink_to_lo(); - let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_then(cx, lint, item.span, msg, |diag| { + let suggestion_span = item.span.shrink_to_lo(); + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); let sugg = format!("#[non_exhaustive]\n{indent}"); - diag.span_suggestion( + diag.span_suggestion_verbose( suggestion_span, "try adding #[non_exhaustive]", sugg, diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index bb74e345703..95b8e882da7 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -62,13 +62,15 @@ impl EarlyLintPass for FieldScopedVisibilityModifiers { // pub(self) is equivalent to not using pub at all, so we ignore it continue; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, FIELD_SCOPED_VISIBILITY_MODIFIERS, field.vis.span, "scoped visibility modifier on a field", - None, - "consider making the field private and adding a scoped visibility method for it", + |diag| { + diag.help("consider making the field private and adding a scoped visibility method for it"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 6adcd2235dc..f095c1add91 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal; use rustc_ast::ast::{self, LitFloatType, LitKind}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; @@ -105,32 +105,43 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { if is_whole && !sym_str.contains(['e', 'E']) { // Normalize the literal by stripping the fractional portion if sym_str.split('.').next().unwrap() != float_str { - // If the type suffix is missing the suggestion would be - // incorrectly interpreted as an integer so adding a `.0` - // suffix to prevent that. - if type_suffix.is_none() { - float_str.push_str(".0"); - } - - span_lint_and_sugg( + span_lint_and_then( cx, LOSSY_FLOAT_LITERAL, expr.span, "literal cannot be represented as the underlying type without loss of precision", - "consider changing the type or replacing it with", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, + |diag| { + // If the type suffix is missing the suggestion would be + // incorrectly interpreted as an integer so adding a `.0` + // suffix to prevent that. + if type_suffix.is_none() { + float_str.push_str(".0"); + } + diag.span_suggestion_with_style( + expr.span, + "consider changing the type or replacing it with", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, ); } } else if digits > max as usize && float_str.len() < sym_str.len() { - span_lint_and_sugg( + span_lint_and_then( cx, EXCESSIVE_PRECISION, expr.span, "float has excessive precision", - "consider changing the type or truncating it to", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion_with_style( + expr.span, + "consider changing the type or truncating it to", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 68bdf88d0a7..bf4bcabfe89 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,9 @@ use clippy_utils::consts::Constant::{Int, F32, F64}; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal, - peel_blocks, sugg, + eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, + numeric_literal, peel_blocks, sugg, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = constant(cx, cx.typeck_results(), base) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { rhs, ) = receiver.kind { - let recv = match ( - constant(cx, cx.typeck_results(), lhs), - constant(cx, cx.typeck_results(), rhs), - ) { + let ecx = ConstEvalCtxt::new(cx); + let recv = match (ecx.eval(lhs), ecx.eval(rhs)) { (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs, (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs, _ => return, @@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = constant(cx, cx.typeck_results(), receiver) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { @@ -251,7 +249,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } // Check argument - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( SUBOPTIMAL_FLOPS, @@ -291,7 +289,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { if value == Int(2) { if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { @@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> { ) = &add_rhs.kind && lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi" - && let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1) - && let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lvalue) = ecx.eval(largs_1) + && let Some(rvalue) = ecx.eval(rargs_1) && Int(2) == lvalue && Int(2) == rvalue { @@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && cx.typeck_results().expr_ty(lhs).is_floating_point() - && let Some(value) = constant(cx, cx.typeck_results(), rhs) + && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && cx.typeck_results().expr_ty(self_arg).is_floating_point() @@ -552,7 +551,7 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match constant_simple(cx, cx.typeck_results(), expr) { + match ConstEvalCtxt::new(cx).eval_simple(expr) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, @@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { mul_lhs, mul_rhs, ) = &div_lhs.kind - && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) - && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(rvalue) = ecx.eval(div_rhs) + && let Some(lvalue) = ecx.eval(mul_rhs) { // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) @@ -753,7 +753,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // All of these operations are currently not const and are in std. - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index a75538dd329..d05c5a01f41 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{higher, match_def_path, paths}; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; @@ -81,13 +81,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { _ => return, }; if is_format(cx, arg) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, FORMAT_PUSH_STRING, expr.span, "`format!(..)` appended to existing `String`", - None, - "consider using `write!` to avoid the extra allocation", + |diag| { + diag.help("consider using `write!` to avoid the extra allocation"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 9acb72b2e37..6ab7bbc2dfc 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{in_constant, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) // do not lint in constant context, because the suggestion won't work. - // NB: keep this check until a new `const_trait_impl` is available and stablized. - && !in_constant(cx, exp.hir_id) + // NB: keep this check until a new `const_trait_impl` is available and stabilized. + && !is_in_const_context(cx) { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let ty = cx.typeck_results().expr_ty(expr); diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index b38cc7b36a1..1c52514a330 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{higher, SpanlessEq}; +use clippy_utils::{eq_expr_value, higher}; use core::ops::ControlFlow; use rustc_errors::Diag; use rustc_hir::{Expr, ExprKind}; @@ -51,53 +51,45 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { if_else: Some(if_else), .. }) = higher::IfLet::hir(cx, expr) + && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None)) + && let Some(arm_mutex) = + for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex))) { - let is_mutex_lock = |e: &'tcx Expr<'tcx>| { - if let Some(mutex) = is_mutex_lock_call(cx, e) { - ControlFlow::Break(mutex) - } else { - ControlFlow::Continue(()) - } + let diag = |diag: &mut Diag<'_, ()>| { + diag.span_label( + op_mutex.span, + "this Mutex will remain locked for the entire `if let`-block...", + ); + diag.span_label( + arm_mutex.span, + "... and is tried to lock again here, which will always deadlock.", + ); + diag.help("move the lock call outside of the `if let ...` expression"); }; - - let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); - if let Some(op_mutex) = op_mutex { - let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); - if let Some(arm_mutex) = arm_mutex - && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) - { - let diag = |diag: &mut Diag<'_, ()>| { - diag.span_label( - op_mutex.span, - "this Mutex will remain locked for the entire `if let`-block...", - ); - diag.span_label( - arm_mutex.span, - "... and is tried to lock again here, which will always deadlock.", - ); - diag.help("move the lock call outside of the `if let ...` expression"); - }; - span_lint_and_then( - cx, - IF_LET_MUTEX, - expr.span, - "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", - diag, - ); - } - } + span_lint_and_then( + cx, + IF_LET_MUTEX, + expr.span, + "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", + diag, + ); } } } -fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn mutex_lock_call<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op_mutex: Option<&'tcx Expr<'_>>, +) -> ControlFlow<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.as_str() == "lock" && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() && is_type_diagnostic_item(cx, ty, sym::Mutex) + && op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op)) { - Some(self_arg) + ControlFlow::Break(self_arg) } else { - None + ControlFlow::Continue(()) } } diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index 2f6daeeb90d..0ebd8d0c237 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ //! lint on if branches that could be swapped so no `!` operation is necessary //! on the condition -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_else_clause; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -49,7 +49,7 @@ declare_clippy_lint! { declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) { return Constant::Int(0) == value; } false diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 39ea16b05d1..0bca53c1536 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{ + contains_return, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, path_res, peel_blocks, +}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -76,37 +78,44 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) && !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { - let mut app = Applicability::Unspecified; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) - .maybe_par() - .to_string(); - let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let mut method_body = if then_block.stmts.is_empty() { - arg_snip.into_owned() - } else { - format!("{{ /* snippet */ {arg_snip} }}") - }; let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { - method_body.insert_str(0, "|| "); "then" }; - let help = - format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",); - span_lint_and_help( + span_lint_and_then( cx, IF_THEN_SOME_ELSE_NONE, expr.span, format!("this could be simplified with `bool::{method_name}`"), - None, - help, + |diag| { + let mut app = Applicability::Unspecified; + let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + .maybe_par() + .to_string(); + let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; + let method_body = if let Some(first_stmt) = then_block.stmts.first() { + let (block_snippet, _) = + snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); + let closure = if method_name == "then" { "|| " } else { "" }; + format!("{closure} {{ {block_snippet}; {arg_snip} }}") + } else { + arg_snip.into_owned() + }; + + diag.span_suggestion( + expr.span, + "try", + format!("{cond_snip}.{method_name}({method_body})"), + app, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 344a04e6e7e..e56f33f8dcf 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; -use rustc_errors::Diag; +use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; use rustc_span::Span; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -77,33 +77,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { &generics_snip[1..generics_snip.len() - 1] }; - multispan_sugg( - diag, - "consider adding a type parameter", - vec![ - ( - generics_suggestion_span, - format!( - "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", - if generics_snip.is_empty() { "" } else { ", " }, - if vis.suggestions.is_empty() { - "" - } else { - // request users to add `Default` bound so that generic constructors can be used - " + Default" - }, - ), - ), - ( - target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + let mut suggestions = vec![ + ( + generics_suggestion_span, + format!( + "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", + if generics_snip.is_empty() { "" } else { ", " }, + if vis.suggestions.is_empty() { + "" + } else { + // request users to add `Default` bound so that generic constructors can be used + " + Default" + }, ), - ], + ), + ( + target.span(), + format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + ), + ]; + suggestions.extend(vis.suggestions); + + diag.multipart_suggestion( + "add a type parameter for `BuildHasher`", + suggestions, + Applicability::MaybeIncorrect, ); - - if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor", vis.suggestions); - } } if !cx.effective_visibilities.is_exported(item.owner_id.def_id) { diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index a102b434cfa..b926e1e62ba 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; use core::ops::ControlFlow; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -45,8 +45,6 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, span, "..", &mut app); span_lint_hir_and_then( cx, IMPLICIT_RETURN, @@ -54,14 +52,20 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { span, "missing `return` statement", |diag| { - diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app); + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, span, "..", &mut app); + diag.span_suggestion_with_style( + span, + "add `return` as shown", + format!("return {snip}"), + app, + SuggestionStyle::ShowAlways, + ); }, ); } fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; span_lint_hir_and_then( cx, IMPLICIT_RETURN, @@ -69,11 +73,14 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp break_span, "missing `return` statement", |diag| { - diag.span_suggestion( + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; + diag.span_suggestion_with_style( break_span, "change `break` to `return` as shown", format!("return {snip}"), app, + SuggestionStyle::ShowAlways, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index f225c6e7f04..dd5908553e5 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; @@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { - let tr = cx.typeck_results(); - if let Some(Constant::Int(c)) = constant(cx, tr, r) { + let ecx = ConstEvalCtxt::new(cx); + if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); }; - if let Some(Constant::Int(c)) = constant(cx, tr, l) { + if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 12ca6d43b27..0ef5b803a89 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -55,7 +55,6 @@ impl IncompatibleMsrv { } } - #[allow(clippy::cast_lossless)] fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion { if let Some(version) = self.is_above_msrv.get(&def_id) { return *version; @@ -67,9 +66,9 @@ impl IncompatibleMsrv { since: StableSince::Version(version), .. } => Some(RustcVersion::new( - version.major as _, - version.minor as _, - version.patch as _, + version.major.into(), + version.minor.into(), + version.patch.into(), )), _ => None, }) { diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 526b4e1fba0..2f9661c9ea3 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; @@ -246,7 +246,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { && let parent_id = cx.tcx.parent_hir_id(expr.hir_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind - && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) + && let Some(Constant::Int(index_value)) = ConstEvalCtxt::new(cx).eval(index_expr) && let Ok(index_value) = index_value.try_into() && index_value < max_suggested_slice diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 6729c7c8d10..3ac50b8f1fb 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -1,7 +1,7 @@ //! lint on indexing and slicing operations use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; use clippy_utils::{higher, is_from_proc_macro}; @@ -70,8 +70,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #![allow(unused)] - /// /// # let x = vec![0; 5]; /// # let y = [0, 1, 2, 3]; /// x.get(2); @@ -179,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { return; } // Index is a constant uint. - if let Some(constant) = constant(cx, cx.typeck_results(), index) { + if let Some(constant) = ConstEvalCtxt::new(cx).eval(index) { // only `usize` index is legal in rust array index // leave other type to rustc if let Constant::Int(off) = constant @@ -217,14 +215,15 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) { - let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr)); + let ecx = ConstEvalCtxt::new(cx); + let s = range.start.map(|expr| ecx.eval(expr)); let start = match s { Some(Some(Constant::Int(x))) => Some(x), Some(_) => None, None => Some(0), }; - let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr)); + let e = range.end.map(|expr| ecx.eval(expr)); let end = match e { Some(Some(Constant::Int(x))) => { if range.limits == RangeLimits::Closed { diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index fa7e7f6b76d..676d50c4951 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -41,7 +41,6 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let infinite_iter = 0..; - /// # #[allow(unused)] /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 0d3786dad4b..9eed7aa9243 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -1,6 +1,6 @@ //! lint on inherent implementations -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; @@ -105,13 +105,14 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. lint_spans.sort_by_key(|x| x.0.lo()); for (span, first_span) in lint_spans { - span_lint_and_note( + span_lint_and_then( cx, MULTIPLE_INHERENT_IMPL, span, "multiple implementations of this structure", - Some(first_span), - "first implementation here", + |diag| { + diag.span_note(first_span, "first implementation here"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs index 30f2285bdd2..1929fbded3b 100644 --- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; @@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) { + if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index c67da689aae..f2f841dcec3 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -66,16 +66,18 @@ impl LateLintPass<'_> for LargeIncludeFile { && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) { - span_lint_and_note( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LARGE_INCLUDE_FILE, expr.span.source_callsite(), "attempted to include a large file", - None, - format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - ), + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 8fa63f3e8fd..b522c22a44d 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{LetStmt, LocalSource, PatKind}; @@ -149,43 +149,53 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_LOCK, local.span, "non-binding `let` on a synchronization lock", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`", + |diag| { + diag.help( + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`", + ); + }, ); } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_FUTURE, local.span, "non-binding `let` on a future", - None, - "consider awaiting the future or dropping explicitly with `std::mem::drop`", + |diag| { + diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`"); + }, ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_MUST_USE, local.span, "non-binding `let` on an expression with `#[must_use]` type", - None, - "consider explicitly using expression value", + |diag| { + diag.help("consider explicitly using expression value"); + }, ); } else if is_must_use_func_call(cx, init) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_MUST_USE, local.span, "non-binding `let` on a result of a `#[must_use]` function", - None, - "consider explicitly using function result", + |diag| { + diag.help("consider explicitly using function result"); + }, ); } @@ -204,18 +214,22 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { return; } - span_lint_and_help( + span_lint_and_then( cx, LET_UNDERSCORE_UNTYPED, local.span, "non-binding `let` without a type annotation", - Some(Span::new( - local.pat.span.hi(), - local.pat.span.hi() + BytePos(1), - local.pat.span.ctxt(), - local.pat.span.parent(), - )), - "consider adding a type annotation", + |diag| { + diag.span_help( + Span::new( + local.pat.span.hi(), + local.pat.span.hi() + BytePos(1), + local.pat.span.ctxt(), + local.pat.span.parent(), + ), + "consider adding a type annotation", + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs b/src/tools/clippy/clippy_lints/src/lib.deprecated.rs deleted file mode 100644 index 0d21261822d..00000000000 --- a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -{ - store.register_removed( - "clippy::should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "clippy::extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "clippy::range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "clippy::unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "clippy::assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "clippy::if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "clippy::unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "clippy::unused_collect", - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", - ); - store.register_removed( - "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", - ); - store.register_removed( - "clippy::regex_macro", - "the regex! macro has been removed from the regex crate in 2018", - ); - store.register_removed( - "clippy::find_map", - "this lint has been replaced by `manual_find_map`, a more specific lint", - ); - store.register_removed( - "clippy::filter_map", - "this lint has been replaced by `manual_filter_map`, a more specific lint", - ); - store.register_removed( - "clippy::pub_enum_variant_names", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items", - ); - store.register_removed( - "clippy::wrong_pub_self_convention", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", - ); - store.register_removed( - "clippy::maybe_misused_cfg", - "this lint has been replaced by `unexpected_cfgs`", - ); - store.register_removed( - "clippy::mismatched_target_os", - "this lint has been replaced by `unexpected_cfgs`", - ); -} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a388b6b2eaa..ce13a9afef5 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -13,7 +13,6 @@ #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] #![recursion_limit = "512"] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, @@ -64,13 +63,11 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; -#[cfg(feature = "internal")] -pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; mod declared_lints; -mod renamed_lints; +mod deprecated_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absolute_paths; @@ -372,6 +369,7 @@ mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; mod unused_peekable; +mod unused_result_ok; mod unused_rounding; mod unused_self; mod unused_unit; @@ -495,7 +493,7 @@ pub fn explain(name: &str) -> i32 { // Check if the lint has configuration let mut mdconf = get_configuration_metadata(); let name = name.to_ascii_lowercase(); - mdconf.retain(|cconf| cconf.lints.contains(&name)); + mdconf.retain(|cconf| cconf.lints.contains(&&*name)); if !mdconf.is_empty() { println!("### Configuration for {}:\n", info.lint.name_lower()); for conf in mdconf { @@ -531,10 +529,14 @@ fn register_categories(store: &mut rustc_lint::LintStore) { /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - register_removed_non_tool_lints(store); register_categories(store); - include!("lib.deprecated.rs"); + for (old_name, new_name) in deprecated_lints::RENAMED { + store.register_renamed(old_name, new_name); + } + for (name, reason) in deprecated_lints::DEPRECATED { + store.register_removed(name, reason); + } #[cfg(feature = "internal")] { @@ -669,6 +671,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); + store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); @@ -818,7 +821,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); - store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); + store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); @@ -907,68 +910,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); - store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains)); + store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); // add lints here, do not remove this comment, it's used in `new_lint` } - -#[rustfmt::skip] -fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { - store.register_removed( - "should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "reverse_range_loop", - "this lint is now included in reversed_empty_ranges", - ); -} - -/// Register renamed lints. -/// -/// Used in `./src/driver.rs`. -pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - for (old_name, new_name) in renamed_lints::RENAMED_LINTS { - ls.register_renamed(old_name, new_name); - } -} - -// only exists to let the dogfood integration test works. -// Don't run clippy as an executable directly -#[allow(dead_code)] -fn main() { - panic!("Please use the cargo-clippy executable"); -} diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index b685d1dad1a..259e4d6c08f 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -2,13 +2,13 @@ //! floating-point literal expressions. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -159,63 +159,39 @@ enum WarningType { } impl WarningType { - fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: Span) { + fn lint_and_text(&self) -> (&'static Lint, &'static str, &'static str) { match self { - Self::MistypedLiteralSuffix => span_lint_and_sugg( - cx, + Self::MistypedLiteralSuffix => ( MISTYPED_LITERAL_SUFFIXES, - span, "mistyped literal suffix", "did you mean to write", - suggested_format, - Applicability::MaybeIncorrect, ), - Self::UnreadableLiteral => span_lint_and_sugg( - cx, - UNREADABLE_LITERAL, - span, - "long literal lacking separators", - "consider", - suggested_format, - Applicability::MachineApplicable, - ), - Self::LargeDigitGroups => span_lint_and_sugg( - cx, - LARGE_DIGIT_GROUPS, - span, - "digit groups should be smaller", - "consider", - suggested_format, - Applicability::MachineApplicable, - ), - Self::InconsistentDigitGrouping => span_lint_and_sugg( - cx, + Self::UnreadableLiteral => (UNREADABLE_LITERAL, "long literal lacking separators", "consider"), + Self::LargeDigitGroups => (LARGE_DIGIT_GROUPS, "digit groups should be smaller", "consider"), + Self::InconsistentDigitGrouping => ( INCONSISTENT_DIGIT_GROUPING, - span, "digits grouped inconsistently by underscores", "consider", - suggested_format, - Applicability::MachineApplicable, ), - Self::DecimalRepresentation => span_lint_and_sugg( - cx, + Self::DecimalRepresentation => ( DECIMAL_LITERAL_REPRESENTATION, - span, "integer literal has a better hexadecimal representation", "consider", - suggested_format, - Applicability::MachineApplicable, ), - Self::UnusualByteGroupings => span_lint_and_sugg( - cx, + Self::UnusualByteGroupings => ( UNUSUAL_BYTE_GROUPINGS, - span, "digits of hex, binary or octal literal not in groups of equal size", "consider", - suggested_format, - Applicability::MachineApplicable, ), - }; + } + } + + fn display(&self, num_lit: &NumericLiteral<'_>, cx: &EarlyContext<'_>, span: Span) { + let (lint, message, try_msg) = self.lint_and_text(); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, lint, span, message, |diag| { + diag.span_suggestion(span, try_msg, num_lit.format(), Applicability::MaybeIncorrect); + }); } } @@ -293,7 +269,7 @@ impl LiteralDigitGrouping { WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true, }; if should_warn { - warning_type.display(num_lit.format(), cx, span); + warning_type.display(&num_lit, cx, span); } } } @@ -346,11 +322,14 @@ impl LiteralDigitGrouping { } } *part = main_part; - let mut sugg = num_lit.format(); - sugg.push('_'); - sugg.push(missing_char); - sugg.push_str(last_group); - WarningType::MistypedLiteralSuffix.display(sugg, cx, span); + let (lint, message, try_msg) = WarningType::MistypedLiteralSuffix.lint_and_text(); + span_lint_and_then(cx, lint, span, message, |diag| { + let mut sugg = num_lit.format(); + sugg.push('_'); + sugg.push(missing_char); + sugg.push_str(last_group); + diag.span_suggestion(span, try_msg, sugg, Applicability::MaybeIncorrect); + }); false } else { true @@ -471,7 +450,7 @@ impl DecimalLiteralRepresentation { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, span); + warning_type.display(&num_lit, cx, span); }); } } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs index f0ee64d714e..73a23615c2d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs @@ -2,6 +2,7 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_block, is_integer_const}; +use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::{Expr, Pat}; @@ -17,6 +18,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, + label: Option<Label>, ) { // Look for variables that are incremented once per loop iteration. let mut increment_visitor = IncrementVisitor::new(cx); @@ -34,7 +36,7 @@ pub(super) fn check<'tcx>( { let mut applicability = Applicability::MaybeIncorrect; let span = expr.span.with_hi(arg.span.hi()); - + let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); let int_name = match ty.map(Ty::kind) { // usize or inferred Some(ty::Uint(UintTy::Usize)) | None => { @@ -45,7 +47,7 @@ pub(super) fn check<'tcx>( format!("the variable `{name}` is used as a loop counter"), "consider using", format!( - "for ({name}, {}) in {}.enumerate()", + "{loop_label}for ({name}, {}) in {}.enumerate()", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), @@ -68,7 +70,7 @@ pub(super) fn check<'tcx>( span, "consider using", format!( - "for ({name}, {}) in (0_{int_name}..).zip({})", + "{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index 6922533fbe9..185d834beca 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -1,8 +1,9 @@ use super::FOR_KV_MAP; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; +use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -40,13 +41,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx format!("you seem to want to iterate on a map's {kind}s"), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); - multispan_sugg( - diag, + diag.multipart_suggestion( "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), ], + Applicability::MachineApplicable, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs index b00a082bb8c..57434f35544 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{match_def_path, paths, SpanlessEq}; use rustc_errors::Applicability; @@ -38,11 +38,10 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>, }; let loop_replacement = format!("while let Some({}) = {}.pop()", pat, snippet(cx, receiver_span, "..")); - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "consider using a `while..let` loop", + vec![(loop_span, loop_replacement), (pop_span, pop_replacement)], Applicability::MachineApplicable, - [(loop_span, loop_replacement), (pop_span, pop_replacement)], ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 7c2a8098af2..92ccc0cc0a1 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -25,6 +25,7 @@ mod while_let_on_iterator; use clippy_config::msrvs::Msrv; use clippy_config::Conf; use clippy_utils::higher; +use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -448,7 +449,7 @@ declare_clippy_lint! { #[clippy::version = "1.80.0"] pub WHILE_FLOAT, nursery, - "while loops comaparing floating point values" + "while loops comparing floating point values" } declare_clippy_lint! { @@ -760,6 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { body, loop_id, span, + label, }) = for_loop { // we don't want to check expanded macros @@ -768,7 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if body.span.from_expansion() { return; } - self.check_for_loop(cx, pat, arg, body, expr, span); + self.check_for_loop(cx, pat, arg, body, expr, span, label); if let ExprKind::Block(block, _) = body.kind { never_loop::check(cx, block, loop_id, span, for_loop.as_ref()); } @@ -808,6 +810,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } impl Loops { + #[allow(clippy::too_many_arguments)] fn check_for_loop<'tcx>( &self, cx: &LateContext<'tcx>, @@ -816,11 +819,12 @@ impl Loops { body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, span: Span, + label: Option<Label>, ) { let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { needless_range_loop::check(cx, pat, arg, body, expr); - explicit_counter_loop::check(cx, pat, arg, body, expr); + explicit_counter_loop::check(cx, pat, arg, body, expr, label); } self.check_for_loop_arg(cx, pat, arg); for_kv_map::check(cx, pat, arg, body); diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index de7ec81bc01..e18e4374667 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -1,11 +1,12 @@ use super::NEEDLESS_RANGE_LOOP; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; @@ -145,8 +146,7 @@ pub(super) fn check<'tcx>( arg.span, format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { - multispan_sugg( - diag, + diag.multipart_suggestion( "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, <item>)", ident.name)), @@ -155,6 +155,7 @@ pub(super) fn check<'tcx>( format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], + Applicability::HasPlaceholders, ); }, ); @@ -171,10 +172,10 @@ pub(super) fn check<'tcx>( arg.span, format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { - multispan_sugg( - diag, + diag.multipart_suggestion( "consider using an iterator", vec![(pat.span, "<item>".to_string()), (arg.span, repl)], + Applicability::HasPlaceholders, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index 40ccfec02be..51e21aa9734 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,8 @@ use super::UNUSED_ENUMERATE_INDEX; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{pat_is_wild, sugg}; +use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; @@ -28,13 +29,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ "you seem to use `.enumerate()` and immediately discard the index", |diag| { let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); - multispan_sugg( - diag, + diag.multipart_suggestion( "remove the `.enumerate()` call", vec![ (pat.span, snippet(cx, elem.span, "..").into_owned()), (arg.span, base_iter.to_string()), ], + Applicability::MachineApplicable, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs index e7b3a2c4973..cc1bd5929d0 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs @@ -1,5 +1,5 @@ use super::WHILE_IMMUTABLE_CONDITION; -use clippy_utils::consts::constant; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use rustc_hir::def::{DefKind, Res}; @@ -10,7 +10,7 @@ use rustc_lint::LateContext; use std::ops::ControlFlow; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - if constant(cx, cx.typeck_results(), cond).is_some() { + if ConstEvalCtxt::new(cx).eval(cond).is_some() { // A pure constant condition (e.g., `while false`) is not linted. return; } diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 194dd4752f9..c171fa1c622 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -14,7 +14,7 @@ use rustc_span::symbol::sym; use rustc_span::Symbol; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::WhileLet { if_then, let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) + if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) @@ -27,6 +27,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && !uses_iter(cx, &iter_expr_struct, if_then) { let mut applicability = Applicability::MachineApplicable; + + let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); + let loop_var = if let Some(some_pat) = some_pat.first() { if is_refutable(cx, some_pat) { // Refutable patterns don't work with for loops. @@ -57,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { expr.span.with_hi(let_expr.span.hi()), "this loop could be written as a `for` loop", "try", - format!("for {loop_var} in {iterator}{by_ref}"), + format!("{loop_label}for {loop_var} in {iterator}{by_ref}"), applicability, ); } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index a79ad018a04..4123c933660 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -1,13 +1,13 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, MaybePath, }; use itertools::Itertools; @@ -122,8 +122,9 @@ impl<'tcx> ClampSuggestion<'tcx> { if max_type != min_type { return false; } - if let Some(max) = constant(cx, cx.typeck_results(), self.params.max) - && let Some(min) = constant(cx, cx.typeck_results(), self.params.min) + let ecx = ConstEvalCtxt::new(cx); + if let Some(max) = ecx.eval(self.params.max) + && let Some(min) = ecx.eval(self.params.min) && let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max) { ord != Ordering::Greater @@ -146,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { if !self.msrv.meets(msrvs::CLAMP) { return; } - if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { + if !expr.span.from_expansion() && !is_in_const_context(cx) { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -159,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) { + if !self.msrv.meets(msrvs::CLAMP) || is_in_const_context(cx) { return; } for suggestion in is_two_if_pattern(cx, block) { diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 03416ba96de..6bdc79129a9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{is_from_proc_macro, path_to_local}; @@ -95,8 +95,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { || cx.tcx.features().declared(sym!(const_float_classify)) ) && let [first, second, const_1, const_2] = exprs - && let Some(const_1) = constant(cx, cx.typeck_results(), const_1) - && let Some(const_2) = constant(cx, cx.typeck_results(), const_2) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(const_1) = ecx.eval(const_1) + && let Some(const_2) = ecx.eval(const_2) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index a9f21d34e4c..31c37c3bc3b 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators}; +use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { + if is_in_const_context(cx) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { return; } diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index 78a750f0dcd..6cf5d272d7d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -1,9 +1,9 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::{in_constant, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && add_rhs.span.ctxt() == ctxt && !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::REM_EUCLID) - && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id)) + && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !is_in_const_context(cx)) && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind @@ -117,7 +117,7 @@ fn check_for_either_unsigned_int_constant<'a>( } fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> { - let int_const = constant_full_int(cx, cx.typeck_results(), expr)?; + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/src/tools/clippy/clippy_lints/src/manual_rotate.rs b/src/tools/clippy/clippy_lints/src/manual_rotate.rs index a517a4d5075..07537fc65c0 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rotate.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rotate.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg; use rustc_errors::Applicability; @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = constant(cx, cx.typeck_results(), r)?; + let const_expr = ConstEvalCtxt::new(cx).eval(r)?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs index 429ee2637c2..b24a0f4695a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs +++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::{expr_or_init, in_constant, std_or_core}; +use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { && BinOpKind::Mul == op.node && !expr.span.from_expansion() // Does not apply inside const because size_of_val is not cost in stable. - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) && let Some(receiver) = simplify(cx, left, right) { let ctxt = expr.span.ctxt(); diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 686ecccf829..85cabd2800a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -1,11 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; @@ -14,6 +15,7 @@ use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::Span; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -108,19 +110,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { format!("stripping a {kind_word} manually"), |diag| { diag.span_note(test_span, format!("the {kind_word} was tested here")); - multispan_sugg( - diag, + diag.multipart_suggestion( format!("try using the `strip_{kind_word}` method"), - vec![( + iter::once(( test_span, format!( "if let Some(<stripped>) = {}.strip_{kind_word}({}) ", snippet(cx, target_arg.span, ".."), snippet(cx, pattern.span, "..") ), - )] - .into_iter() - .chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))), + )) + .chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))) + .collect(), + Applicability::HasPlaceholders, ); }, ); @@ -145,7 +147,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E // Returns the length of the `expr` if it's a constant string or char. fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { - let value = constant(cx, cx.typeck_results(), expr)?; + let value = ConstEvalCtxt::new(cx).eval(expr)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), @@ -183,9 +185,9 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> { } } -// Find expressions where `target` is stripped using the length of `pattern`. -// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` -// method. +/// Find expressions where `target` is stripped using the length of `pattern`. +/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` +/// method. fn find_stripping<'tcx>( cx: &LateContext<'tcx>, strip_kind: StripKind, diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index f1acc4b2174..8f8390b6f3f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -10,7 +10,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment}; +use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment}; declare_clippy_lint! { /// ### What it does @@ -174,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) && !expr.span.from_expansion() - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) { handle(cx, if_let_or_match, expr); } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 85a08f81c2f..2d4c8daf5cb 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::constant_simple; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -69,7 +69,7 @@ fn check_and_lint<'tcx>( && let Some(ty_name) = find_type_name(cx, ty) && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span) && let Some(indent) = indent_of(cx, expr.span) - && constant_simple(cx, cx.typeck_results(), else_expr).is_some() + && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() { lint(cx, expr, let_expr, ty_name, or_body_snippet, indent); } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs index aba4c85c59e..5445ee1f042 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use core::iter::once; @@ -54,7 +54,11 @@ where span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { if !expr.span.from_expansion() { - multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs)); + diag.multipart_suggestion( + msg, + first_sugg.chain(remaining_suggs).collect(), + Applicability::MachineApplicable, + ); } }); } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 8d22ceb47f8..91e40e4275c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; @@ -148,23 +148,27 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { Applicability::MaybeIncorrect, ), variants => { - let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { - suggestions.push("_".into()); - "wildcard matches known variants and will also match future added variants" + let (message, add_wildcard) = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { + ( + "wildcard matches known variants and will also match future added variants", + true, + ) } else { - "wildcard match will also match any future added variants" + ("wildcard match will also match any future added variants", false) }; - span_lint_and_sugg( - cx, - WILDCARD_ENUM_MATCH_ARM, - wildcard_span, - message, - "try", - suggestions.join(" | "), - Applicability::MaybeIncorrect, - ); + span_lint_and_then(cx, WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, |diag| { + let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); + if add_wildcard { + suggestions.push("_".into()); + } + diag.span_suggestion( + wildcard_span, + "try", + suggestions.join(" | "), + Applicability::MaybeIncorrect, + ); + }); }, }; } @@ -176,9 +180,8 @@ enum CommonPrefixSearcher<'a> { } impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { - match path { - [path @ .., _] => self.with_prefix(path), - [] => (), + if let [path @ .., _] = path { + self.with_prefix(path); } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index 310675d01a2..d0d2025878e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt}; +use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, sym}; @@ -11,7 +11,7 @@ use super::MATCH_WILD_ERR_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) { // `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now - if in_constant(cx, ex.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index cec3504ed8d..cf5377e0725 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -27,7 +27,7 @@ mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::source::walk_span_to_context; -use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; +use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -1069,7 +1069,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_str_case_mismatch::check(cx, ex, arms); redundant_guards::check(cx, arms, &self.msrv); - if !in_constant(cx, expr.hir_id) { + if !is_in_const_context(cx) { manual_unwrap_or::check_match(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); manual_filter::check_match(cx, ex, arms, expr); @@ -1098,7 +1098,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { else_expr, ); } - if !in_constant(cx, expr.hir_id) { + if !is_in_const_context(cx) { manual_unwrap_or::check_if_let( cx, expr, diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 45b375dbe3d..71f211be925 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt}; +use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -33,22 +33,20 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { - let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, - None => { - let min_val_const = ty.numeric_min_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? - }, + let lhs_const = if let Some(lhs) = lhs { + ConstEvalCtxt::new(cx).eval(lhs)? + } else { + let min_val_const = ty.numeric_min_val(cx.tcx)?; + mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; - let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, - None => { - let max_val_const = ty.numeric_max_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? - }, + let rhs_const = if let Some(rhs) = rhs { + ConstEvalCtxt::new(cx).eval(rhs)? + } else { + let max_val_const = ty.numeric_max_val(cx.tcx)?; + mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? }; - let lhs_val = lhs_const.int_value(cx, ty)?; - let rhs_val = rhs_const.int_value(cx, ty)?; + let lhs_val = lhs_const.int_value(cx.tcx, ty)?; + let rhs_val = rhs_const.int_value(cx.tcx, ty)?; let rhs_bound = match range_end { RangeEnd::Included => EndBound::Included(rhs_val), RangeEnd::Excluded => EndBound::Excluded(rhs_val), @@ -60,7 +58,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) } if let PatKind::Lit(value) = pat.kind { - let value = constant_full_int(cx, cx.typeck_results(), value)?; + let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index c2c0fbf439d..8222c3057f7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{in_constant, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -116,7 +116,7 @@ fn check_method_calls<'tcx>( // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] - if ty.is_str() && !in_constant(cx, if_expr.hir_id) { + if ty.is_str() && !is_in_const_context(cx) { r#""""#.into() } else if slice_like { "[]".into() diff --git a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 316b2f63e4e..2154cd5b24a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{Pat, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -15,13 +15,15 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { && fields.len() == def.non_enum_variant().fields.len() && !def.non_enum_variant().is_field_list_non_exhaustive() { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, REST_PAT_IN_FULLY_BOUND_STRUCTS, pat.span, "unnecessary use of `..` pattern in struct binding. All fields were already bound", - None, - "consider removing `..` from this binding", + |diag| { + diag.help("consider removing `..` from this binding"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 99fdbcff890..24dea03601c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,12 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet, SpanRangeExt}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; -use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; -use core::cmp::max; +use clippy_utils::ty::implements_trait; +use clippy_utils::{ + is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, +}; +use core::ops::ControlFlow; +use rustc_arena::DroplessArena; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_pat, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{sym, Span}; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -27,52 +32,58 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { } #[rustfmt::skip] -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { - if expr.span.from_expansion() { - // Don't lint match expressions present in - // macro_rules! block - return; - } - if let PatKind::Or(..) = arms[0].pat.kind { - // don't lint for or patterns for now, this makes - // the lint noisy in unnecessary situations - return; - } - let els = arms[1].body; - let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) { + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + && !expr.span.from_expansion() + // don't lint for or patterns for now, this makes + // the lint noisy in unnecessary situations + && !matches!(arm1.pat.kind, PatKind::Or(..)) + { + let els = if is_unit_expr(peel_blocks(arm2.body)) && !empty_arm_has_comment(cx, arm2.body.span) { None - } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind { - if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { + } else if let ExprKind::Block(block, _) = arm2.body.kind { + if matches!((block.stmts, block.expr), ([], Some(_)) | ([_], None)) { // single statement/expr "else" block, don't lint return; } // block with 2+ statements or 1 expr and 1+ statement - Some(els) + Some(arm2.body) } else { // not a block or an empty block w/ comments, don't lint return; }; - let ty = cx.typeck_results().expr_ty(ex); - if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) && - (check_single_pattern(arms) || check_opt_like(cx, arms, ty)) { - report_single_pattern(cx, ex, arms, expr, els); + let typeck = cx.typeck_results(); + if *typeck.expr_ty(ex).peel_refs().kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) { + let mut v = PatVisitor { + typeck, + has_enum: false, + }; + if v.visit_pat(arm2.pat).is_break() { + return; + } + if v.has_enum { + let cx = PatCtxt { + tcx: cx.tcx, + param_env: cx.param_env, + typeck, + arena: DroplessArena::default(), + }; + let mut state = PatState::Other; + if !(state.add_pat(&cx, arm2.pat) || state.add_pat(&cx, arm1.pat)) { + // Don't lint if the pattern contains an enum which doesn't have a wild match. + return; + } + } + + report_single_pattern(cx, ex, arm1, expr, els); } } } -fn check_single_pattern(arms: &[Arm<'_>]) -> bool { - is_wild(arms[1].pat) -} - -fn report_single_pattern( - cx: &LateContext<'_>, - ex: &Expr<'_>, - arms: &[Arm<'_>], - expr: &Expr<'_>, - els: Option<&Expr<'_>>, -) { +fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let ctxt = expr.span.ctxt(); let mut app = Applicability::MachineApplicable; @@ -80,9 +91,9 @@ fn report_single_pattern( format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); - let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); + let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind - && let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)) + && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() && (ty.is_integral() @@ -114,17 +125,17 @@ fn report_single_pattern( snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), - snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + snippet(cx, arm.pat.span, ".."), + expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) } else { let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( "if let {} = {} {}{els_str}", - snippet(cx, arms[0].pat.span, ".."), + snippet(cx, arm.pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) }; @@ -132,106 +143,227 @@ fn report_single_pattern( span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); } -fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool { - // We don't want to lint if the second arm contains an enum which could - // have more variants in the future. - form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) +struct PatVisitor<'tcx> { + typeck: &'tcx TypeckResults<'tcx>, + has_enum: bool, } - -/// Returns `true` if all of the types in the pattern are enums which we know -/// won't be expanded in the future -fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool { - let mut paths_and_types = Vec::new(); - collect_pat_paths(&mut paths_and_types, cx, pat, ty); - paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty)) +impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> { + type Result = ControlFlow<()>; + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) -> Self::Result { + if matches!(pat.kind, PatKind::Binding(..)) { + ControlFlow::Break(()) + } else { + self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum); + walk_pat(self, pat) + } + } } -/// Returns `true` if the given type is an enum we know won't be expanded in the future -fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - // list of candidate `Enum`s we know will never get any more members - let candidates = [sym::Cow, sym::Option, sym::Result]; +/// The context needed to manipulate a `PatState`. +struct PatCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + arena: DroplessArena, +} - for candidate_ty in candidates { - if is_type_diagnostic_item(cx, ty, candidate_ty) { - return true; +/// State for tracking whether a match can become non-exhaustive by adding a variant to a contained +/// enum. +/// +/// This treats certain std enums as if they will never be extended. +enum PatState<'a> { + /// Either a wild match or an uninteresting type. Uninteresting types include: + /// * builtin types (e.g. `i32` or `!`) + /// * A struct/tuple/array containing only uninteresting types. + /// * A std enum containing only uninteresting types. + Wild, + /// A std enum we know won't be extended. Tracks the states of each variant separately. + /// + /// This is not used for `Option` since it uses the current pattern to track it's state. + StdEnum(&'a mut [PatState<'a>]), + /// Either the initial state for a pattern or a non-std enum. There is currently no need to + /// distinguish these cases. + /// + /// For non-std enums there's no need to track the state of sub-patterns as the state of just + /// this pattern on it's own is enough for linting. Consider two cases: + /// * This enum has no wild match. This case alone is enough to determine we can lint. + /// * This enum has a wild match and therefore all sub-patterns also have a wild match. + /// + /// In both cases the sub patterns are not needed to determine whether to lint. + Other, +} +impl<'a> PatState<'a> { + /// Adds a set of patterns as a product type to the current state. Returns whether or not the + /// current state is a wild match after the merge. + fn add_product_pat<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>, + ) -> bool { + // Ideally this would actually keep track of the state separately for each pattern. Doing so would + // require implementing something similar to exhaustiveness checking which is a significant increase + // in complexity. + // + // For now treat this as a wild match only if all the sub-patterns are wild + let is_wild = pats.into_iter().all(|p| { + let mut state = Self::Other; + state.add_pat(cx, p) + }); + if is_wild { + *self = Self::Wild; } + is_wild } - false -} -/// Collects types from the given pattern -fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) { - match pat.kind { - PatKind::Tuple(inner, _) => inner.iter().for_each(|p| { - let p_ty = cx.typeck_results().pat_ty(p); - collect_pat_paths(acc, cx, p, p_ty); - }), - PatKind::TupleStruct(..) | PatKind::Binding(BindingMode::NONE, .., None) | PatKind::Path(_) => { - acc.push(ty); - }, - _ => {}, + /// Attempts to get the state for the enum variant, initializing the current state if necessary. + fn get_std_enum_variant<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + adt: AdtDef<'tcx>, + path: &'tcx QPath<'_>, + hir_id: HirId, + ) -> Option<(&mut Self, &'tcx VariantDef)> { + let states = match self { + Self::Wild => return None, + Self::Other => { + *self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other))); + let Self::StdEnum(x) = self else { + unreachable!(); + }; + x + }, + Self::StdEnum(x) => x, + }; + let i = match cx.typeck.qpath_res(path, hir_id) { + Res::Def(DefKind::Ctor(..), id) => adt.variant_index_with_ctor_id(id), + Res::Def(DefKind::Variant, id) => adt.variant_index_with_id(id), + _ => return None, + }; + Some((&mut states[i.as_usize()], adt.variant(i))) } -} -/// Returns true if the given arm of pattern matching contains wildcard patterns. -fn contains_only_wilds(pat: &Pat<'_>) -> bool { - match pat.kind { - PatKind::Wild => true, - PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds), - _ => false, + fn check_all_wild_enum(&mut self) -> bool { + if let Self::StdEnum(states) = self + && states.iter().all(|s| matches!(s, Self::Wild)) + { + *self = Self::Wild; + true + } else { + false + } } -} -/// Returns true if the given patterns forms only exhaustive matches that don't contain enum -/// patterns without a wildcard. -fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool { - match (&left.kind, &right.kind) { - (PatKind::Wild, _) | (_, PatKind::Wild) => true, - (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { - // We don't actually know the position and the presence of the `..` (dotdot) operator - // in the arms, so we need to evaluate the correct offsets here in order to iterate in - // both arms at the same time. - let left_pos = left_pos.as_opt_usize(); - let right_pos = right_pos.as_opt_usize(); - let len = max( - left_in.len() + usize::from(left_pos.is_some()), - right_in.len() + usize::from(right_pos.is_some()), - ); - let mut left_pos = left_pos.unwrap_or(usize::MAX); - let mut right_pos = right_pos.unwrap_or(usize::MAX); - let mut left_dot_space = 0; - let mut right_dot_space = 0; - for i in 0..len { - let mut found_dotdot = false; - if i == left_pos { - left_dot_space += 1; - if left_dot_space < len - left_in.len() { - left_pos += 1; - } - found_dotdot = true; - } - if i == right_pos { - right_dot_space += 1; - if right_dot_space < len - right_in.len() { - right_pos += 1; - } - found_dotdot = true; - } - if found_dotdot { - continue; + #[expect(clippy::similar_names)] + fn add_struct_pats<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + pat: &'tcx Pat<'tcx>, + path: &'tcx QPath<'tcx>, + single_pat: Option<&'tcx Pat<'tcx>>, + pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>, + ) -> bool { + let ty::Adt(adt, _) = *cx.typeck.pat_ty(pat).kind() else { + // Should never happen + *self = Self::Wild; + return true; + }; + if adt.is_struct() { + return if let Some(pat) = single_pat + && adt.non_enum_variant().fields.len() == 1 + { + self.add_pat(cx, pat) + } else { + self.add_product_pat(cx, pats) + }; + } + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => { + if let Some(pat) = single_pat { + self.add_pat(cx, pat) + } else { + *self = Self::Wild; + true } - if !contains_only_wilds(&left_in[i - left_dot_space]) - && !contains_only_wilds(&right_in[i - right_dot_space]) + }, + Some(sym::Result | sym::Cow) => { + let Some((state, variant)) = self.get_std_enum_variant(cx, adt, path, pat.hir_id) else { + return matches!(self, Self::Wild); + }; + let is_wild = if let Some(pat) = single_pat + && variant.fields.len() == 1 { - return false; - } - } - true - }, - (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right), - (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => { - pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds) - }, - _ => false, + state.add_pat(cx, pat) + } else { + state.add_product_pat(cx, pats) + }; + is_wild && self.check_all_wild_enum() + }, + _ => matches!(self, Self::Wild), + } + } + + /// Adds the pattern into the current state. Returns whether or not the current state is a wild + /// match after the merge. + #[expect(clippy::similar_names)] + fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { + match pat.kind { + PatKind::Path(_) + if match *cx.typeck.pat_ty(pat).peel_refs().kind() { + ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), + ty::Tuple(tys) => !tys.is_empty(), + ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1), + ty::Slice(..) => true, + _ => false, + } => + { + matches!(self, Self::Wild) + }, + + // Patterns for things which can only contain a single sub-pattern. + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { + self.add_pat(cx, pat) + }, + PatKind::Tuple([sub_pat], pos) + if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + { + self.add_pat(cx, sub_pat) + }, + PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat]) + if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind() + && len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) => + { + self.add_pat(cx, sub_pat) + }, + + PatKind::Or(pats) => pats.iter().any(|p| self.add_pat(cx, p)), + PatKind::Tuple(pats, _) => self.add_product_pat(cx, pats), + PatKind::Slice(head, _, tail) => self.add_product_pat(cx, head.iter().chain(tail)), + + PatKind::TupleStruct(ref path, pats, _) => self.add_struct_pats( + cx, + pat, + path, + if let [pat] = pats { Some(pat) } else { None }, + pats.iter(), + ), + PatKind::Struct(ref path, pats, _) => self.add_struct_pats( + cx, + pat, + path, + if let [pat] = pats { Some(pat.pat) } else { None }, + pats.iter().map(|p| p.pat), + ), + + PatKind::Wild + | PatKind::Binding(_, _, _, None) + | PatKind::Lit(_) + | PatKind::Range(..) + | PatKind::Path(_) + | PatKind::Never + | PatKind::Err(_) => { + *self = PatState::Wild; + true + }, + } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs index dd489fc250b..b7ffa8b8a78 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; @@ -48,29 +48,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine return; }; - let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); - let mut applicability = Applicability::MachineApplicable; - let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { - "" // already returns - } else { - "return " - }; - let suggestion = if err_ty == expr_err_ty { - format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") - } else { - format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") - }; - - span_lint_and_sugg( + span_lint_and_then( cx, TRY_ERR, expr.span, "returning an `Err(_)` with the `?` operator", - "try", - suggestion, - applicability, + |diag| { + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); + let mut applicability = Applicability::MachineApplicable; + let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; + let suggestion = if err_ty == expr_err_ty { + format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") + } else { + format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") + }; + diag.span_suggestion(expr.span, "try", suggestion, applicability); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index 20f3722e173..2a8a5fcc3b6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,5 +1,5 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::visitors::find_all_ret_expressions; @@ -136,15 +136,16 @@ impl BindInsteadOfMap { return false; }; span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, |diag| { - multispan_sugg_with_applicability( - diag, - "try", + diag.multipart_suggestion( + format!("use `{}` instead", self.good_method_name), + std::iter::once((span, self.good_method_name.into())) + .chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ) + .collect(), Applicability::MachineApplicable, - std::iter::once((span, self.good_method_name.into())).chain( - suggs - .into_iter() - .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), - ), ); }); true diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs index 926bd06bacb..e0826b53004 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir as hir; @@ -29,19 +29,22 @@ pub(super) fn check( sym::RcWeak | sym::ArcWeak => "Weak", _ => return, }; - - // Sometimes unnecessary ::<_> after Rc/Arc/Weak - let mut app = Applicability::Unspecified; - let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; - - span_lint_and_sugg( + span_lint_and_then( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", - "try", - format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - app, + |diag| { + // Sometimes unnecessary ::<_> after Rc/Arc/Weak + let mut app = Applicability::Unspecified; + let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), + app, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs index eab536b88a5..2ab0401947c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; @@ -33,6 +33,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr span = expr.span; } let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); - let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); - span_lint_and_help(cx, FILETYPE_IS_FILE, span, lint_msg, None, help_msg); + + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, FILETYPE_IS_FILE, span, lint_msg, |diag| { + diag.help(format!("use `{help_unary}FileType::is_dir()` instead")); + }); } diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs index 455274a4428..c6285c87a26 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs @@ -1,5 +1,5 @@ use super::utils::derefs_to_slice; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; @@ -19,9 +19,7 @@ pub(super) fn check<'tcx>( ) { // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`, // because they do not implement `IndexMut` - let mut applicability = Applicability::MachineApplicable; let expr_ty = cx.typeck_results().expr_ty(recv); - let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability); let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { "slice" } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) { @@ -58,24 +56,34 @@ pub(super) fn check<'tcx>( }; let mut_str = if is_mut { "_mut" } else { "" }; - let borrow_str = if !needs_ref { - "" - } else if is_mut { - "&mut " - } else { - "&" - }; - span_lint_and_sugg( + span_lint_and_then( cx, GET_UNWRAP, span, - format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), - "try", - format!( - "{borrow_str}{}[{get_args_str}]", - snippet_with_applicability(cx, recv.span, "..", &mut applicability) - ), - applicability, + format!("called `.get{mut_str}().unwrap()` on a {caller_type}"), + |diag| { + let mut applicability = Applicability::MachineApplicable; + let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability); + + let borrow_str = if !needs_ref { + "" + } else if is_mut { + "&mut " + } else { + "&" + }; + + diag.span_suggestion_with_style( + span, + "using `[]` is clearer and more concise", + format!( + "{borrow_str}{}[{get_args_str}]", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + rustc_errors::SuggestionStyle::ShowAlways, + ); + }, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index c510cd915d0..519091406cc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; -use clippy_utils::{is_diag_item_method, is_diag_trait_item}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) - && let (input_type, ref_count) = peel_mid_ty_refs(input_type) + && let (input_type, ref_count) = peel_middle_ty_refs(input_type) && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs index 210e4ae0a7b..22d896433f0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -2,7 +2,7 @@ use super::IS_DIGIT_ASCII_RADIX; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index d921b7ea14f..cc82f6cfd63 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::constant_is_empty; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::{find_binding_init, path_to_local}; use rustc_hir::{Expr, HirId}; @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if !receiver.span.eq_ctxt(init_expr.span) { return; } - if let Some(init_is_empty) = constant_is_empty(cx, init_expr) { + if let Some(init_is_empty) = ConstEvalCtxt::new(cx).eval_is_empty(init_expr) { span_lint( cx, CONST_IS_EMPTY, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 05e77386128..33de3b87abc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -1,15 +1,12 @@ -#![allow(unused_imports)] - use super::ITER_KV_MAP; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::pat_is_wild; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{pat_is_wild, sugg}; -use rustc_hir::{BindingMode, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind}; -use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_hir::{Body, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; /// lint use of: /// diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs index 262a57ab591..9ff6eaa3487 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs index 6b696b42a69..39e440e784f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| { + && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { if let Constant::Int(arg) = constant { Some(arg) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs index b631cd00cda..9b358235a40 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir as hir; @@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { if is_trait_method(cx, expr, sym::Iterator) { - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) { + if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( cx, ITERATOR_STEP_BY_ZERO, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index a5ba5e5d891..08ce7e204dd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -2,10 +2,10 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; -use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks}; +use clippy_utils::{is_diag_trait_item, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -114,7 +114,7 @@ fn handle_path( recv: &hir::Expr<'_>, ) { if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() - && match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + && cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) { // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option` // and `Result`. diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs index fbb83c8ce56..162f0ac564d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -22,13 +22,17 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { { // span the area of the closure capture and warn that the // original error will be thrown away - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, MAP_ERR_IGNORE, fn_decl_span, "`map_err(|_|...` wildcard pattern discards the original error", - None, - "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + |diag| { + diag.help( + "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index aa4cad9342a..1d7b10fe8f0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -133,7 +133,7 @@ mod zst_offset; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; @@ -749,7 +749,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// (0_i32..10) /// .filter(|n| n.checked_add(1).is_some()) /// .map(|n| n.checked_add(1).unwrap()); @@ -757,7 +756,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #[allow(unused)] /// (0_i32..10).filter_map(|n| n.checked_add(1)); /// ``` #[clippy::version = "1.51.0"] @@ -850,7 +848,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// let vec = vec![1]; /// vec.iter().find(|x| **x == 0).is_some(); /// @@ -862,7 +859,6 @@ declare_clippy_lint! { /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); /// - /// # #[allow(unused)] /// !"hello world".contains("world"); /// ``` #[clippy::version = "pre 1.29.0"] @@ -1505,7 +1501,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #[allow(unused)] /// (0..3).fold(false, |acc, x| acc || x > 2); /// ``` /// @@ -1897,7 +1892,9 @@ declare_clippy_lint! { /// trait. /// /// ### Why is this bad? - /// It is recommended style to use collect. See + /// If it's needed to create a collection from the contents of an iterator, the `Iterator::collect(_)` + /// method is preferred. However, when it's needed to specify the container type, + /// `Vec::from_iter(_)` can be more readable than using a turbofish (e.g. `_.collect::<Vec<_>>()`). See /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) /// /// ### Example @@ -1916,6 +1913,14 @@ declare_clippy_lint! { /// /// assert_eq!(v, vec![5, 5, 5, 5, 5]); /// ``` + /// but prefer to use + /// ```no_run + /// let numbers: Vec<i32> = FromIterator::from_iter(1..=5); + /// ``` + /// instead of + /// ```no_run + /// let numbers = (1..=5).collect::<Vec<_>>(); + /// ``` #[clippy::version = "1.49.0"] pub FROM_ITER_INSTEAD_OF_COLLECT, pedantic, @@ -2008,13 +2013,11 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #[allow(unused)] /// "Hello".bytes().nth(3); /// ``` /// /// Use instead: /// ```no_run - /// # #[allow(unused)] /// "Hello".as_bytes().get(3); /// ``` #[clippy::version = "1.52.0"] @@ -2059,7 +2062,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// let some_vec = vec![0, 1, 2, 3]; /// /// some_vec.iter().count(); @@ -3656,7 +3658,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); /// owned_string.as_str().as_bytes() /// # ; @@ -3664,7 +3665,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); /// owned_string.as_bytes() /// # ; @@ -4915,13 +4915,13 @@ impl Methods { str_split::check(cx, expr, recv, arg); }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index d425b505a76..cbeb48b6cc3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -151,7 +151,7 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, S cx, NONSENSICAL_OPEN_OPTIONS, prev_span, - format!("the method `{}` is called more than once", &option), + format!("the method `{option}` is called more than once"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs index bb4cdd2a6fa..7837517ed5d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs +++ b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 4f42fb73547..12cabd43cb1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -1,5 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; @@ -301,7 +301,7 @@ fn parse_iter_usage<'tcx>( }; }, ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs index 78851d4122f..86c0a6322b6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::consts::{constant, constant_with_source, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; use clippy_utils::source::snippet; use rustc_errors::Applicability; @@ -20,10 +20,9 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) { let typeck_results = cx.typeck_results(); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = - constant_with_source(cx, typeck_results, recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = - constant_with_source(cx, typeck_results, arg) + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results); + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; @@ -78,9 +77,9 @@ enum Extrema { fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?; + let cv = ConstEvalCtxt::new(cx).eval(expr)?; - match (cv.int_value(cx, ty)?, ty.kind()) { + match (cv.int_value(cx.tcx, ty)?, ty.kind()) { (FullInt::S(i), &ty::Int(ity)) if i == i128::MIN >> (128 - ity.bit_width()?) => Some(Extrema::Minimum), (FullInt::S(i), &ty::Int(ity)) if i == i128::MAX >> (128 - ity.bit_width()?) => Some(Extrema::Maximum), (FullInt::U(i), &ty::Uint(uty)) if i == u128::MAX >> (128 - uty.bit_width()?) => Some(Extrema::Maximum), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 5d899415d77..fed2b128dcf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,12 +3,11 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{ - get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs, -}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, peel_middle_ty_refs, + return_ty, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -120,8 +119,8 @@ fn check_addr_of_expr( }, ] = adjustments[..] && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty) - && let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty) + && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -382,7 +381,7 @@ fn check_other_call_arg<'tcx>( && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) && let Some(input) = fn_sig.inputs().get(i) - && let (input, n_refs) = peel_mid_ty_refs(*input) + && let (input, n_refs) = peel_middle_ty_refs(*input) && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() && let [trait_predicate] = trait_predicates diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs index 8b8a965b9f0..3004d9c4233 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; @@ -97,10 +97,8 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, enumerate_span, "you seem to use `.enumerate()` and immediately discard the index", |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "remove the `.enumerate()` call", - Applicability::MachineApplicable, vec![ (closure_param.span, new_closure_param), ( @@ -108,6 +106,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, String::new(), ), ], + Applicability::MachineApplicable, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index ae2b6e6347e..ebad4ae6ee9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{should_call_clone_as_function, walk_ptrs_ty_depth}; use clippy_utils::{ - get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs, + get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs, }; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -104,7 +104,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool { // We check it's calling the `clone` method of the `Clone` trait. if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() { - match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs index 181b413a182..8ed61637eca 100644 --- a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs +++ b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{Expr, ExprKind, QPath}; @@ -23,6 +23,9 @@ pub(super) fn check<'tcx>( && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) { - span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| { + diag.help(help); + }); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 28068c63473..7384e534ed7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( .collect::<Vec<_>>() .join(" and "); - format!("methods with the following characteristics: ({})", &s) + format!("methods with the following characteristics: ({s})") } else { format!("methods called {}", &conventions[0]) } diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index c3fbca1d560..e95864c6db8 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir::{Expr, ExprKind}; @@ -106,15 +106,11 @@ fn fetch_const<'a, 'tcx>( if args.next().is_some() { return None; } - constant_simple(cx, cx.typeck_results(), first_arg).map_or_else( - || constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)), - |c| { - if constant_simple(cx, cx.typeck_results(), second_arg).is_none() { - // otherwise ignore - Some((m, c, second_arg)) - } else { - None - } - }, - ) + let ecx = ConstEvalCtxt::new(cx); + match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + (Some(c), None) => Some((m, c, second_arg)), + (None, Some(c)) => Some((m, c, first_arg)), + // otherwise ignore + _ => None, + } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs index e0a5e401a50..22467999cd8 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use rustc_span::Span; @@ -12,24 +12,36 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffi // Do not lint when literal is unsuffixed. if !suffix.is_empty() { if lit_snip.as_bytes()[maybe_last_sep_idx] == b'_' { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, SEPARATED_LITERAL_SUFFIX, lit_span, format!("{sugg_type} type suffix should not be separated by an underscore"), - "remove the underscore", - format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + lit_span, + "remove the underscore", + format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), + Applicability::MachineApplicable, + ); + }, ); } else { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNSEPARATED_LITERAL_SUFFIX, lit_span, format!("{sugg_type} type suffix should be separated by an underscore"), - "add an underscore", - format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + lit_span, + "add an underscore", + format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index cb305cf5582..6db03adf44a 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; @@ -21,13 +21,15 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { } } if !pfields.is_empty() && wilds == pfields.len() { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNNEEDED_FIELD_PATTERN, pat.span, "all the struct fields are matched to a wildcard pattern, consider using `..`", - None, - format!("try with `{type_name} {{ .. }}` instead"), + |diag| { + diag.help(format!("try with `{type_name} {{ .. }}` instead")); + }, ); return; } @@ -56,14 +58,15 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { } } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNNEEDED_FIELD_PATTERN, field.span, - "you matched a field with a wildcard pattern, consider using `..` \ - instead", - None, - format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), + "you matched a field with a wildcard pattern, consider using `..` instead", + |diag| { + diag.help(format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", "))); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs index 935ed48dacc..a9ea11f4c2b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs +++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; use rustc_hir::Expr; @@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { }; if let PanicExpn::Empty = panic_expn { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, MISSING_ASSERT_MESSAGE, macro_call.span, "assert without any message", - None, - "consider describing why the failing assert is problematic", + |diag| { + diag.help("consider describing why the failing assert is problematic"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 68c158330ab..052d738b2e7 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -186,7 +186,7 @@ fn already_const(header: hir::FnHeader) -> bool { fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: &Msrv, abi: Abi) -> bool { match abi { Abi::Rust => true, - // `const extern "C"` was stablized after 1.62.0 + // `const extern "C"` was stabilized after 1.62.0 Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_FN), // Rest ABIs are still unstable and need the `const_extern_fn` feature enabled. _ => cx.tcx.features().const_extern_fn, diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs index 85029a5e6a0..7ee746365d1 100644 --- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::macros::span_is_local; -use rustc_hir::def_id::DefIdMap; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::AssocItem; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -68,33 +67,26 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { }) = item.kind && let Some(trait_id) = trait_ref.trait_def_id() { - let mut provided: DefIdMap<&AssocItem> = cx - .tcx - .provided_trait_methods(trait_id) - .map(|assoc| (assoc.def_id, assoc)) + let trait_item_ids: DefIdSet = items + .iter() + .filter_map(|impl_item| impl_item.trait_item_def_id) .collect(); - for impl_item in *items { - if let Some(def_id) = impl_item.trait_item_def_id { - provided.remove(&def_id); - } + for assoc in cx + .tcx + .provided_trait_methods(trait_id) + .filter(|assoc| !trait_item_ids.contains(&assoc.def_id)) + { + span_lint_and_then( + cx, + MISSING_TRAIT_METHODS, + cx.tcx.def_span(item.owner_id), + format!("missing trait method provided by default: `{}`", assoc.name), + |diag| { + diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method"); + }, + ); } - - cx.tcx.with_stable_hashing_context(|hcx| { - for assoc in provided.values_sorted(&hcx, true) { - let source_map = cx.tcx.sess.source_map(); - let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); - - span_lint_and_help( - cx, - MISSING_TRAIT_METHODS, - source_map.guess_head_span(item.span), - format!("missing trait method provided by default: `{}`", assoc.name), - Some(definition_span), - "implement the method", - ); - } - }); } } } diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 9c5a8a0cfcd..6964d8c8dbb 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; @@ -324,13 +324,17 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { if path_to_local_id(expr, self.var) { // Check that this is a read, not a write. if !is_in_assignment_position(self.cx, expr) { - span_lint_and_note( + span_lint_and_then( self.cx, MIXED_READ_WRITE_IN_EXPRESSION, expr.span, format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), - Some(self.write_expr.span), - "whether read occurs before this write depends on evaluation order", + |diag| { + diag.span_note( + self.write_expr.span, + "whether read occurs before this write depends on evaluation order", + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 305499f9da4..e9c5f64a255 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; @@ -121,17 +121,18 @@ impl EarlyLintPass for ModStyle { for folder in &folder_segments { if !mod_folders.contains(folder) { if let Some((file, path)) = file_map.get(folder) { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - span_lint_and_help( + span_lint_and_then( cx, SELF_NAMED_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are required, found `{}`", path.display()), - None, - format!("move `{}` to `{}`", path.display(), correct.display(),), + |diag| { + let mut correct = path.to_path_buf(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); + }, ); } } @@ -161,17 +162,18 @@ fn process_paths_for_mod_files<'a>( /// for code-sharing between tests. fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { if path.ends_with("mod.rs") && !path.starts_with("tests") { - let mut mod_file = path.to_path_buf(); - mod_file.pop(); - mod_file.set_extension("rs"); - - span_lint_and_help( + span_lint_and_then( cx, MOD_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are not allowed, found `{}`", path.display()), - None, - format!("move `{}` to `{}`", path.display(), mod_file.display()), + |diag| { + let mut mod_file = path.to_path_buf(); + mod_file.pop(); + mod_file.set_extension("rs"); + + diag.help(format!("move `{}` to `{}`", path.display(), mod_file.display())); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index 67f9b52c352..32e7fde03b2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::PossibleBorrowerMap; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; @@ -12,6 +12,7 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::{ self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, }; @@ -107,6 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } && let count = needless_borrow_count( cx, + &mut self.possible_borrowers, fn_id, cx.typeck_results().node_args(hir_id), i, @@ -155,9 +157,14 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// The following constraints will be checked: /// * The borrowed expression meets all the generic type's constraints. /// * The generic type appears only once in the functions signature. -/// * The borrowed value is Copy itself OR not a variable (created by a function call) +/// * The borrowed value is: +/// - `Copy` itself, or +/// - the only use of a mutable reference, or +/// - not a variable (created by a function call) +#[expect(clippy::too_many_arguments)] fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, fn_id: DefId, callee_args: ty::GenericArgsRef<'tcx>, arg_index: usize, @@ -232,9 +239,9 @@ fn needless_borrow_count<'tcx>( let referent_ty = cx.typeck_results().expr_ty(referent); - if (!is_copy(cx, referent_ty) && !referent_ty.is_ref()) - && let ExprKind::AddrOf(_, _, inner) = reference.kind - && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) + if !(is_copy(cx, referent_ty) + || referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, reference) + || matches!(referent.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))) { return false; } @@ -337,6 +344,37 @@ fn is_mixed_projection_predicate<'tcx>( } } +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) + && let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let block_data = &mir.basic_blocks[location.block] + && let Some(statement) = block_data.statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.is_indirect_first_projection() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index f2e00cef7e9..a0bbf6b14b2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_self; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; @@ -278,9 +278,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - - multispan_sugg(diag, "consider taking a reference instead", spans); + diag.span_suggestion( + input.span, + "consider taking a reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); }; span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9d326c06eff..8232e69db39 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -273,7 +273,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } let snippet = if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) { - format!("assert!({}.len() > {});", &arr, &func) + format!("assert!({arr}.len() > {func});") } else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 15bb328b446..6915cd40615 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -6,7 +6,7 @@ use std::ptr; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; use clippy_utils::ty::{implements_trait, InteriorMut}; use rustc_hir::def::{DefKind, Res}; @@ -406,7 +406,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Path(qpath) = &expr.kind { // Only lint if we use the const item inside a function. - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 8b8aabe7acc..aadd729f32a 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -243,7 +243,6 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { owner_id, .. }) => { - #[allow(trivial_casts)] if let Node::Item(item) = cx.tcx.parent_hir_node(owner_id.into()) && let Some(trait_ref) = cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 9769da6d3e9..a0de5ea711c 100644 --- a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -3,7 +3,7 @@ use rustc_lint::LateContext; use rustc_middle::ty; use clippy_utils::comparisons::{normalize_comparison, Rel}; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::ty::is_isize_or_usize; @@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>( fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?; + let cv = ConstEvalCtxt::new(cx).eval(expr)?; let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index a7e381be743..bc71a4790b9 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,6 +1,6 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; @@ -162,7 +162,7 @@ impl ArithmeticSideEffects { { return Some(n.get()); } - if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) { return Some(n); } None @@ -200,7 +200,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if constant_simple(cx, cx.typeck_results(), expr).is_some() { + if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { return; } if !matches!( @@ -280,7 +280,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if constant_simple(cx, cx.typeck_results(), receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty(receiver); @@ -308,7 +308,7 @@ impl ArithmeticSideEffects { let hir::UnOp::Neg = un_op else { return; }; - if constant(cx, cx.typeck_results(), un_expr).is_some() { + if ConstEvalCtxt::new(cx).eval(un_expr).is_some() { return; } let ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs index 545e680ce0d..4414056a467 100644 --- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: } fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> { - match constant(cx, cx.typeck_results(), lit)? { + match ConstEvalCtxt::new(cx).eval(lit)? { Constant::Int(n) => Some(n), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs index 7bf9b8ef866..c1317524396 100644 --- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::layout::HasTyCtxt; @@ -20,13 +20,14 @@ use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS}; // Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42` fn comparison_to_const<'tcx>( cx: &LateContext<'tcx>, - typeck: &TypeckResults<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { - match (constant(cx, typeck, left), constant(cx, typeck, right)) { + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + match (ecx.eval(left), ecx.eval(right)) { (Some(_), Some(_)) => None, (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))), diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index ca3112ce5c4..e3029f8438e 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) { let suggested_fn = match (method_path.ident.as_str(), divisor) { ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", diff --git a/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs index 066e08f3bd4..24bfe2b050b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::same_type_and_consts; @@ -34,12 +34,12 @@ fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) fn check_op<'tcx>( cx: &LateContext<'tcx>, - tck: &TypeckResults<'tcx>, + tck: &'tcx TypeckResults<'tcx>, op: &Expr<'tcx>, other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.param_env, tck).eval_simple(op) == Some(Constant::Int(0)) { if different_types(tck, other, parent) { return; } diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 0e5b440c50f..df6e6745596 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_with_source, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; @@ -17,12 +17,14 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { - let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { + let typeck = cx.typeck_results(); + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + let left_is_local = match ecx.eval_with_source(left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) { + let right_is_local = match ecx.eval_with_source(right) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs index 0879dcd9bcd..830be50c8ba 100644 --- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{clip, peel_hir_expr_refs, unsext}; @@ -184,14 +184,13 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Exp && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code && !(cmp == BinOpKind::Shl - && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) - && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) + && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0)) + && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1))) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { - let lhs_const = constant_full_int(cx, cx.typeck_results(), left); - let rhs_const = constant_full_int(cx, cx.typeck_results(), right); - if match (lhs_const, rhs_const) { + let ecx = ConstEvalCtxt::new(cx); + if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -201,7 +200,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool { - if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs index 631d10f4a72..76eba7327cf 100644 --- a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs +++ b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir as hir; use rustc_lint::LateContext; @@ -15,13 +15,9 @@ pub(crate) fn check<'tcx>( && cx.typeck_results().expr_ty(left).is_integral() && cx.typeck_results().expr_ty(right).is_integral() { - span_lint_and_help( - cx, - INTEGER_DIVISION, - expr.span, - "integer division", - None, - "division of integers may cause loss of precision. consider using floats", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { + diag.help("division of integers may cause loss of precision. consider using floats"); + }); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index d65fffac5a8..c83bdda347a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; @@ -42,15 +42,11 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }; if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne { - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) { - return true; - } - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) { - return true; - } + let ecx = ConstEvalCtxt::new(cx); + matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + } else { + false } - - false } struct OperandInfo { @@ -60,7 +56,7 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> { - match constant(cx, cx.typeck_results(), operand) { + match ConstEvalCtxt::new(cx).eval(operand) { Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs index ea933168cfd..565294bb40a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,5 @@ use super::FLOAT_ARITHMETIC; -use clippy_utils::consts::constant_simple; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::LateContext; @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index c2b27c9b229..82b9d10fbeb 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_enclosing_block; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; @@ -64,10 +64,10 @@ pub(crate) fn check<'tcx>( |diag| { let lsnip = snippet(cx, l.span, "...").to_string(); let rsnip = snippet(cx, r.span, "...").to_string(); - multispan_sugg( - diag, + diag.multipart_suggestion( "use the values directly", vec![(left.span, lsnip), (right.span, rsnip)], + Applicability::MachineApplicable, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index d4906328ccb..ae02d22512e 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks, - peel_hir_expr_while, CaptureKind, + can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, + peel_blocks, peel_hir_expr_while, CaptureKind, }; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -294,7 +294,7 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { // Don't lint macros and constants - if expr.span.from_expansion() || in_constant(cx, expr.hir_id) { + if expr.span.from_expansion() || is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs index 2d20cbea698..267e2067e10 100644 --- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs +++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -57,24 +57,16 @@ impl EarlyLintPass for PartialPubFields { for field in fields { if all_priv && field.vis.kind.is_pub() { - span_lint_and_help( - cx, - PARTIAL_PUB_FIELDS, - field.vis.span, - msg, - None, - "consider using private field here", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, PARTIAL_PUB_FIELDS, field.vis.span, msg, |diag| { + diag.help("consider using private field here"); + }); return; } else if all_pub && !field.vis.kind.is_pub() { - span_lint_and_help( - cx, - PARTIAL_PUB_FIELDS, - field.vis.span, - msg, - None, - "consider using public field here", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, PARTIAL_PUB_FIELDS, field.vis.span, msg, |diag| { + diag.help("consider using public field here"); + }); return; } } diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index 0008f154ae3..18bfb588a11 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -21,8 +21,8 @@ declare_clippy_lint! { /// `PathBuf::from` instead. /// /// ### Known problems - /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternativly be - /// used when the `PathBuf` is newly constructed. This will avoild the implicit clone. + /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternatively be + /// used when the `PathBuf` is newly constructed. This will avoid the implicit clone. /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index 9661a57b8b9..c1296b04387 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{ intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, }; @@ -133,23 +133,25 @@ enum DerefPossible { fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat); if let Some((span, mutability, level)) = maybe_mismatch { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, PATTERN_TYPE_MISMATCH, span, "type of pattern does not match the expression type", - None, - format!( - "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", - match (deref_possible, level) { - (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", - _ => "", - }, - match mutability { - Mutability::Mut => "&mut _", - Mutability::Not => "&_", - }, - ), + |diag| { + diag.help(format!( + "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", + match (deref_possible, level) { + (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", + _ => "", + }, + match mutability { + Mutability::Mut => "&mut _", + Mutability::Not => "&_", + }, + )); + }, ); true } else { diff --git a/src/tools/clippy/clippy_lints/src/pub_use.rs b/src/tools/clippy/clippy_lints/src/pub_use.rs index ab8f8a1689d..5b973a79eae 100644 --- a/src/tools/clippy/clippy_lints/src/pub_use.rs +++ b/src/tools/clippy/clippy_lints/src/pub_use.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -42,14 +42,10 @@ impl EarlyLintPass for PubUse { if let ItemKind::Use(_) = item.kind && let VisibilityKind::Public = item.vis.kind { - span_lint_and_help( - cx, - PUB_USE, - item.span, - "using `pub use`", - None, - "move the exported item to a public module instead", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, PUB_USE, item.span, "using `pub use`", |diag| { + diag.help("move the exported item to a public module instead"); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index ef6b4d3aeab..e1e3ded2c79 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ - eq_expr_value, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, + eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_comment, }; @@ -346,15 +346,13 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() && !in_constant(cx, stmt.hir_id) { + if !self.inside_try_block() && !is_in_const_context(cx) { check_let_some_else_return_none(cx, stmt); } self.check_manual_let_else(cx, stmt); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !self.inside_try_block() - && !in_constant(cx, expr.hir_id) - && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) { check_is_none_or_err_and_early_return(cx, expr); check_if_let_some_or_err_and_early_return(cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs index f5e6cb804da..0a974bf9d2f 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark_used.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use rustc_hir::{Expr, ExprKind, MatchSource}; @@ -39,13 +39,15 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { return; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, QUESTION_MARK_USED, expr.span, "question mark operator was used", - None, - "consider using a custom macro or match expression", + |diag| { + diag.help("consider using a custom macro or match expression"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 829fb58bc65..81189fe517c 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -1,10 +1,10 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local}; +use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; @@ -202,7 +202,7 @@ fn check_possible_range_contains( expr: &Expr<'_>, span: Span, ) { - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } @@ -319,7 +319,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> _ => return None, }; if let Some(id) = path_to_local(l) { - if let Some(c) = constant(cx, cx.typeck_results(), r) { + if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, expr: r, @@ -331,7 +331,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> }); } } else if let Some(id) = path_to_local(r) { - if let Some(c) = constant(cx, cx.typeck_results(), l) { + if let Some(c) = ConstEvalCtxt::new(cx).eval(l) { return Some(RangeBounds { val: c, expr: l, @@ -451,8 +451,9 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { }) = higher::Range::hir(expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() - && let Some(start_idx) = constant(cx, cx.typeck_results(), start) - && let Some(end_idx) = constant(cx, cx.typeck_results(), end) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(start_idx) = ecx.eval(start) + && let Some(end_idx) = ecx.eval(end) && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx) && is_empty_range(limits, ordering) { diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 3416a93e3c4..a1231c082e6 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let (fn_def_id, arg, arg_ty, clone_ret) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); - let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) + let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id) && is_type_lang_item(cx, arg_ty, LangItem::String)); diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 82f22ad693d..1c10e84d3ca 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs}; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -82,13 +82,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { && let ExprKind::Index(indexed, range, _) = addressee.kind && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) { - let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed)); + let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX); - let mut app = Applicability::MachineApplicable; - let ((lint, msg), help, sugg) = if expr_ty == indexed_ty { + if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { // Indexing takes self by reference and can't return a reference to that // reference as it's a local variable. The only way this could happen is if @@ -99,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { } let deref_count = indexed_ref_count - expr_ref_count; - let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut { + let ((lint, msg), reborrow_str, help_msg) = if mutability == Mutability::Mut { // The slice was used to reborrow the mutable reference. (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead") } else if matches!( @@ -113,8 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })) ) - }) { - // The slice was used to make a temporary reference. + }) || (matches!( + cx.typeck_results().expr_ty(indexed).ref_mutability(), + Some(Mutability::Mut) + ) && mutability == Mutability::Not) + { (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead") } else if deref_count != 0 { (DEREF_BY_SLICING_LINT, "", "dereference the original value instead") @@ -122,38 +124,36 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { (REDUNDANT_SLICING_LINT, "", "use the original value instead") }; - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { - format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) - } else { - format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) - }; - - (lint, help_str, sugg) + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { + format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) + } else { + format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) + }; + diag.span_suggestion(expr.span, help_msg, sugg, app); + }); } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.param_env, Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), ) { if deref_ty == expr_ty { - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - } else { - format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - }; - (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) - } else { - return; + let (lint, msg) = DEREF_BY_SLICING_LINT; + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if needs_parens_for_prefix { + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + } else { + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); + }); } - } else { - return; } - } else { - return; - }; - - span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg, app); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs index 467038523b4..002c6c41d52 100644 --- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{BindingMode, Pat, PatKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -33,14 +33,10 @@ impl EarlyLintPass for RefPatterns { if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind && !pat.span.from_expansion() { - span_lint_and_help( - cx, - REF_PATTERNS, - pat.span, - "usage of ref pattern", - None, - "consider using `&` for clarity instead", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| { + diag.help("consider using `&` for clarity instead"); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 8cacb646f51..95014b23043 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::snippet_opt; use clippy_utils::{def_path_def_ids, path_def_id, paths}; @@ -148,7 +148,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> { - constant(cx, cx.typeck_results(), e).and_then(|c| match c { + ConstEvalCtxt::new(cx).eval(e).and_then(|c| match c { Constant::Str(s) => Some(s), _ => None, }) diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs deleted file mode 100644 index 8e999f3e89a..00000000000 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ /dev/null @@ -1,65 +0,0 @@ -// This file is managed by `cargo dev rename_lint`. Prefer using that when possible. - -#[rustfmt::skip] -pub static RENAMED_LINTS: &[(&str, &str)] = &[ - ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), - ("clippy::blacklisted_name", "clippy::disallowed_names"), - ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"), - ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"), - ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"), - ("clippy::box_vec", "clippy::box_collection"), - ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), - ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), - ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), - ("clippy::disallowed_method", "clippy::disallowed_methods"), - ("clippy::disallowed_type", "clippy::disallowed_types"), - ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::identity_conversion", "clippy::useless_conversion"), - ("clippy::if_let_some_result", "clippy::match_result_ok"), - ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), - ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), - ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), - ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), - ("clippy::new_without_default_derive", "clippy::new_without_default"), - ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), - ("clippy::option_expect_used", "clippy::expect_used"), - ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), - ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), - ("clippy::option_unwrap_used", "clippy::unwrap_used"), - ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), - ("clippy::ref_in_deref", "clippy::needless_borrow"), - ("clippy::result_expect_used", "clippy::expect_used"), - ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), - ("clippy::result_unwrap_used", "clippy::unwrap_used"), - ("clippy::single_char_push_str", "clippy::single_char_add_str"), - ("clippy::stutter", "clippy::module_name_repetitions"), - ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), - ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), - ("clippy::zero_width_space", "clippy::invisible_characters"), - ("clippy::cast_ref_to_mut", "invalid_reference_casting"), - ("clippy::clone_double_ref", "suspicious_double_ref_op"), - ("clippy::cmp_nan", "invalid_nan_comparisons"), - ("clippy::drop_bounds", "drop_bounds"), - ("clippy::drop_copy", "dropping_copy_types"), - ("clippy::drop_ref", "dropping_references"), - ("clippy::fn_null_check", "useless_ptr_null_checks"), - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), - ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - ("clippy::forget_copy", "forgetting_copy_types"), - ("clippy::forget_ref", "forgetting_references"), - ("clippy::into_iter_on_array", "array_into_iter"), - ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - ("clippy::invalid_ref", "invalid_value"), - ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), - ("clippy::let_underscore_drop", "let_underscore_drop"), - ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), - ("clippy::panic_params", "non_fmt_panics"), - ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), - ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), - ("clippy::undropped_manually_drops", "undropped_manually_drops"), - ("clippy::unknown_clippy_lints", "unknown_lints"), - ("clippy::unused_label", "unused_labels"), - ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), -]; diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs index 792d8fc88f0..678681ea425 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; @@ -69,7 +69,7 @@ fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) { && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr) && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) && !len_expr.span.from_expansion() - && let Some(Constant::Int(2..)) = constant(cx, cx.typeck_results(), expr_or_init(cx, len_expr)) + && let Some(Constant::Int(2..)) = ConstEvalCtxt::new(cx).eval(expr_or_init(cx, len_expr)) { emit_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 8ced47b48a4..13016cdadb0 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -395,7 +395,7 @@ fn check_final_expr<'tcx>( // Returns may be used to turn an expression into a statement in rustc's AST. // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) // `#[expect(clippy::needless_return)]` needs to be handled separatly to - // actually fullfil the expectation (clippy::#12998) + // actually fulfill the expectation (clippy::#12998) match cx.tcx.hir().attrs(expr.hir_id) { [] => {}, [attr] => { diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 7615c21276d..09f1c112352 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -92,11 +92,10 @@ impl SemicolonBlock { semi_span, "consider moving the `;` inside the block for consistent formatting", |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "put the `;` here", + vec![(remove_span, String::new()), (insert_span, ";".to_owned())], Applicability::MachineApplicable, - [(remove_span, String::new()), (insert_span, ";".to_owned())], ); }, ); @@ -124,11 +123,10 @@ impl SemicolonBlock { block.span, "consider moving the `;` outside the block for consistent formatting", |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "put the `;` here", + vec![(remove_span, String::new()), (insert_span, ";".to_owned())], Applicability::MachineApplicable, - [(remove_span, String::new()), (insert_span, ";".to_owned())], ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index 5e65b9fa517..e6fe7649397 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -12,8 +12,8 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `contains` to see if a value is not - /// present on `HashSet` followed by a `insert`. + /// Checks for usage of `contains` to see if a value is not present + /// in a set like `HashSet` or `BTreeSet`, followed by an `insert`. /// /// ### Why is this bad? /// Using just `insert` and checking the returned `bool` is more efficient. @@ -45,12 +45,12 @@ declare_clippy_lint! { #[clippy::version = "1.80.0"] pub SET_CONTAINS_OR_INSERT, nursery, - "call to `HashSet::contains` followed by `HashSet::insert`" + "call to `<set>::contains` followed by `<set>::insert`" } -declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]); +declare_lint_pass!(SetContainsOrInsert => [SET_CONTAINS_OR_INSERT]); -impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains { +impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && let Some(higher::If { @@ -58,14 +58,14 @@ impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains { then: then_expr, .. }) = higher::If::hir(expr) - && let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) + && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr) { span_lint( cx, SET_CONTAINS_OR_INSERT, vec![contains_expr.span, insert_expr.span], - "usage of `HashSet::insert` after `HashSet::contains`", + format!("usage of `{sym}::insert` after `{sym}::contains`"), ); } } @@ -77,7 +77,11 @@ struct OpExpr<'tcx> { span: Span, } -fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option<OpExpr<'tcx>> { +fn try_parse_op_call<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + symbol: Symbol, +) -> Option<(OpExpr<'tcx>, Symbol)> { let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Unary(UnOp::Not, e) = e.kind { Some(e) @@ -97,11 +101,12 @@ fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: }); let receiver = receiver.peel_borrows(); let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); - if value.span.eq_ctxt(expr.span) - && is_type_diagnostic_item(cx, receiver_ty, sym::HashSet) - && path.ident.name == symbol - { - return Some(OpExpr { receiver, value, span }); + if value.span.eq_ctxt(expr.span) && path.ident.name == symbol { + for sym in &[sym::HashSet, sym::BTreeSet] { + if is_type_diagnostic_item(cx, receiver_ty, *sym) { + return Some((OpExpr { receiver, value, span }, *sym)); + } + } } } None @@ -113,7 +118,7 @@ fn find_insert_calls<'tcx>( expr: &'tcx Expr<'_>, ) -> Option<OpExpr<'tcx>> { for_each_expr(cx, expr, |e| { - if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert)) + if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert)) && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) { diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 80f5fd0b494..7ae0310b6d9 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use rustc_data_structures::fx::FxHashMap; @@ -194,14 +194,9 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) (SHADOW_UNRELATED, msg) }, }; - span_lint_and_note( - cx, - lint, - span, - msg, - Some(cx.tcx.hir().span(shadowed)), - "previous binding is here", - ); + span_lint_and_then(cx, lint, span, msg, |diag| { + diag.span_note(cx.tcx.hir().span(shadowed), "previous binding is here"); + }); } /// Returns true if the expression is a simple transformation of a local binding such as `&x` diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs index 72feb977c31..d92b890950a 100644 --- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{GenericParam, GenericParamKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -48,13 +48,15 @@ impl EarlyLintPass for SingleCharLifetimeNames { if let GenericParamKind::Lifetime = param.kind { if !param.is_placeholder && param.ident.as_str().len() <= 2 { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( ctx, SINGLE_CHAR_LIFETIME_NAMES, param.ident.span, "single-character lifetime names are likely uninformative", - None, - "use a more informative name", + |diag| { + diag.help("use a more informative name"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs index 8d7f12af86e..b3d32a6d7d8 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; -use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::{path_def_id, peel_middle_ty_refs}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -60,7 +59,7 @@ impl LateLintPass<'_> for SizeOfRef { && let Some(def_id) = path_def_id(cx, path) && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) && let arg_ty = cx.typeck_results().expr_ty(arg) - && peel_mid_ty_refs(arg_ty).1 > 1 + && peel_middle_ty_refs(arg_ty).1 > 1 { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 12b70075a3d..974e21df817 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -1,11 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::msrvs::Msrv; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_attr::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_semver::RustcVersion; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; use rustc_span::{sym, Span}; @@ -66,6 +70,10 @@ declare_clippy_lint! { /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint /// is also useful for crates migrating to become `no_std` compatible. /// + /// ### Known problems + /// The lint is only partially aware of the required MSRV for items that were originally in `std` but moved + /// to `core`. + /// /// ### Example /// ```no_run /// # extern crate alloc; @@ -81,20 +89,30 @@ declare_clippy_lint! { "type is imported from alloc when available in core" } -#[derive(Default)] pub struct StdReexports { // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro // when the path could be also be used to access the module. prev_span: Span, + msrv: Msrv, } + +impl StdReexports { + pub fn new(conf: &'static Conf) -> Self { + Self { + prev_span: Span::default(), + msrv: conf.msrv.clone(), + } + } +} + impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) - && is_stable(cx, def_id) + && is_stable(cx, def_id, &self.msrv) && !in_external_macro(cx.sess(), path.span) && !is_from_proc_macro(cx, &first_segment.ident) { @@ -118,19 +136,27 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { _ => return, }; if first_segment.ident.span != self.prev_span { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, lint, first_segment.ident.span, format!("used import from `{used_mod}` instead of `{replace_with}`"), - format!("consider importing the item from `{replace_with}`"), - replace_with.to_string(), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + first_segment.ident.span, + format!("consider importing the item from `{replace_with}`"), + replace_with.to_string(), + Applicability::MachineApplicable, + ); + }, ); self.prev_span = first_segment.ident.span; } } } + + extract_msrv_attr!(LateContext); } /// Returns the first named segment of a [`Path`]. @@ -146,16 +172,29 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> } } -/// Checks if all ancestors of `def_id` are stable, to avoid linting -/// [unstable moves](https://github.com/rust-lang/rust/pull/95956) -fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool { +/// Checks if all ancestors of `def_id` meet `msrv` to avoid linting [unstable moves](https://github.com/rust-lang/rust/pull/95956) +/// or now stable moves that were once unstable. +/// +/// Does not catch individually moved items +fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool { loop { - if cx - .tcx - .lookup_stability(def_id) - .map_or(false, |stability| stability.is_unstable()) + if let Some(stability) = cx.tcx.lookup_stability(def_id) + && let StabilityLevel::Stable { + since, + allowed_through_unstable_modules: false, + } = stability.level { - return false; + let stable = match since { + StableSince::Version(v) => { + msrv.meets(RustcVersion::new(v.major.into(), v.minor.into(), v.patch.into())) + }, + StableSince::Current => msrv.current().is_none(), + StableSince::Err => false, + }; + + if !stable { + return false; + } } match cx.tcx.opt_parent(def_id) { diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 7da661485ab..cfc387886dc 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ @@ -399,17 +399,16 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { && let ty::Ref(_, ty, ..) = ty.kind() && ty.is_str() { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); - - span_lint_and_sugg( + span_lint_and_then( cx, STR_TO_STRING, expr.span, "`to_string()` called on a `&str`", - "try", - format!("{snippet}.to_owned()"), - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); + diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability); + }, ); } } @@ -455,13 +454,15 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { && let ty = cx.typeck_results().expr_ty(self_arg) && is_type_lang_item(cx, ty, LangItem::String) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, STRING_TO_STRING, expr.span, "`to_string()` called on a `String`", - None, - "consider using `.clone()`", + |diag| { + diag.help("consider using `.clone()`"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs index d150a5f858a..d1d822a5532 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet; use rustc_ast::LitKind; @@ -43,14 +43,19 @@ impl LateLintPass<'_> for ConfusingXorAndPow { && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node) .is_some_and(|x| x.is_decimal()) { - span_lint_and_sugg( + span_lint_and_then( cx, SUSPICIOUS_XOR_USED_AS_POW, expr.span, "`^` is not the exponentiation operator", - "did you mean to write", - format!("{}.pow({})", lit_left.node, lit_right.node), - Applicability::MaybeIncorrect, + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to write", + format!("{}.pow({})", lit_left.node, lit_right.node), + Applicability::MaybeIncorrect, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 93bad865809..197011cde3a 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -170,7 +170,7 @@ fn generate_swap_warning<'tcx>( /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if in_constant(cx, block.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs index 58e42892c41..25d0a16e2ab 100644 --- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; @@ -61,13 +61,15 @@ impl LateLintPass<'_> for TestsOutsideTestModule { && is_in_test_function(cx.tcx, body.id().hir_id) && !is_in_cfg_test(cx.tcx, body.id().hir_id) { - span_lint_and_note( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, TESTS_OUTSIDE_TEST_MODULE, sp, "this function marked with #[test] is outside a #[cfg(test)] module", - None, - "move it to a testing module marked with #[cfg(test)]", + |diag| { + diag.note("move it to a testing module marked with #[cfg(test)]"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 9c6813a54b9..373bf61d8ff 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -21,7 +21,7 @@ mod wrong_transmute; use clippy_config::msrvs::Msrv; use clippy_config::Conf; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -595,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // - from/to bits (https://github.com/rust-lang/rust/issues/73736) // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) // - char conversions (https://github.com/rust-lang/rust/issues/89259) - let const_context = in_constant(cx, e.hir_id); + let const_context = is_in_const_context(cx); let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { [] => (cx.typeck_results().expr_ty(arg), false), diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs index b26365e34ab..7acf3be51fb 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -33,10 +33,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // transmute over constants that resolve to `null`. ExprKind::Path(ref _qpath) - if matches!( - constant(cx, cx.typeck_results(), casts_peeled), - Some(Constant::RawPtr(0)) - ) => + if matches!(ConstEvalCtxt::new(cx).eval(casts_peeled), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); true diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs index 471bd44b5d5..544014bd32b 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. if let ExprKind::Path(ref _qpath) = arg.kind - && let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg) + && let Some(Constant::RawPtr(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 83cc9f2d8df..2fcfc71a8c7 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -48,15 +48,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{ltopt}({})", &inner_snippet) + format!("&{ltopt}({inner_snippet})") }, TyKind::Path(qpath) if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) .map_or(false, |bounds| bounds.len() > 1) => { - format!("&{ltopt}({})", &inner_snippet) + format!("&{ltopt}({inner_snippet})") }, - _ => format!("&{ltopt}{}", &inner_snippet), + _ => format!("&{ltopt}{inner_snippet}"), }; span_lint_and_sugg( cx, @@ -82,16 +82,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { if let TyKind::TraitObject(traits, ..) = t.kind { - return traits - .iter() - .any(|(bound, _)| { - if let Some(trait_did) = bound.trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::Any, trait_did) - { - return true; - } - false - }); + return traits.iter().any(|(bound, _)| { + if let Some(trait_did) = bound.trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; + } + false + }); } false diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs index f6c2d8d5a5e..d691f1878b1 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; @@ -13,14 +13,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ let app = Applicability::Unspecified; if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { if let Some(alternate) = match_buffer_type(cx, qpath) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Rc<T>` when T is a buffer type", - "try", - format!("Rc<{alternate}>"), - app, + |diag| { + diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app); + }, ); } else { let Some(ty) = qpath_generic_tys(qpath).next() else { @@ -35,31 +36,37 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ Some(ty) => ty.span, None => return false, }; - let mut applicability = app; - span_lint_and_sugg( + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Rc<T>` when T is a buffer type", - "try", - format!( - "Rc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - app, + |diag| { + let mut applicability = app; + diag.span_suggestion( + hir_ty.span, + "try", + format!( + "Rc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + app, + ); + }, ); return true; } } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { if let Some(alternate) = match_buffer_type(cx, qpath) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Arc<T>` when T is a buffer type", - "try", - format!("Arc<{alternate}>"), - app, + |diag| { + diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app); + }, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { let Some(id) = path_def_id(cx, ty) else { return false }; @@ -71,18 +78,23 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ Some(ty) => ty.span, None => return false, }; - let mut applicability = app; - span_lint_and_sugg( + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Arc<T>` when T is a buffer type", - "try", - format!( - "Arc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - app, + |diag| { + let mut applicability = app; + diag.span_suggestion( + hir_ty.span, + "try", + format!( + "Arc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + app, + ); + }, ); return true; } diff --git a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs index afc31921704..7b13debc01e 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; @@ -13,14 +13,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ && let Some(id) = path_def_id(cx, arg) && cx.tcx.is_diagnostic_item(sym::Mutex, id) { - span_lint_and_help( - cx, - RC_MUTEX, - hir_ty.span, - "usage of `Rc<Mutex<_>>`", - None, - "consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc<Mutex<_>>`", |diag| { + diag.help("consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead"); + }); return true; } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 3ab30bf5dba..f51c5f74d99 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{for_each_expr, Descend}; @@ -129,13 +129,15 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { block.span }; - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNDOCUMENTED_UNSAFE_BLOCKS, span, "unsafe block missing a safety comment", - None, - "consider adding a safety comment on the preceding line", + |diag| { + diag.help("consider adding a safety comment on the preceding line"); + }, ); } @@ -145,13 +147,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, tail.span, "expression has unnecessary safety comment", - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } } @@ -168,13 +171,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, stmt.span, "statement has unnecessary safety comment", - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } } @@ -210,13 +214,15 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { item.span }; - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNDOCUMENTED_UNSAFE_BLOCKS, span, "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", + |diag| { + diag.help("consider adding a safety comment on the preceding line"); + }, ); } }, @@ -225,13 +231,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, span, "impl has unnecessary safety comment", - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } }, @@ -246,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { ) { let (span, help_span) = mk_spans(pos); - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, span, format!("{} has unnecessary safety comment", item.kind.descr()), - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } } @@ -263,13 +271,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, span, format!("{} has unnecessary safety comment", item.kind.descr()), - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } }, diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs index d42697b31d1..e1fc644e4ce 100644 --- a/src/tools/clippy/clippy_lints/src/unicode.rs +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::macros::span_is_local; use clippy_utils::source::snippet; @@ -105,45 +105,51 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { - span_lint_and_sugg( - cx, - INVISIBLE_CHARACTERS, - span, - "invisible character detected", - "consider replacing the string with", - string - .replace('\u{200B}', "\\u{200B}") - .replace('\u{ad}', "\\u{AD}") - .replace('\u{2060}', "\\u{2060}"), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, INVISIBLE_CHARACTERS, span, "invisible character detected", |diag| { + diag.span_suggestion( + span, + "consider replacing the string with", + string + .replace('\u{200B}', "\\u{200B}") + .replace('\u{ad}', "\\u{AD}") + .replace('\u{2060}', "\\u{2060}"), + Applicability::MachineApplicable, + ); + }); } if string.chars().any(|c| c as u32 > 0x7F) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, NON_ASCII_LITERAL, span, "literal non-ASCII character detected", - "consider replacing the string with", - if is_lint_allowed(cx, UNICODE_NOT_NFC, id) { - escape(string.chars()) - } else { - escape(string.nfc()) + |diag| { + diag.span_suggestion( + span, + "consider replacing the string with", + if is_lint_allowed(cx, UNICODE_NOT_NFC, id) { + escape(string.chars()) + } else { + escape(string.nfc()) + }, + Applicability::MachineApplicable, + ); }, - Applicability::MachineApplicable, ); } if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) { - span_lint_and_sugg( - cx, - UNICODE_NOT_NFC, - span, - "non-NFC Unicode sequence detected", - "consider replacing the string with", - string.nfc().collect::<String>(), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, UNICODE_NOT_NFC, span, "non-NFC Unicode sequence detected", |diag| { + diag.span_suggestion( + span, + "consider replacing the string with", + string.nfc().collect::<String>(), + Applicability::MachineApplicable, + ); + }); } } diff --git a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs new file mode 100644 index 00000000000..297288db0a5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs @@ -0,0 +1,59 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Result::ok()` without using the returned `Option`. + /// + /// ### Why is this bad? + /// Using `Result::ok()` may look like the result is checked like `unwrap` or `expect` would do + /// but it only silences the warning caused by `#[must_use]` on the `Result`. + /// + /// ### Example + /// ```no_run + /// # fn some_function() -> Result<(), ()> { Ok(()) } + /// some_function().ok(); + /// ``` + /// Use instead: + /// ```no_run + /// # fn some_function() -> Result<(), ()> { Ok(()) } + /// let _ = some_function(); + /// ``` + #[clippy::version = "1.70.0"] + pub UNUSED_RESULT_OK, + restriction, + "Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead." +} +declare_lint_pass!(UnusedResultOk => [UNUSED_RESULT_OK]); + +impl LateLintPass<'_> for UnusedResultOk { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result<T,E>.ok(, _) + && ok_path.ident.as_str() == "ok" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && !in_external_macro(cx.sess(), stmt.span) + { + let ctxt = expr.span.ctxt(); + let mut applicability = Applicability::MaybeIncorrect; + let snippet = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; + let sugg = format!("let _ = {snippet}"); + span_lint_and_sugg( + cx, + UNUSED_RESULT_OK, + expr.span, + "ignoring a result with `.ok()` is misleading", + "consider using `let _ =` and removing the call to `.ok()` instead", + sugg, + applicability, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index b017a6bf665..7c2e23995c1 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && match_def_path(cx, *def_id, &paths::SYMBOL_INTERN) - && let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg) + && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) && let value = Symbol::intern(&arg).as_u32() && let Some(&def_id) = self.symbol_map.get(&value) { @@ -199,7 +199,7 @@ impl InterningDefinedSymbol { }); } // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) { let value = Symbol::intern(&s).as_u32(); // ...which matches a symbol constant if let Some(&def_id) = self.symbol_map.get(&value) { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 980437259c3..0ffcb433481 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; @@ -32,9 +32,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if mod_name.as_str() == "paths" && let hir::ItemKind::Const(.., body_id) = item.kind - && let body = cx.tcx.hir().body(body_id) - && let typeck_results = cx.tcx.typeck_body(body_id) - && let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value) + && let Some(Constant::Vec(path)) = + ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(item.owner_id), cx.tcx.typeck(item.owner_id)) + .eval_simple(cx.tcx.hir().body(body_id).value) && let Some(path) = path .iter() .map(|x| { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 84f84781e71..df342e48d63 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -1,4 +1,3 @@ -use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{is_lint_allowed, match_def_path, paths}; @@ -87,82 +86,32 @@ declare_clippy_lint! { "found clippy lint without `clippy::version` attribute" } -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated deprecated lint without an updated reason, - /// i.e. `"default deprecation note"`. - /// - /// ### Why is this bad? - /// Indicates that the documentation is incomplete. - /// - /// ### Example - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// TODO - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "default deprecation note" - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// This lint has been replaced by `cooler_lint` - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "this lint has been replaced by `cooler_lint`" - /// } - /// ``` - pub DEFAULT_DEPRECATION_REASON, - internal, - "found 'default deprecation note' in a deprecated lint declaration" -} - #[derive(Clone, Debug, Default)] pub struct LintWithoutLintPass { declared_lints: FxHashMap<Symbol, Span>, registered_lints: FxHashSet<Symbol>, } -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]); impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) - || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) - { + if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) { return; } if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { - let is_lint_ref_ty = is_lint_ref_type(cx, ty); - if is_deprecated_lint(cx, ty) || is_lint_ref_ty { + if is_lint_ref_type(cx, ty) { check_invalid_clippy_version_attribute(cx, item); let expr = &cx.tcx.hir().body(body_id).value; - let fields; - if is_lint_ref_ty { - if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind - && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind - { - fields = struct_fields; - } else { - return; - } - } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { - fields = struct_fields; + let fields = if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind + { + struct_fields } else { return; - } + }; let field = fields .iter() @@ -175,25 +124,15 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { }) = field.expr.kind { let sym_str = sym.as_str(); - if is_lint_ref_ty { - if sym_str == "default lint description" { - span_lint( - cx, - DEFAULT_LINT, - item.span, - format!("the lint `{}` has the default lint description", item.ident.name), - ); - } - - self.declared_lints.insert(item.ident.name, item.span); - } else if sym_str == "default deprecation note" { + if sym_str == "default lint description" { span_lint( cx, - DEFAULT_DEPRECATION_REASON, + DEFAULT_LINT, item.span, - format!("the lint `{}` has the default deprecation reason", item.ident.name), + format!("the lint `{}` has the default lint description", item.ident.name), ); } + self.declared_lints.insert(item.ident.name, item.span); } } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 1c149f20456..57f45aa3e48 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,13 +7,12 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) -use crate::renamed_lints::RENAMED_LINTS; use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_config::{get_configuration_metadata, ClippyConfiguration}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; -use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths}; +use clippy_utils::{last_path_segment, match_function_call, match_path, paths}; use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -85,11 +84,6 @@ const SUGGESTION_DIAG_METHODS: [(&str, bool); 9] = [ ("tool_only_multipart_suggestion", true), ("span_suggestions", true), ]; -const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ - &["clippy_utils", "diagnostics", "multispan_sugg"], - &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], -]; -const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"]; /// The index of the applicability name of `paths::APPLICABILITY_VALUES` const APPLICABILITY_NAME_INDEX: usize = 2; @@ -165,9 +159,9 @@ impl MetadataCollector { fn get_lint_configs(&self, lint_name: &str) -> Option<String> { self.config .iter() - .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) + .filter(|config| config.lints.iter().any(|&lint| lint == lint_name)) .map(ToString::to_string) - .reduce(|acc, x| acc + &x) + .reduce(|acc, x| acc + "\n\n" + &x) .map(|configurations| { format!( r#" @@ -216,6 +210,13 @@ impl Drop for MetadataCollector { let mut applicability_info = std::mem::take(&mut self.applicability_info); + // Add deprecated lints + self.lints.extend( + crate::deprecated_lints::DEPRECATED + .iter() + .zip(crate::deprecated_lints::DEPRECATED_VERSION) + .filter_map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)), + ); // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); for x in &mut lints { @@ -261,7 +262,7 @@ Please use that command to update the file and do not edit it by hand. #[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct LintMetadata { id: String, - id_span: SerializableSpan, + id_span: Option<SerializableSpan>, group: String, level: String, docs: String, @@ -285,7 +286,7 @@ impl LintMetadata { ) -> Self { Self { id, - id_span, + id_span: Some(id_span), group, level: level.to_string(), version, @@ -294,6 +295,29 @@ impl LintMetadata { former_ids: BTreeSet::new(), } } + + fn new_deprecated(name: &str, reason: &str, version: &str) -> Option<Self> { + // The reason starts with a lowercase letter and end without a period. + // This needs to be fixed for the website. + let mut reason = reason.to_owned(); + if let Some(reason) = reason.get_mut(0..1) { + reason.make_ascii_uppercase(); + } + name.strip_prefix("clippy::").map(|name| Self { + id: name.into(), + id_span: None, + group: DEPRECATED_LINT_GROUP_STR.into(), + level: DEPRECATED_LINT_LEVEL.into(), + version: version.into(), + docs: format!( + "### What it does\n\n\ + Nothing. This lint has been deprecated\n\n\ + ### Deprecation reason\n\n{reason}.\n", + ), + applicability: None, + former_ids: BTreeSet::new(), + }) + } } fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) { @@ -564,24 +588,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { raw_docs, )); } - - if is_deprecated_lint(cx, ty) - // disallow check - && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() - // Metadata the little we can get from a deprecated lint - && let Some(raw_docs) = extract_attr_docs_or_lint(cx, item) - { - let version = get_lint_version(cx, item); - - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - DEPRECATED_LINT_GROUP_STR.to_string(), - DEPRECATED_LINT_LEVEL, - version, - raw_docs, - )); - } } } @@ -684,6 +690,11 @@ fn cleanup_docs(docs_collection: &Vec<String>) -> String { .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic")) // if no language is present, fill in "rust" .unwrap_or("rust"); + let len_diff = line.len() - line.trim_start().len(); + if len_diff != 0 { + // We put back the indentation. + docs.push_str(&line[..len_diff]); + } docs.push_str("```"); docs.push_str(lang); @@ -766,16 +777,6 @@ fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> { .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level)) } -pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(ref path) = ty.kind { - if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) { - return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE); - } - } - - false -} - fn collect_renames(lints: &mut Vec<LintMetadata>) { for lint in lints { let mut collected = String::new(); @@ -783,7 +784,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) { loop { if let Some(lint_name) = names.pop() { - for (k, v) in RENAMED_LINTS { + for (k, v) in crate::deprecated_lints::RENAMED { if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) && name == lint_name && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) @@ -1055,33 +1056,21 @@ impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { return; } - match &expr.kind { - ExprKind::Call(fn_expr, _args) => { - let found_function = SUGGESTION_FUNCTIONS - .iter() - .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some()); - if found_function { - // These functions are all multi part suggestions - self.add_single_span_suggestion(); - } - }, - ExprKind::MethodCall(path, recv, _, _arg_span) => { - let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv)); - if match_type(self.cx, self_ty, &paths::DIAG) { - let called_method = path.ident.name.as_str().to_string(); - for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS { - if *method_name == called_method { - if *is_multi_part { - self.add_multi_part_suggestion(); - } else { - self.add_single_span_suggestion(); - } - break; + if let ExprKind::MethodCall(path, recv, _, _arg_span) = &expr.kind { + let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv)); + if match_type(self.cx, self_ty, &paths::DIAG) { + let called_method = path.ident.name.as_str().to_string(); + for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS { + if *method_name == called_method { + if *is_multi_part { + self.add_multi_part_suggestion(); + } else { + self.add_single_span_suggestion(); } + break; } } - }, - _ => {}, + } } intravisit::walk_expr(self, expr); diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index a831234906b..228db14d1b7 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_copy; @@ -159,7 +159,7 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) { + if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { return; diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 11dcceca7ab..63f3a5d7f83 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; @@ -85,14 +85,19 @@ impl EarlyLintPass for Visibility { if **path == kw::SelfLower && let Some(false) = is_from_proc_macro(cx, item.vis.span) { - span_lint_and_sugg( + span_lint_and_then( cx, NEEDLESS_PUB_SELF, item.vis.span, format!("unnecessary `pub({}self)`", if *shorthand { "" } else { "in " }), - "remove it", - String::new(), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion_hidden( + item.vis.span, + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + }, ); } @@ -101,14 +106,20 @@ impl EarlyLintPass for Visibility { && let [.., last] = &*path.segments && let Some(false) = is_from_proc_macro(cx, item.vis.span) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, PUB_WITHOUT_SHORTHAND, item.vis.span, "usage of `pub` with `in`", - "remove it", - format!("pub({})", last.ident), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + item.vis.span, + "remove it", + format!("pub({})", last.ident), + Applicability::MachineApplicable, + ); + }, ); } @@ -116,14 +127,20 @@ impl EarlyLintPass for Visibility { && let [.., last] = &*path.segments && let Some(false) = is_from_proc_macro(cx, item.vis.span) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, PUB_WITH_SHORTHAND, item.vis.span, "usage of `pub` without `in`", - "add it", - format!("pub(in {})", last.ident), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + item.vis.span, + "add it", + format!("pub(in {})", last.ident), + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs index 60d8a13d359..5eb207a0aed 100644 --- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -36,8 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. - && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left) - && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lhs_value) = ecx.eval_simple(left) + && let Some(rhs_value) = ecx.eval_simple(right) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index f0c64fdd573..9fefd94d339 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -12,9 +12,6 @@ rustc-semver = "1.1" # FIXME(f16_f128): remove when no longer needed for parsing rustc_apfloat = "0.2.0" -[features] -deny-warnings = ["clippy_config/deny-warnings"] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index de6ccfe476f..e907e4058e5 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -14,12 +14,13 @@ use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; -use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; +use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Ident; use rustc_span::{sym, SyntaxContext}; use rustc_target::abi::Size; +use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::iter; @@ -263,10 +264,10 @@ impl<'tcx> Constant<'tcx> { } /// Returns the integer value or `None` if `self` or `val_type` is not integer type. - pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> { + pub fn int_value(&self, tcx: TyCtxt<'_>, val_type: Ty<'_>) -> Option<FullInt> { if let Constant::Int(const_int) = *self { match *val_type.kind() { - ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Int(ity) => Some(FullInt::S(sext(tcx, const_int, ity))), ty::Uint(_) => Some(FullInt::U(const_int)), _ => None, } @@ -322,6 +323,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan } /// The source of a constant value. +#[derive(Clone, Copy)] pub enum ConstantSource { /// The value is determined solely from the expression. Local, @@ -331,54 +333,11 @@ pub enum ConstantSource { CoreConstant, } impl ConstantSource { - pub fn is_local(&self) -> bool { + pub fn is_local(self) -> bool { matches!(self, Self::Local) } } -/// Attempts to check whether the expression is a constant representing an empty slice, str, array, -/// etc… -pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> { - ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e) -} - -/// Attempts to evaluate the expression as a constant. -pub fn constant<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<Constant<'tcx>> { - ConstEvalLateContext::new(lcx, typeck_results).expr(e) -} - -/// Attempts to evaluate the expression as a constant. -pub fn constant_with_source<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<(Constant<'tcx>, ConstantSource)> { - let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results); - let res = ctxt.expr(e); - res.map(|x| (x, ctxt.source)) -} - -/// Attempts to evaluate an expression only if its value is not dependent on other items. -pub fn constant_simple<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<Constant<'tcx>> { - constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c)) -} - -pub fn constant_full_int<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<FullInt> { - constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e)) -} - #[derive(Copy, Clone, Debug, Eq)] pub enum FullInt { S(i128), @@ -417,44 +376,87 @@ impl Ord for FullInt { } } -pub struct ConstEvalLateContext<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - param_env: ty::ParamEnv<'tcx>, - source: ConstantSource, - args: GenericArgsRef<'tcx>, +/// The context required to evaluate a constant expression. +/// +/// This is currently limited to constant folding and reading the value of named constants. +pub struct ConstEvalCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + source: Cell<ConstantSource>, } -impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { - pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { +impl<'tcx> ConstEvalCtxt<'tcx> { + /// Creates the evaluation context from the lint context. This requires the lint context to be + /// in a body (i.e. `cx.enclosing_body.is_some()`). + pub fn new(cx: &LateContext<'tcx>) -> Self { + Self { + tcx: cx.tcx, + param_env: cx.param_env, + typeck: cx.typeck_results(), + source: Cell::new(ConstantSource::Local), + } + } + + /// Creates an evaluation context. + pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self { Self { - lcx, - typeck_results, - param_env: lcx.param_env, - source: ConstantSource::Local, - args: List::empty(), + tcx, + param_env, + typeck, + source: Cell::new(ConstantSource::Local), + } + } + + /// Attempts to evaluate the expression and returns both the value and whether it's dependant on + /// other items. + pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + self.source.set(ConstantSource::Local); + self.expr(e).map(|c| (c, self.source.get())) + } + + /// Attempts to evaluate the expression. + pub fn eval(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { + self.expr(e) + } + + /// Attempts to evaluate the expression without accessing other items. + pub fn eval_simple(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { + match self.eval_with_source(e) { + Some((x, ConstantSource::Local)) => Some(x), + _ => None, + } + } + + /// Attempts to evaluate the expression as an integer without accessing other items. + pub fn eval_full_int(&self, e: &Expr<'_>) -> Option<FullInt> { + match self.eval_with_source(e) { + Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), + _ => None, } } /// Simple constant folding: Insert an expression, get a constant or none. - pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> { + fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.lcx.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.lcx.tcx.crate_name(def_id.krate) == sym::core + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core } else { false }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { - let result = mir_to_const(this.lcx, result)?; + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; // If source is already Constant we wouldn't want to override it with CoreConstant - this.source = if is_core_crate && !matches!(this.source, ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }; + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); Some(result) }) }, @@ -463,21 +465,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if is_direct_expn_of(e.span, "cfg").is_some() { None } else { - Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e))) + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) } }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Repeat(value, _) => { - let n = match self.typeck_results.expr_ty(e).kind() { - ty::Array(_, n) => n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)?, + let n = match self.typeck.expr_ty(e).kind() { + ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, _ => span_bug!(e.span, "typeck error"), }; self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) }, ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op { - UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), - UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), + UnOp::Not => self.constant_not(&o, self.typeck.expr_ty(e)), + UnOp::Neg => self.constant_negate(&o, self.typeck.expr_ty(e)), UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), @@ -486,21 +488,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { // We only handle a few const functions for now. if args.is_empty() && let ExprKind::Path(qpath) = &callee.kind - && let res = self.typeck_results.qpath_res(qpath, callee.hir_id) - && let Some(def_id) = res.opt_def_id() - && let def_path = self.lcx.get_def_path(def_id) - && let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::<Vec<_>>() - && let ["core", "num", int_impl, "max_value"] = *def_path + && let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id() { - let value = match int_impl { - "<impl i8>" => i8::MAX as u128, - "<impl i16>" => i16::MAX as u128, - "<impl i32>" => i32::MAX as u128, - "<impl i64>" => i64::MAX as u128, - "<impl i128>" => i128::MAX as u128, - _ => return None, - }; - Some(Constant::Int(value)) + match self.tcx.get_diagnostic_name(did) { + Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)), + Some(sym::i16_legacy_fn_max_value) => Some(Constant::Int(i16::MAX as u128)), + Some(sym::i32_legacy_fn_max_value) => Some(Constant::Int(i32::MAX as u128)), + Some(sym::i64_legacy_fn_max_value) => Some(Constant::Int(i64::MAX as u128)), + Some(sym::i128_legacy_fn_max_value) => Some(Constant::Int(i128::MAX as u128)), + _ => None, + } } else { None } @@ -512,9 +509,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if let Some(Constant::Adt(constant)) = &self.expr(local_expr) && let ty::Adt(adt_def, _) = constant.ty().kind() && adt_def.is_struct() - && let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field) + && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field) { - mir_to_const(self.lcx, desired_field) + mir_to_const(self.tcx, desired_field) } else { result } @@ -526,21 +523,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Simple constant folding to determine if an expression is an empty slice, str, array, … /// `None` will be returned if the constness cannot be determined, or if the resolution /// leaves the local crate. - pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> { + pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option<bool> { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), - ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.eval_is_empty(e), ExprKind::Path(ref qpath) => { if !self - .typeck_results + .typeck .qpath_res(qpath, e.hir_id) .opt_def_id() .is_some_and(DefId::is_local) { return None; } - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { - mir_is_empty(this.lcx, result) + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { + mir_is_empty(self_.tcx, result) }) }, ExprKind::Lit(lit) => { @@ -556,8 +553,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { }, ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), ExprKind::Repeat(..) => { - if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { - Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0) + if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() { + Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0) } else { span_bug!(e.span, "typeck error"); } @@ -574,8 +571,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { Int(value) => { let value = !value; match *ty.kind() { - ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), - ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), + ty::Int(ity) => Some(Int(unsext(self.tcx, value as i128, ity))), + ty::Uint(ity) => Some(Int(clip(self.tcx, value, ity))), _ => None, } }, @@ -590,7 +587,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let ty::Int(ity) = *ty.kind() else { return None }; let (min, _) = ity.min_max()?; // sign extend - let value = sext(self.lcx.tcx, value, ity); + let value = sext(self.tcx, value, ity); // Applying unary - to the most negative value of any signed integer type panics. if value == min { @@ -599,7 +596,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let value = value.checked_neg()?; // clear unused bits - Some(Int(unsext(self.lcx.tcx, value, ity))) + Some(Int(unsext(self.tcx, value, ity))) }, F32(f) => Some(F32(-f)), F64(f) => Some(F64(-f)), @@ -609,21 +606,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> { vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T> + fn fetch_path_and_apply<T, F>(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T> where - F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option<T>, + F: FnOnce(&Self, mir::Const<'tcx>) -> Option<T>, { - let res = self.typeck_results.qpath_res(qpath, id); + let res = self.typeck.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { // Check if this constant is based on `cfg!(..)`, // which is NOT constant for our purposes. - if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) + if let Some(node) = self.tcx.hir().get_if_local(def_id) && let Node::Item(Item { kind: ItemKind::Const(.., body_id), .. @@ -632,20 +629,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { kind: ExprKind::Lit(_), span, .. - }) = self.lcx.tcx.hir_node(body_id.hir_id) + }) = self.tcx.hir_node(body_id.hir_id) && is_direct_expn_of(*span, "cfg").is_some() { return None; } - let args = self.typeck_results.node_args(id); - let args = if self.args.is_empty() { - args - } else { - EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args) - }; + let args = self.typeck.node_args(id); let result = self - .lcx .tcx .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() @@ -656,7 +647,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> { let lhs = self.expr(lhs); let index = self.expr(index); @@ -685,8 +676,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - /// A block can only yield a constant if it only has one constant expression. - fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> { + /// A block can only yield a constant if it has exactly one constant expression. + fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -696,7 +687,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx) + && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; @@ -705,11 +696,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source = ConstantSource::Constant; + self.source.set(ConstantSource::Constant); } } else { // Unable to access the source. Assume a non-local dependency. - self.source = ConstantSource::Constant; + self.source.set(ConstantSource::Constant); } } @@ -719,7 +710,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -731,16 +722,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { + fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { + (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck.expr_ty_opt(left)?.kind() { ty::Int(ity) => { let (ty_min_value, _) = ity.min_max()?; let bits = ity.bits(); - let l = sext(self.lcx.tcx, l, ity); - let r = sext(self.lcx.tcx, r, ity); + let l = sext(self.tcx, l, ity); + let r = sext(self.tcx, r, ity); // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // the right-hand argument is -1 always panics, even with overflow-checks disabled @@ -751,7 +742,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { return None; } - let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); + let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity)); match op.node { // When +, * or binary - create a value greater than the maximum value, or less than // the minimum value that can be stored, it panics. @@ -845,7 +836,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } -pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { +pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -863,13 +854,13 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> _ => None, }, (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { - let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; + let data = val.try_get_slice_bytes_for_diagnostics(tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { - let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let len = len.try_to_target_usize(lcx.tcx)?; + let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let len = len.try_to_target_usize(tcx)?; let ty::Float(flt) = sub_type.kind() else { return None; }; @@ -877,7 +868,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> let mut res = Vec::new(); for idx in 0..len { let range = alloc_range(offset + size * idx, size); - let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; + let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), @@ -891,7 +882,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> } } -fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { +fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -902,26 +893,26 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti if let ConstValue::Indirect { alloc_id, offset } = val { // Get the length from the slice, using the same formula as // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. - let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let ptr_size = lcx.tcx.data_layout.pointer_size; + let a = tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let ptr_size = tcx.data_layout.pointer_size; if a.size() < offset + 2 * ptr_size { // (partially) dangling reference return None; } let len = a - .read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) + .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false) .ok()? - .to_target_usize(&lcx.tcx) + .to_target_usize(&tcx) .ok()?; Some(len == 0) } else { None } }, - ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0), _ => None, }, - (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0), (ConstValue::ZeroSized, _) => Some(true), _ => None, } @@ -929,12 +920,12 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, - lcx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>, field: &Ident, ) -> Option<mir::Const<'tcx>> { if let mir::Const::Val(result, ty) = result - && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty) + && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 0641d37cd9a..4877fb65d37 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -330,32 +330,3 @@ pub fn span_lint_and_sugg<T: LintContext>( diag.span_suggestion(sp, help.into(), sugg, applicability); }); } - -/// Create a suggestion made from several `span → replacement`. -/// -/// Note: in the JSON format (used by `compiletest_rs`), the help message will -/// appear once per -/// replacement. In human-readable format though, it only appears once before -/// the whole suggestion. -pub fn multispan_sugg<I>(diag: &mut Diag<'_, ()>, help_msg: impl Into<SubdiagMessage>, sugg: I) -where - I: IntoIterator<Item = (Span, String)>, -{ - multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg); -} - -/// Create a suggestion made from several `span → replacement`. -/// -/// rustfix currently doesn't support the automatic application of suggestions with -/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141). -/// Suggestions with multiple spans will be silently ignored. -pub fn multispan_sugg_with_applicability<I>( - diag: &mut Diag<'_, ()>, - help_msg: impl Into<SubdiagMessage>, - applicability: Applicability, - sugg: I, -) where - I: IntoIterator<Item = (Span, String)>, -{ - diag.multipart_suggestion(help_msg.into(), sugg.into_iter().collect(), applicability); -} diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 6c40029a9de..a6dd12a28c5 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::consts::{constant, FullInt}; +use crate::consts::{ConstEvalCtxt, FullInt}; use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; @@ -206,7 +206,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if constant(self.cx, self.cx.typeck_results(), right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { self.eagerness |= NoChange; }, @@ -232,7 +232,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && constant(self.cx, self.cx.typeck_results(), right).is_none() => + && ConstEvalCtxt::new(self.cx).eval(right).is_none() => { self.eagerness |= NoChange; }, @@ -240,9 +240,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) && let right_ty = self.cx.typeck_results().expr_ty(right) - && let left = constant(self.cx, self.cx.typeck_results(), left) - && let right = constant(self.cx, self.cx.typeck_results(), right) - .and_then(|c| c.int_value(self.cx, right_ty)) + && let ecx = ConstEvalCtxt::new(self.cx) + && let left = ecx.eval(left) + && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -261,8 +261,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && (constant(self.cx, self.cx.typeck_results(), left).is_none() - || constant(self.cx, self.cx.typeck_results(), right).is_none()) => + && let ecx = ConstEvalCtxt::new(self.cx) + && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 277ba8427e0..8970b4d1229 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -2,7 +2,7 @@ #![deny(clippy::missing_docs_in_private_items)] -use crate::consts::{constant_simple, Constant}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; @@ -25,6 +25,8 @@ pub struct ForLoop<'tcx> { pub loop_id: HirId, /// entire `for` loop span pub span: Span, + /// label + pub label: Option<ast::Label>, } impl<'tcx> ForLoop<'tcx> { @@ -33,7 +35,7 @@ impl<'tcx> ForLoop<'tcx> { if let ExprKind::DropTemps(e) = expr.kind && let ExprKind::Match(iterexpr, [arm], MatchSource::ForLoopDesugar) = e.kind && let ExprKind::Call(_, [arg]) = iterexpr.kind - && let ExprKind::Loop(block, ..) = arm.body.kind + && let ExprKind::Loop(block, label, ..) = arm.body.kind && let [stmt] = block.stmts && let hir::StmtKind::Expr(e) = stmt.kind && let ExprKind::Match(_, [_, some_arm], _) = e.kind @@ -45,6 +47,7 @@ impl<'tcx> ForLoop<'tcx> { body: some_arm.body, loop_id: arm.body.hir_id, span: expr.span.ctxt().outer_expn_data().call_site, + label, }); } None @@ -367,6 +370,7 @@ pub struct WhileLet<'hir> { pub let_expr: &'hir Expr<'hir>, /// `while let` loop body pub if_then: &'hir Expr<'hir>, + pub label: Option<ast::Label>, /// `while let PAT = EXPR` /// ^^^^^^^^^^^^^^ pub let_span: Span, @@ -399,7 +403,7 @@ impl<'hir> WhileLet<'hir> { }), .. }, - _, + label, LoopSource::While, _, ) = expr.kind @@ -408,6 +412,7 @@ impl<'hir> WhileLet<'hir> { let_pat, let_expr, if_then, + label, let_span, }); } @@ -466,7 +471,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.first()?; - return match constant_simple(cx, cx.typeck_results(), arg) { + return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 28178a61a93..f325e4eaf15 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::constant_simple; +use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt}; use crate::tokenize_with_text; @@ -255,8 +255,8 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_lhs, left), - constant_simple(self.inner.cx, typeck_rhs, right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right), ) && l == r { @@ -714,9 +714,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { - let simple_const = self - .maybe_typeck_results - .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e)); + let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e) + }); // const hashing may result in the same hash as some unrelated node, so add a sort of // discriminant depending on which path we're choosing next diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1d5f1a2a2bb..af74e4b67c1 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -10,7 +10,6 @@ #![feature(assert_matches)] #![feature(unwrap_infallible)] #![recursion_limit = "512"] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( clippy::missing_errors_doc, clippy::missing_panics_doc, @@ -126,7 +125,7 @@ use rustc_span::{sym, Span}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{constant, mir_to_const, Constant}; +use crate::consts::{mir_to_const, ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -211,20 +210,24 @@ pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool { false } -/// Returns `true` if the given `HirId` is inside a constant context. +/// Checks if we are currently in a const context (e.g. `const fn`, `static`/`const` initializer). /// -/// This is the same as `is_inside_always_const_context`, but also includes -/// `const fn`. +/// The current context is determined based on the current body which is set before calling a lint's +/// entry point (any function on `LateLintPass`). If you need to check in a different context use +/// `tcx.hir().is_inside_const_context(_)`. /// -/// # Example -/// -/// ```rust,ignore -/// if in_constant(cx, expr.hir_id) { -/// // Do something -/// } -/// ``` -pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { - cx.tcx.hir().is_inside_const_context(id) +/// Do not call this unless the `LateContext` has an enclosing body. For release build this case +/// will safely return `false`, but debug builds will ICE. Note that `check_expr`, `check_block`, +/// `check_pat` and a few other entry points will always have an enclosing body. Some entry points +/// like `check_path` or `check_ty` may or may not have one. +pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { + debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body"); + cx.enclosing_body.is_some_and(|id| { + cx.tcx + .hir() + .body_const_context(cx.tcx.hir().body_owner_def_id(id)) + .is_some() + }) } /// Returns `true` if the given `HirId` is inside an always constant context. @@ -589,9 +592,8 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<It "u128" => SimplifiedType::Uint(UintTy::U128), "f32" => SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), - #[allow(trivial_casts)] _ => { - return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]) + return Result::<&[_], rustc_errors::ErrorGuaranteed>::Ok(&[]) .into_iter() .flatten() .copied(); @@ -1579,8 +1581,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) - && let Some(start_const) = constant(cx, cx.typeck_results(), start) + && let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) + && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { start_const == min_const } else { @@ -1592,8 +1594,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) - && let Some(end_const) = constant(cx, cx.typeck_results(), end) + && let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) + && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { end_const == max_const } else { @@ -1624,7 +1626,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool return true; } let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); - if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { + if let Some(Constant::Int(v)) = + ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e) + { return value == v; } false diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index d5a3d8b9e5a..684c645c199 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -16,7 +16,6 @@ pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", " pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; -pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index f206b2ceebc..553af913ef9 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -142,7 +142,7 @@ fn check_rvalue<'tcx>( // We cannot allow this for now. return Err((span, "unsizing casts are only allowed for references right now".into())); }; - let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); + let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { check_operand(tcx, op, span, body, msrv)?; // Casting/coercing things to slices is fine. diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 496c8f5b553..96dd3c55d37 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -6,7 +6,8 @@ use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LateContext}; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::{ @@ -17,6 +18,30 @@ use std::borrow::Cow; use std::fmt; use std::ops::Range; +pub trait HasSession { + fn sess(&self) -> &Session; +} +impl HasSession for Session { + fn sess(&self) -> &Session { + self + } +} +impl HasSession for TyCtxt<'_> { + fn sess(&self) -> &Session { + self.sess + } +} +impl HasSession for EarlyContext<'_> { + fn sess(&self) -> &Session { + ::rustc_lint::LintContext::sess(self) + } +} +impl HasSession for LateContext<'_> { + fn sess(&self) -> &Session { + self.tcx.sess() + } +} + /// Conversion of a value into the range portion of a `Span`. pub trait SpanRange: Sized { fn into_range(self) -> Range<BytePos>; @@ -71,19 +96,19 @@ impl IntoSpan for Range<BytePos> { pub trait SpanRangeExt: SpanRange { /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_text(self, cx: &impl LintContext) -> Option<SourceFileRange> { + fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> { get_source_text(cx.sess().source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { + fn with_source_text<T>(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { with_source_text(cx.sess().source_map(), self.into_range(), f) } /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { self.with_source_text(cx, pred).unwrap_or(false) } @@ -91,7 +116,7 @@ pub trait SpanRangeExt: SpanRange { /// and returns the value. Returns `None` if the source text cannot be retrieved. fn with_source_text_and_range<T>( self, - cx: &impl LintContext, + cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, ) -> Option<T> { with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) @@ -104,30 +129,30 @@ pub trait SpanRangeExt: SpanRange { /// The new range must reside within the same source file. fn map_range( self, - cx: &impl LintContext, + cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>, ) -> Option<Range<BytePos>> { map_range(cx.sess().source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. - fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> { + fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> { + fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> { trim_start(cx.sess().source_map(), self.into_range()) } /// Writes the referenced source text to the given writer. Will return `Err` if the source text /// could not be retrieved. - fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result { + fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result { write_source_text_to(cx.sess().source_map(), self.into_range(), dst) } /// Extracts the referenced source text as an owned string. - fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> { + fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> { self.with_source_text(cx, ToOwned::to_owned) } } @@ -227,15 +252,15 @@ impl SourceFileRange { } /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. -pub fn expr_block<T: LintContext>( - cx: &T, +pub fn expr_block( + sess: &impl HasSession, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option<Span>, app: &mut Applicability, ) -> String { - let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); + let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, _) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -260,13 +285,13 @@ pub fn expr_block<T: LintContext>( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { - first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { + first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } -fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { - let line_span = line_span(cx, span); - snippet_opt(cx, line_span).and_then(|snip| { +fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option<BytePos> { + let line_span = line_span(sess, span); + snippet_opt(sess, line_span).and_then(|snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -281,9 +306,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo /// let x = (); /// // ^^^^^^^^^^^^^^ /// ``` -fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { +fn line_span(sess: &impl HasSession, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); - let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap(); let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) @@ -297,13 +322,13 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { - snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +pub fn indent_of(sess: &impl HasSession, span: Span) -> Option<usize> { + snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> { - snippet_opt(cx, line_span(cx, span)).map(|mut s| { +pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> { + snippet_opt(sess, line_span(sess, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -315,8 +340,8 @@ pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> { // sources that the user has no control over. // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. -pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { - if let Some(snippet) = snippet_opt(cx, span) { +pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sess, span) { if snippet.is_empty() { return false; } @@ -407,8 +432,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, /// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` -pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) +pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -417,13 +442,13 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it to /// `HasPlaceholders` -pub fn snippet_with_applicability<'a, T: LintContext>( - cx: &T, +pub fn snippet_with_applicability<'a>( + sess: &impl HasSession, span: Span, default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - snippet_with_applicability_sess(cx.sess(), span, default, applicability) + snippet_with_applicability_sess(sess.sess(), span, default, applicability) } fn snippet_with_applicability_sess<'a>( @@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt_sess(sess, span).map_or_else( + snippet_opt(sess, span).map_or_else( || { if *applicability == Applicability::MachineApplicable { *applicability = Applicability::HasPlaceholders; @@ -447,12 +472,8 @@ fn snippet_with_applicability_sess<'a>( } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> { - snippet_opt_sess(cx.sess(), span) -} - -fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> { - sess.source_map().span_to_snippet(span).ok() +pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> { + sess.sess().source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -489,41 +510,41 @@ fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block<'a, T: LintContext>( - cx: &T, +pub fn snippet_block<'a>( + sess: &impl HasSession, span: Span, default: &'a str, indent_relative_to: Option<Span>, ) -> Cow<'a, str> { - let snip = snippet(cx, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let snip = snippet(sess, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); reindent_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of /// `snippet_with_applicability`. pub fn snippet_block_with_applicability<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, default: &'a str, indent_relative_to: Option<Span>, applicability: &mut Applicability, ) -> Cow<'a, str> { - let snip = snippet_with_applicability(cx, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let snip = snippet_with_applicability(sess, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); reindent_multiline(snip, true, indent) } pub fn snippet_block_with_context<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, outer: SyntaxContext, default: &'a str, indent_relative_to: Option<Span>, app: &mut Applicability, ) -> (Cow<'a, str>, bool) { - let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); (reindent_multiline(snip, true, indent), from_macro) } @@ -537,13 +558,13 @@ pub fn snippet_block_with_context<'a>( /// /// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - snippet_with_context_sess(cx.sess(), span, outer, default, applicability) + snippet_with_context_sess(sess.sess(), span, outer, default, applicability) } fn snippet_with_context_sess<'a>( @@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { - let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); +pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span { + let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true); extended.with_lo(extended.lo() - BytePos(1)) } /// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// character (or a single byte with `ascii_only`) pub fn str_literal_to_char_literal( - cx: &LateContext<'_>, + sess: &impl HasSession, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal( } && len == 1 { - let snip = snippet_with_applicability(cx, expr.span, string, applicability); + let snip = snippet_with_applicability(sess, expr.span, string, applicability); let ch = if let StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 812fb647fda..bd48990aea9 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -525,19 +525,6 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(*ty, count + 1) - } else { - (ty, count) - } - } - peel(ty, 0) -} - /// Peels off all references on the type. Returns the underlying type, the number of references /// removed, and whether the pointer is ultimately mutable or not. pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 7066c9ad2b9..2a5d3536ff6 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,6 +1,7 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; +use rustc_ast::visit::{try_visit, VisitorResult}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; @@ -50,16 +51,17 @@ impl Continue for Descend { /// A type which can be visited. pub trait Visitable<'tcx> { /// Calls the corresponding `visit_*` function on the visitor. - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V); + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result; } impl<'tcx, T> Visitable<'tcx> for &'tcx [T] where &'tcx T: Visitable<'tcx>, { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { for x in self { - x.visit(visitor); + try_visit!(x.visit(visitor)); } + V::Result::output() } } impl<'tcx, A, B> Visitable<'tcx> for (A, B) @@ -67,27 +69,28 @@ where A: Visitable<'tcx>, B: Visitable<'tcx>, { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { let (a, b) = self; - a.visit(visitor); - b.visit(visitor); + try_visit!(a.visit(visitor)); + b.visit(visitor) } } impl<'tcx, T> Visitable<'tcx> for Option<T> where T: Visitable<'tcx>, { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { if let Some(x) = self { - x.visit(visitor); + try_visit!(x.visit(visitor)); } + V::Result::output() } } macro_rules! visitable_ref { ($t:ident, $f:ident) => { impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { - visitor.$f(self); + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { + visitor.$f(self) } } }; @@ -104,45 +107,37 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B> { - struct V<B, F> { + struct V<F> { f: F, - res: Option<B>, } - impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> { - type Result = ControlFlow<()>; + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<F> { + type Result = ControlFlow<B>; - fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> ControlFlow<()> { - if self.res.is_some() { - return ControlFlow::Break(()); - } + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result { match (self.f)(e) { ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), - ControlFlow::Break(b) => { - self.res = Some(b); - ControlFlow::Break(()) - }, + ControlFlow::Break(b) => ControlFlow::Break(b), ControlFlow::Continue(_) => ControlFlow::Continue(()), } } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> ControlFlow<()> { + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result { ControlFlow::Continue(()) } - fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> ControlFlow<()> { + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result { ControlFlow::Continue(()) } - fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> ControlFlow<()> { + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result { ControlFlow::Continue(()) } // Avoid monomorphising all `visit_*` functions. - fn visit_nested_item(&mut self, _: ItemId) -> ControlFlow<()> { + fn visit_nested_item(&mut self, _: ItemId) -> Self::Result { ControlFlow::Continue(()) } } - let mut v = V { f, res: None }; - node.visit(&mut v); - v.res + let mut v = V { f }; + node.visit(&mut v).break_value() } /// Calls the given function once for each expression contained. This will enter bodies, but not @@ -152,44 +147,47 @@ pub fn for_each_expr<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B> { - struct V<'tcx, B, F> { + struct V<'tcx, F> { tcx: TyCtxt<'tcx>, f: F, - res: Option<B>, } - impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> { + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, F> { type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow<B>; + fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() } - fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { - if self.res.is_some() { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result { match (self.f)(e) { ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), - ControlFlow::Break(b) => self.res = Some(b), - ControlFlow::Continue(_) => (), + ControlFlow::Break(b) => ControlFlow::Break(b), + ControlFlow::Continue(_) => ControlFlow::Continue(()), } } // Only walk closures - fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + fn visit_anon_const(&mut self, _: &'tcx AnonConst) -> Self::Result { + ControlFlow::Continue(()) + } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} - fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} - fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result { + ControlFlow::Continue(()) + } + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result { + ControlFlow::Continue(()) + } + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result { + ControlFlow::Continue(()) + } // Avoid monomorphising all `visit_*` functions. - fn visit_nested_item(&mut self, _: ItemId) {} + fn visit_nested_item(&mut self, _: ItemId) -> Self::Result { + ControlFlow::Continue(()) + } } - let mut v = V { - tcx: cx.tcx, - f, - res: None, - }; - node.visit(&mut v); - v.res + let mut v = V { tcx: cx.tcx, f }; + node.visit(&mut v).break_value() } /// returns `true` if expr contains match expr desugared from try diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 80106f683c2..31270241b0b 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -11,6 +11,3 @@ proc-macro = true itertools = "0.12" quote = "1.0.21" syn = "2.0" - -[features] -deny-warnings = [] diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs index 25b2fc9395c..ca070f6c250 100644 --- a/src/tools/clippy/declare_clippy_lint/src/lib.rs +++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs @@ -1,5 +1,4 @@ #![feature(let_chains)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index 350418eeeb8..b0e4e3e3e57 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -25,6 +25,3 @@ tar = "0.4" toml = "0.7.3" ureq = { version = "2.2", features = ["json"] } walkdir = "2.3" - -[features] -deny-warnings = [] diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index 6bec1753fc7..bd4fcc5e337 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -33,13 +33,13 @@ pub(crate) struct LintcheckConfig { /// Runs cargo clippy --fix and checks if all suggestions apply #[clap(long, conflicts_with("max_jobs"))] pub fix: bool, - /// Apply a filter to only collect specified lints, this also overrides `allow` attributes + /// Apply a filter to only collect specified lints #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)] pub lint_filter: Vec<String>, - /// Set all lints to the "warn" lint level, even resitriction ones. Usually, - /// it's better to use `--filter` instead + /// Check all Clippy lints, by default only `clippy::all` and `clippy::pedantic` are checked. + /// Usually, it's better to use `--filter` instead #[clap(long, conflicts_with("lint_filter"))] - pub warn_all: bool, + pub all_lints: bool, /// Set the output format of the log file #[clap(long, short, default_value = "text")] pub format: OutputFormat, diff --git a/src/tools/clippy/lintcheck/src/input.rs b/src/tools/clippy/lintcheck/src/input.rs index 3b263674aa8..3383d50fa02 100644 --- a/src/tools/clippy/lintcheck/src/input.rs +++ b/src/tools/clippy/lintcheck/src/input.rs @@ -98,12 +98,12 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateWithSource>, RecursiveOptions) let crate_list: SourceList = toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec<TomlCrate> = crate_list.crates.into_values().collect(); + let toml_crates: Vec<TomlCrate> = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => - // multiple Cratesources) + // multiple CrateSources) let mut crate_sources = Vec::new(); - for tk in tomlcrates { + for tk in toml_crates { if let Some(ref path) = tk.path { crate_sources.push(CrateWithSource { name: tk.name.clone(), diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 0dd62ded293..acb6eaa5278 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -119,7 +119,8 @@ impl Crate { cmd.arg(if config.fix { "fix" } else { "check" }) .arg("--quiet") .current_dir(&self.path) - .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")); + .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")) + .env("CLIPPY_DISABLE_DOCS_LINKS", "1"); if let Some(server) = server { // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to @@ -284,29 +285,24 @@ fn lintcheck(config: LintcheckConfig) { let (crates, recursive_options) = read_crates(&config.sources_toml_path); let counter = AtomicUsize::new(1); - let mut lint_level_args: Vec<String> = vec![]; + let mut lint_level_args: Vec<String> = vec!["--cap-lints=allow".into()]; if config.lint_filter.is_empty() { - lint_level_args.push("--cap-lints=warn".to_string()); - - // Set allow-by-default to warn - if config.warn_all { - [ + let groups = if config.all_lints { + &[ + "clippy::all", "clippy::cargo", "clippy::nursery", "clippy::pedantic", "clippy::restriction", - ] + ][..] + } else { + &["clippy::all", "clippy::pedantic"] + }; + groups .iter() - .map(|group| format!("--warn={group}")) + .map(|group| format!("--force-warn={group}")) .collect_into(&mut lint_level_args); - } else { - ["clippy::cargo", "clippy::pedantic"] - .iter() - .map(|group| format!("--warn={group}")) - .collect_into(&mut lint_level_args); - } } else { - lint_level_args.push("--cap-lints=allow".to_string()); config .lint_filter .iter() diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 69fb11a4824..5fbe4e544a9 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-07-25" +channel = "nightly-2024-08-08" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml index 877049ae7d0..37b592da132 100644 --- a/src/tools/clippy/rustc_tools_util/Cargo.toml +++ b/src/tools/clippy/rustc_tools_util/Cargo.toml @@ -10,6 +10,3 @@ categories = ["development-tools"] edition = "2018" [dependencies] - -[features] -deny-warnings = [] diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs index 4c1d8c3733d..2cc38130472 100644 --- a/src/tools/clippy/rustc_tools_util/src/lib.rs +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] +use std::str; /// This macro creates the version string during compilation from the /// current environment @@ -101,49 +101,46 @@ impl std::fmt::Debug for VersionInfo { #[must_use] pub fn get_commit_hash() -> Option<String> { - std::process::Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) + let output = std::process::Command::new("git") + .args(["rev-parse", "HEAD"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let mut stdout = output.status.success().then_some(output.stdout)?; + stdout.truncate(10); + String::from_utf8(stdout).ok() } #[must_use] pub fn get_commit_date() -> Option<String> { - std::process::Command::new("git") + let output = std::process::Command::new("git") .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let stdout = output.status.success().then_some(output.stdout)?; + String::from_utf8(stdout).ok() } #[must_use] pub fn get_channel() -> String { - match std::env::var("CFG_RELEASE_CHANNEL") { - Ok(channel) => channel, - Err(_) => { - // if that failed, try to ask rustc -V, do some parsing and find out - match std::process::Command::new("rustc") - .arg("-V") - .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) - { - Some(rustc_output) => { - if rustc_output.contains("beta") { - String::from("beta") - } else if rustc_output.contains("stable") { - String::from("stable") - } else { - // default to nightly if we fail to parse - String::from("nightly") - } - }, - // default to nightly - None => String::from("nightly"), + if let Ok(channel) = std::env::var("CFG_RELEASE_CHANNEL") { + return channel; + } + + // if that failed, try to ask rustc -V, do some parsing and find out + if let Ok(output) = std::process::Command::new("rustc").arg("-V").output() { + if output.status.success() { + if let Ok(rustc_output) = str::from_utf8(&output.stdout) { + if rustc_output.contains("beta") { + return String::from("beta"); + } else if rustc_output.contains("stable") { + return String::from("stable"); + } } - }, + } } + + // default to nightly + String::from("nightly") } #[cfg(test)] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 3fafe2427a2..0ac3f35b446 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -2,7 +2,6 @@ #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] #![feature(let_chains)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints @@ -151,7 +150,6 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_config::Conf::read(sess, &conf_path); clippy_lints::register_lints(lint_store, conf); clippy_lints::register_pre_expansion_lints(lint_store, conf); - clippy_lints::register_renamed(lint_store); })); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 30beaae34d2..c9af2138a72 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/tools/clippy/tests/check-fmt.rs b/src/tools/clippy/tests/check-fmt.rs index e106583de4a..cd3fed896c2 100644 --- a/src/tools/clippy/tests/check-fmt.rs +++ b/src/tools/clippy/tests/check-fmt.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use std::path::PathBuf; diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 4e5120406b0..c7080e5dcdc 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] @@ -208,7 +207,8 @@ fn run_ui_toml() { ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, - |config, path, _file_contents| { + |config, file_contents| { + let path = file_contents.span().file; config .program .envs @@ -260,7 +260,7 @@ fn run_ui_cargo() { path.ends_with("Cargo.toml") .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, - |_config, _path, _file_contents| {}, + |_config, _file_contents| {}, status_emitter::Text::from(args.format), ) .unwrap(); diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index c8a761bf509..8d10d5e7161 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -3,7 +3,6 @@ //! //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs index 77b7bb6a7bf..13cf36823c5 100644 --- a/src/tools/clippy/tests/integration.rs +++ b/src/tools/clippy/tests/integration.rs @@ -8,7 +8,6 @@ //! Clippy doesn't produce an ICE. Lint warnings are ignored by this test. #![cfg(feature = "integration")] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use std::env; diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs index 6ce7e44474d..7ed1f485c1c 100644 --- a/src/tools/clippy/tests/lint_message_convention.rs +++ b/src/tools/clippy/tests/lint_message_convention.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use std::ffi::OsStr; diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index 141c11ddb47..a8225d037e8 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] #![feature(path_file_prefix)] diff --git a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs deleted file mode 100644 index c8961d5e1f0..00000000000 --- a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -#[macro_use] -extern crate clippy_lints; -use clippy_lints::deprecated_lints::ClippyDeprecatedLint; - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// TODO - #[clippy::version = "1.63.0"] - pub COOL_LINT_DEFAULT, - "default deprecation note" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `cooler_lint` - #[clippy::version = "1.63.0"] - pub COOL_LINT, - "this lint has been replaced by `cooler_lint`" -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr deleted file mode 100644 index 3b7c747c23f..00000000000 --- a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason - --> tests/ui-internal/default_deprecation_reason.rs:8:1 - | -LL | / declare_deprecated_lint! { -LL | | /// ### What it does -LL | | /// Nothing. This lint has been deprecated. -LL | | /// -... | -LL | | "default deprecation note" -LL | | } - | |_^ - | -note: the lint level is defined here - --> tests/ui-internal/default_deprecation_reason.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_deprecation_reason)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in the macro `declare_deprecated_lint` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 4375f324aca..858aab528a9 100644 --- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -1,15 +1,19 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs #![rustfmt::skip] #![feature(custom_inner_attributes)] -#![allow(unused)] -#![allow(clippy::let_and_return)] -#![allow(clippy::redundant_closure_call)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::never_loop)] -#![allow(clippy::needless_if)] #![warn(clippy::excessive_nesting)] -#![allow(clippy::collapsible_if, clippy::blocks_in_conditions)] +#![allow( + unused, + clippy::let_and_return, + clippy::redundant_closure_call, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::never_loop, + clippy::needless_if, + clippy::collapsible_if, + clippy::blocks_in_conditions, + clippy::single_match, +)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr index dafcd442055..ccdaecdd481 100644 --- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr +++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.stderr @@ -1,5 +1,5 @@ error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:21:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:25:25 | LL | let w = { 3 }; | ^^^^^ @@ -9,7 +9,7 @@ LL | let w = { 3 }; = help: to override `-D warnings` add `#[allow(clippy::excessive_nesting)]` error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:67:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:71:17 | LL | / impl C { LL | | pub fn c() {} @@ -19,7 +19,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:81:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:85:25 | LL | let x = { 1 }; // not a warning, but cc is | ^^^^^ @@ -27,7 +27,7 @@ LL | let x = { 1 }; // not a warning, but cc is = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:98:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:102:17 | LL | / pub mod e { LL | | pub mod f {} @@ -37,7 +37,7 @@ LL | | } // not here = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:111:18 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:115:18 | LL | a_but_not({{{{{{{{0}}}}}}}}); | ^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | a_but_not({{{{{{{{0}}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:112:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:116:12 | LL | a.a({{{{{{{{{0}}}}}}}}}); | ^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | a.a({{{{{{{{{0}}}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:113:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:117:12 | LL | (0, {{{{{{{1}}}}}}}); | ^^^^^^^^^ @@ -61,7 +61,7 @@ LL | (0, {{{{{{{1}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:118:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:122:25 | LL | if true { | _________________________^ @@ -74,7 +74,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:130:29 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:134:29 | LL | let z = (|| { | _____________________________^ @@ -86,7 +86,7 @@ LL | | })(); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:149:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:13 | LL | y += {{{{{5}}}}}; | ^^^^^ @@ -94,7 +94,7 @@ LL | y += {{{{{5}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:150:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:20 | LL | let z = y + {{{{{{{{{5}}}}}}}}}; | ^^^^^^^^^^^^^ @@ -102,7 +102,7 @@ LL | let z = y + {{{{{{{{{5}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:151:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:155:12 | LL | [0, {{{{{{{{{{0}}}}}}}}}}]; | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | [0, {{{{{{{{{{0}}}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:152:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:25 | LL | let mut xx = [0; {{{{{{{{100}}}}}}}}]; | ^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | let mut xx = [0; {{{{{{{{100}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:11 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:157:11 | LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:13 | LL | &mut {{{{{{{{{{y}}}}}}}}}}; | ^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | &mut {{{{{{{{{{y}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:17 | LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} | ^^^^ @@ -142,7 +142,7 @@ LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:28 | LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} | ^^^^^^^^^^ @@ -150,7 +150,7 @@ LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:28 | LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} | ^^^^^^^^^^^^^ @@ -158,7 +158,7 @@ LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:48 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:48 | LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} | ^^^^^^^^ @@ -166,7 +166,7 @@ LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:14 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:14 | LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} | ^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:35 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:35 | LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} | ^^^^^^^^^^^^ @@ -182,7 +182,7 @@ LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:23 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:23 | LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8 | LL | {{{{1;}}}}..{{{{{{3}}}}}}; | ^^^^ @@ -198,7 +198,7 @@ LL | {{{{1;}}}}..{{{{{{3}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:20 | LL | {{{{1;}}}}..{{{{{{3}}}}}}; | ^^^^^^^ @@ -206,7 +206,7 @@ LL | {{{{1;}}}}..{{{{{{3}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:8 | LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; | ^^^^ @@ -214,7 +214,7 @@ LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:21 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:21 | LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:10 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:10 | LL | ..{{{{{{{5}}}}}}}; | ^^^^^^^^^ @@ -230,7 +230,7 @@ LL | ..{{{{{{{5}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:167:11 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:11 | LL | ..={{{{{3}}}}}; | ^^^^^ @@ -238,7 +238,7 @@ LL | ..={{{{{3}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:172:8 | LL | {{{{{1;}}}}}..; | ^^^^^^ @@ -246,7 +246,7 @@ LL | {{{{{1;}}}}}..; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20 | LL | loop { break {{{{1}}}} }; | ^^^^^ @@ -254,7 +254,7 @@ LL | loop { break {{{{1}}}} }; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:13 | LL | loop {{{{{{}}}}}} | ^^^^^^ @@ -262,7 +262,7 @@ LL | loop {{{{{{}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:173:14 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:177:14 | LL | match {{{{{{true}}}}}} { | ^^^^^^^^^^ @@ -270,7 +270,7 @@ LL | match {{{{{{true}}}}}} { = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:178:20 | LL | true => {{{{}}}}, | ^^ @@ -278,7 +278,7 @@ LL | true => {{{{}}}}, = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:21 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:179:21 | LL | false => {{{{}}}}, | ^^ @@ -286,7 +286,7 @@ LL | false => {{{{}}}}, = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:181:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:185:17 | LL | / { LL | | println!("warning! :)"); @@ -296,7 +296,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:190:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:194:28 | LL | async fn c() -> u32 {{{{{{{0}}}}}}} | ^^^^^^^^^ @@ -304,7 +304,7 @@ LL | async fn c() -> u32 {{{{{{{0}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:196:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:200:8 | LL | {{{{b().await}}}}; | ^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr index 41d5afd3efe..320578bfabc 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -1,11 +1,15 @@ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::get-unwrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::get_unwrap)]` +help: using `[]` is clearer and more concise + | +LL | let _ = &boxed_slice[1]; + | ~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17 @@ -18,11 +22,16 @@ LL | let _ = boxed_slice.get(1).unwrap(); = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17 | LL | let _ = some_slice.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_slice[0]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17 @@ -33,11 +42,16 @@ LL | let _ = some_slice.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17 | LL | let _ = some_vec.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vec[0]; + | ~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17 @@ -48,11 +62,16 @@ LL | let _ = some_vec.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a VecDeque --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17 | LL | let _ = some_vecdeque.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vecdeque[0]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17 @@ -63,11 +82,16 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a HashMap --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17 | LL | let _ = some_hashmap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_hashmap[&1]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17 @@ -78,11 +102,16 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a BTreeMap --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17 | LL | let _ = some_btreemap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_btreemap[&1]; + | ~~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17 @@ -93,11 +122,16 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _: u8 = boxed_slice[1]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:22 @@ -108,11 +142,16 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | boxed_slice[0] = 1; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:10 @@ -123,11 +162,16 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:9 | LL | *some_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_slice[0] = 1; + | ~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:10 @@ -138,11 +182,16 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:9 | LL | *some_vec.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vec[0] = 1; + | ~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:10 @@ -153,11 +202,16 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a VecDeque --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vecdeque[0] = 1; + | ~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:10 @@ -168,11 +222,16 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17 @@ -183,11 +242,16 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17 @@ -198,17 +262,27 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:75:13 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &boxed_slice[1]; + | ~~~~~~~~~~~~~~~ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:93:17 | LL | let _ = Box::new([0]).get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&Box::new([0])[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &Box::new([0])[1]; + | ~~~~~~~~~~~~~~~~~ error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed index 60f6a385fc5..b376d55a402 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.fixed +++ b/src/tools/clippy/tests/ui/assigning_clones.fixed @@ -3,6 +3,7 @@ #![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 #![allow(clippy::needless_late_init)] #![allow(clippy::box_collection)] +#![allow(clippy::boxed_local)] #![warn(clippy::assigning_clones)] use std::borrow::ToOwned; @@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom { } } +// Deref handling +fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + (*a).clone_from(&b); +} + +fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) { + (*a).clone_from(&b); +} + +fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + (*a).clone_from(&b); +} + +fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) { + a.clone_from(&b); +} + +fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + Clone::clone_from(&mut *a, &b); +} + +fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + Clone::clone_from(&mut *a, &b); +} + // ToOwned fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { ref_str.clone_into(mut_string); @@ -328,3 +354,45 @@ mod borrowck_conflicts { path = path.components().as_path().to_owned(); } } + +struct DerefWrapper<T>(T); + +impl<T> Deref for DerefWrapper<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapper<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +struct DerefWrapperWithClone<T>(T); + +impl<T> Deref for DerefWrapperWithClone<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapperWithClone<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<T: Clone> Clone for DerefWrapperWithClone<T> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + fn clone_from(&mut self, source: &Self) { + *self = Self(source.0.clone()); + } +} diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs index 6eb85be511a..11a5d4459c3 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.rs +++ b/src/tools/clippy/tests/ui/assigning_clones.rs @@ -3,6 +3,7 @@ #![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 #![allow(clippy::needless_late_init)] #![allow(clippy::box_collection)] +#![allow(clippy::boxed_local)] #![warn(clippy::assigning_clones)] use std::borrow::ToOwned; @@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom { } } +// Deref handling +fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + *a = b.clone(); +} + +fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) { + *a = b.clone(); +} + +fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + *a = b.clone(); +} + +fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) { + *a = b.clone(); +} + +fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + *a = Clone::clone(&b); +} + +fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + *a = Clone::clone(&b); +} + // ToOwned fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { *mut_string = ref_str.to_owned(); @@ -328,3 +354,45 @@ mod borrowck_conflicts { path = path.components().as_path().to_owned(); } } + +struct DerefWrapper<T>(T); + +impl<T> Deref for DerefWrapper<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapper<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +struct DerefWrapperWithClone<T>(T); + +impl<T> Deref for DerefWrapperWithClone<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapperWithClone<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<T: Clone> Clone for DerefWrapperWithClone<T> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + fn clone_from(&mut self, source: &Self) { + *self = Self(source.0.clone()); + } +} diff --git a/src/tools/clippy/tests/ui/assigning_clones.stderr b/src/tools/clippy/tests/ui/assigning_clones.stderr index a68516376ab..19724a6d4ec 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.stderr +++ b/src/tools/clippy/tests/ui/assigning_clones.stderr @@ -1,5 +1,5 @@ error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:24:5 + --> tests/ui/assigning_clones.rs:25:5 | LL | *mut_thing = value_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(&value_thing)` @@ -8,142 +8,178 @@ LL | *mut_thing = value_thing.clone(); = help: to override `-D warnings` add `#[allow(clippy::assigning_clones)]` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:28:5 + --> tests/ui/assigning_clones.rs:29:5 | LL | *mut_thing = ref_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:32:5 + --> tests/ui/assigning_clones.rs:33:5 | LL | mut_thing = ref_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:36:5 + --> tests/ui/assigning_clones.rs:37:5 | LL | *mut_thing = Clone::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:40:5 + --> tests/ui/assigning_clones.rs:41:5 | LL | mut_thing = Clone::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:44:5 + --> tests/ui/assigning_clones.rs:45:5 | LL | *mut_thing = Clone::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:48:5 + --> tests/ui/assigning_clones.rs:49:5 | LL | *mut_thing = HasCloneFrom::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:52:5 + --> tests/ui/assigning_clones.rs:53:5 | LL | *mut_thing = <HasCloneFrom as Clone>::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:57:5 + --> tests/ui/assigning_clones.rs:58:5 | LL | *(mut_thing + &mut HasCloneFrom) = ref_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `(mut_thing + &mut HasCloneFrom).clone_from(ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:62:5 + --> tests/ui/assigning_clones.rs:63:5 | LL | *mut_thing = (ref_thing + ref_thing).clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:67:5 + --> tests/ui/assigning_clones.rs:68:5 | LL | s = format!("{} {}", "hello", "world").clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:72:5 + --> tests/ui/assigning_clones.rs:73:5 | LL | s = Clone::clone(&format!("{} {}", "hello", "world")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:78:9 + --> tests/ui/assigning_clones.rs:79:9 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:149:5 + --> tests/ui/assigning_clones.rs:150:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:156:5 + --> tests/ui/assigning_clones.rs:157:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:157:5 + --> tests/ui/assigning_clones.rs:158:5 | LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:188:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:192:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:196:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:200:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:204:5 + | +LL | *a = Clone::clone(&b); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut *a, &b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:208:5 + | +LL | *a = Clone::clone(&b); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut *a, &b)` + error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:187:5 + --> tests/ui/assigning_clones.rs:213:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:191:5 + --> tests/ui/assigning_clones.rs:217:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:212:5 + --> tests/ui/assigning_clones.rs:238:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:216:5 + --> tests/ui/assigning_clones.rs:242:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:220:5 + --> tests/ui/assigning_clones.rs:246:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:224:5 + --> tests/ui/assigning_clones.rs:250:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:229:5 + --> tests/ui/assigning_clones.rs:255:5 | LL | s = format!("{} {}", "hello", "world").to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:234:5 + --> tests/ui/assigning_clones.rs:260:5 | LL | s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)` -error: aborting due to 24 previous errors +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr index b15857c325a..2adaecc96d6 100644 --- a/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr +++ b/src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try +help: use `map` instead | LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ @@ -20,7 +20,7 @@ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try +help: use `map` instead | LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ @@ -31,7 +31,7 @@ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try +help: use `map_err` instead | LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~ @@ -48,7 +48,7 @@ LL | | } LL | | }); | |______^ | -help: try +help: use `map` instead | LL ~ Some("42").map(|s| { LL | if { @@ -82,7 +82,7 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try +help: use `map` instead | LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); | ~~~ ~~~~ ~~~~~~~~ diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr index 01627ff5b94..4b4618ee1bb 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr @@ -9,14 +9,13 @@ note: the lint level is defined here | LL | #![deny(clippy::implicit_hasher)] | ^^^^^^^^^^^^^^^^^^^^^^^ -help: consider adding a type parameter +help: add a type parameter for `BuildHasher` | -LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { - | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~ -help: ...and use generic constructor +LL ~ pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { +LL | +LL | let _ = [0u8; 0]; +LL ~ let _: HashSet<usize> = HashSet::default(); | -LL | let _: HashSet<usize> = HashSet::default(); - | ~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/crashes/ice-6254.rs b/src/tools/clippy/tests/ui/crashes/ice-6254.rs index 8af60890390..aaca32ab2d9 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6254.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6254.rs @@ -2,7 +2,8 @@ // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 -#[allow(clippy::derive_partial_eq_without_eq)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::single_match)] + #[derive(PartialEq)] struct Foo(i32); const FOO_REF_REF: &&Foo = &&Foo(42); diff --git a/src/tools/clippy/tests/ui/create_dir.stderr b/src/tools/clippy/tests/ui/create_dir.stderr index 9c6e640ca78..ab51705bb55 100644 --- a/src/tools/clippy/tests/ui/create_dir.stderr +++ b/src/tools/clippy/tests/ui/create_dir.stderr @@ -2,16 +2,25 @@ error: calling `std::fs::create_dir` where there may be a better way --> tests/ui/create_dir.rs:10:5 | LL | std::fs::create_dir("foo"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::create-dir` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::create_dir)]` +help: consider calling `std::fs::create_dir_all` instead + | +LL | create_dir_all("foo"); + | ~~~~~~~~~~~~~~~~~~~~~ error: calling `std::fs::create_dir` where there may be a better way --> tests/ui/create_dir.rs:11:5 | LL | std::fs::create_dir("bar").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider calling `std::fs::create_dir_all` instead + | +LL | create_dir_all("bar").unwrap(); + | ~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index 7d3c3f7c918..b3d74b9ff61 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr @@ -81,7 +81,7 @@ error: the `dbg!` macro is intended as a debugging tool --> tests/ui/dbg_macro/dbg_macro.rs:48:5 | LL | dbg!(); - | ^^^^^^^ + | ^^^^^^ | help: remove the invocation before committing it to a version control system | @@ -136,7 +136,7 @@ error: the `dbg!` macro is intended as a debugging tool --> tests/ui/dbg_macro/dbg_macro.rs:43:13 | LL | dbg!(); - | ^^^^^^^ + | ^^^^^^ ... LL | expand_to_dbg!(); | ---------------- in this macro invocation diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr index 16e51f4742e..b8e91906b93 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr @@ -2,7 +2,7 @@ error: the `dbg!` macro is intended as a debugging tool --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5 | LL | dbg!(); - | ^^^^^^^ + | ^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index d3c34fb3716..5617db90a47 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -2,23 +2,18 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -#![warn(clippy::should_assert_eq)] -#![warn(clippy::extend_from_slice)] -#![warn(clippy::range_step_by_zero)] -#![warn(clippy::unstable_as_slice)] -#![warn(clippy::unstable_as_mut_slice)] -#![warn(clippy::misaligned_transmute)] -#![warn(clippy::assign_ops)] -#![warn(clippy::if_let_redundant_pattern_matching)] -#![warn(clippy::unsafe_vector_initialization)] -#![warn(clippy::unused_collect)] -#![warn(clippy::replace_consts)] -#![warn(clippy::regex_macro)] -#![warn(clippy::find_map)] -#![warn(clippy::filter_map)] -#![warn(clippy::pub_enum_variant_names)] -#![warn(clippy::wrong_pub_self_convention)] -#![warn(clippy::maybe_misused_cfg)] -#![warn(clippy::mismatched_target_os)] +#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` +#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` +#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` +#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` +#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` +#![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` +#![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` +#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` +#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` +#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` +#![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index 49b90c70c06..b3e1646c804 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -1,4 +1,4 @@ -error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011 +error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can --> tests/ui/deprecated.rs:5:9 | LL | #![warn(clippy::should_assert_eq)] @@ -7,107 +7,77 @@ LL | #![warn(clippy::should_assert_eq)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` -error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice +error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization --> tests/ui/deprecated.rs:6:9 | LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays +error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator --> tests/ui/deprecated.rs:7:9 | LL | #![warn(clippy::range_step_by_zero)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable --> tests/ui/deprecated.rs:8:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable --> tests/ui/deprecated.rs:9:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr +error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` --> tests/ui/deprecated.rs:10:9 | LL | #![warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless +error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy --> tests/ui/deprecated.rs:11:9 | LL | #![warn(clippy::assign_ops)] | ^^^^^^^^^^^^^^^^^^ -error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower --> tests/ui/deprecated.rs:12:9 | -LL | #![warn(clippy::if_let_redundant_pattern_matching)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior - --> tests/ui/deprecated.rs:13:9 - | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint - --> tests/ui/deprecated.rs:14:9 +error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` + --> tests/ui/deprecated.rs:13:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants - --> tests/ui/deprecated.rs:15:9 +error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated + --> tests/ui/deprecated.rs:14:9 | LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 - --> tests/ui/deprecated.rs:16:9 +error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 + --> tests/ui/deprecated.rs:15:9 | LL | #![warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint - --> tests/ui/deprecated.rs:17:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ - -error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint - --> tests/ui/deprecated.rs:18:9 - | -LL | #![warn(clippy::filter_map)] - | ^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items - --> tests/ui/deprecated.rs:19:9 +error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:16:9 | LL | #![warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items - --> tests/ui/deprecated.rs:20:9 +error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:17:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::maybe_misused_cfg` has been removed: this lint has been replaced by `unexpected_cfgs` - --> tests/ui/deprecated.rs:21:9 - | -LL | #![warn(clippy::maybe_misused_cfg)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::mismatched_target_os` has been removed: this lint has been replaced by `unexpected_cfgs` - --> tests/ui/deprecated.rs:22:9 - | -LL | #![warn(clippy::mismatched_target_os)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 18 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/deprecated_old.rs b/src/tools/clippy/tests/ui/deprecated_old.rs deleted file mode 100644 index 356ad5f060b..00000000000 --- a/src/tools/clippy/tests/ui/deprecated_old.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[warn(unstable_as_slice)] -//~^ ERROR: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized -//~| NOTE: `-D renamed-and-removed-lints` implied by `-D warnings` -#[warn(unstable_as_mut_slice)] -//~^ ERROR: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been st -#[warn(misaligned_transmute)] -//~^ ERROR: lint `misaligned_transmute` has been removed: this lint has been split into ca - -fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated_old.stderr b/src/tools/clippy/tests/ui/deprecated_old.stderr deleted file mode 100644 index 685bca64df5..00000000000 --- a/src/tools/clippy/tests/ui/deprecated_old.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 - --> tests/ui/deprecated_old.rs:1:8 - | -LL | #[warn(unstable_as_slice)] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` - -error: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 - --> tests/ui/deprecated_old.rs:4:8 - | -LL | #[warn(unstable_as_mut_slice)] - | ^^^^^^^^^^^^^^^^^^^^^ - -error: lint `misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr - --> tests/ui/deprecated_old.rs:6:8 - | -LL | #[warn(misaligned_transmute)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/deref_by_slicing.fixed b/src/tools/clippy/tests/ui/deref_by_slicing.fixed index a3c2e845666..87b33b1f881 100644 --- a/src/tools/clippy/tests/ui/deref_by_slicing.fixed +++ b/src/tools/clippy/tests/ui/deref_by_slicing.fixed @@ -25,4 +25,8 @@ fn main() { let bytes: &[u8] = &[]; let _ = (&*bytes).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice + + // issue 12751 + let a = &mut [1, 2, 3][..]; + let _ = &*a; } diff --git a/src/tools/clippy/tests/ui/deref_by_slicing.rs b/src/tools/clippy/tests/ui/deref_by_slicing.rs index 5b4a73712ee..8d8882a1781 100644 --- a/src/tools/clippy/tests/ui/deref_by_slicing.rs +++ b/src/tools/clippy/tests/ui/deref_by_slicing.rs @@ -25,4 +25,8 @@ fn main() { let bytes: &[u8] = &[]; let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice + + // issue 12751 + let a = &mut [1, 2, 3][..]; + let _ = &a[..]; } diff --git a/src/tools/clippy/tests/ui/deref_by_slicing.stderr b/src/tools/clippy/tests/ui/deref_by_slicing.stderr index 17b00610899..ceb9ab6db73 100644 --- a/src/tools/clippy/tests/ui/deref_by_slicing.stderr +++ b/src/tools/clippy/tests/ui/deref_by_slicing.stderr @@ -55,5 +55,11 @@ error: slicing when dereferencing would work LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice | ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)` -error: aborting due to 9 previous errors +error: slicing when dereferencing would work + --> tests/ui/deref_by_slicing.rs:31:13 + | +LL | let _ = &a[..]; + | ^^^^^^ help: reborrow the original value instead: `&*a` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/doc/footnote_issue_13183.rs b/src/tools/clippy/tests/ui/doc/footnote_issue_13183.rs new file mode 100644 index 00000000000..a18b4c3ac53 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/footnote_issue_13183.rs @@ -0,0 +1,10 @@ +// This is a regression test for <https://github.com/rust-lang/rust-clippy/issues/13183>. +// It should not warn on missing backticks on footnote references. + +#![warn(clippy::doc_markdown)] +// Should not warn! +//! Here's a footnote[^example_footnote_identifier] +//! +//! [^example_footnote_identifier]: This is merely an example. + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_drop.stderr b/src/tools/clippy/tests/ui/empty_drop.stderr index 4223ddaf3fb..d4d020fec30 100644 --- a/src/tools/clippy/tests/ui/empty_drop.stderr +++ b/src/tools/clippy/tests/ui/empty_drop.stderr @@ -4,10 +4,11 @@ error: empty drop implementation LL | / impl Drop for Foo { LL | | fn drop(&mut self) {} LL | | } - | |_^ help: try removing this impl + | |_^ | = note: `-D clippy::empty-drop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::empty_drop)]` + = help: try removing this impl error: empty drop implementation --> tests/ui/empty_drop.rs:23:1 @@ -17,7 +18,9 @@ LL | | fn drop(&mut self) { LL | | {} LL | | } LL | | } - | |_^ help: try removing this impl + | |_^ + | + = help: try removing this impl error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 7126f279945..ca422ee29c1 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -2,6 +2,7 @@ #![allow(unused)] #![allow( clippy::needless_borrow, + clippy::needless_option_as_deref, clippy::needless_pass_by_value, clippy::no_effect, clippy::option_map_unit_fn, @@ -482,3 +483,19 @@ mod issue_12853 { x(); } } + +mod issue_13073 { + fn get_default() -> Option<&'static str> { + Some("foo") + } + + pub fn foo() { + // shouldn't lint + let bind: Option<String> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + let bind: Option<&'static str> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + // should lint + let _field = bind.or_else(get_default).unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index 0787abf5f3e..c0db91c03ef 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -2,6 +2,7 @@ #![allow(unused)] #![allow( clippy::needless_borrow, + clippy::needless_option_as_deref, clippy::needless_pass_by_value, clippy::no_effect, clippy::option_map_unit_fn, @@ -482,3 +483,19 @@ mod issue_12853 { x(); } } + +mod issue_13073 { + fn get_default() -> Option<&'static str> { + Some("foo") + } + + pub fn foo() { + // shouldn't lint + let bind: Option<String> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + let bind: Option<&'static str> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + // should lint + let _field = bind.or_else(|| get_default()).unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index c757601042f..5540261fc57 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> tests/ui/eta.rs:29:27 + --> tests/ui/eta.rs:30:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> tests/ui/eta.rs:33:40 + --> tests/ui/eta.rs:34:40 | LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> tests/ui/eta.rs:34:35 + --> tests/ui/eta.rs:35:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> tests/ui/eta.rs:35:26 + --> tests/ui/eta.rs:36:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> tests/ui/eta.rs:42:27 + --> tests/ui/eta.rs:43:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> tests/ui/eta.rs:94:51 + --> tests/ui/eta.rs:95:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,166 +41,172 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> tests/ui/eta.rs:95:51 + --> tests/ui/eta.rs:96:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> tests/ui/eta.rs:97:42 + --> tests/ui/eta.rs:98:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> tests/ui/eta.rs:101:29 + --> tests/ui/eta.rs:102:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> tests/ui/eta.rs:102:27 + --> tests/ui/eta.rs:103:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> tests/ui/eta.rs:104:65 + --> tests/ui/eta.rs:105:65 | LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> tests/ui/eta.rs:167:22 + --> tests/ui/eta.rs:168:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> tests/ui/eta.rs:174:27 + --> tests/ui/eta.rs:175:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> tests/ui/eta.rs:179:27 + --> tests/ui/eta.rs:180:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> tests/ui/eta.rs:211:28 + --> tests/ui/eta.rs:212:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:212:28 + --> tests/ui/eta.rs:213:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:213:28 + --> tests/ui/eta.rs:214:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> tests/ui/eta.rs:220:21 + --> tests/ui/eta.rs:221:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> tests/ui/eta.rs:224:21 + --> tests/ui/eta.rs:225:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> tests/ui/eta.rs:317:18 + --> tests/ui/eta.rs:318:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:320:19 + --> tests/ui/eta.rs:321:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:324:26 + --> tests/ui/eta.rs:325:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> tests/ui/eta.rs:336:19 + --> tests/ui/eta.rs:337:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> tests/ui/eta.rs:339:19 + --> tests/ui/eta.rs:340:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> tests/ui/eta.rs:342:17 + --> tests/ui/eta.rs:343:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> tests/ui/eta.rs:346:17 + --> tests/ui/eta.rs:347:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn` error: redundant closure - --> tests/ui/eta.rs:406:19 + --> tests/ui/eta.rs:407:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` error: redundant closure - --> tests/ui/eta.rs:434:22 + --> tests/ui/eta.rs:435:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` error: redundant closure - --> tests/ui/eta.rs:438:22 + --> tests/ui/eta.rs:439:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` error: redundant closure - --> tests/ui/eta.rs:451:18 + --> tests/ui/eta.rs:452:18 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` error: redundant closure - --> tests/ui/eta.rs:458:30 + --> tests/ui/eta.rs:459:30 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` error: redundant closure - --> tests/ui/eta.rs:477:38 + --> tests/ui/eta.rs:478:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `&f` error: redundant closure - --> tests/ui/eta.rs:481:38 + --> tests/ui/eta.rs:482:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `f` -error: aborting due to 33 previous errors +error: redundant closure + --> tests/ui/eta.rs:499:35 + | +LL | let _field = bind.or_else(|| get_default()).unwrap(); + | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` + +error: aborting due to 34 previous errors diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr index 6d8e166a649..81e4fb6765d 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.stderr +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -2,100 +2,179 @@ error: float has excessive precision --> tests/ui/excessive_precision.rs:20:26 | LL | const BAD32_1: f32 = 0.123_456_789_f32; - | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32` + | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::excessive-precision` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]` +help: consider changing the type or truncating it to + | +LL | const BAD32_1: f32 = 0.123_456_79_f32; + | ~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:21:26 | LL | const BAD32_2: f32 = 0.123_456_789; - | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD32_2: f32 = 0.123_456_79; + | ~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:22:26 | LL | const BAD32_3: f32 = 0.100_000_000_000_1; - | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD32_3: f32 = 0.1; + | ~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:23:29 | LL | const BAD32_EDGE: f32 = 1.000_000_9; - | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001` + | ^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD32_EDGE: f32 = 1.000_001; + | ~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:27:26 | LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD64_3: f64 = 0.1; + | ~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:30:22 | LL | println!("{:?}", 8.888_888_888_888_888_888_888); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | println!("{:?}", 8.888_888_888_888_89); + | ~~~~~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:41:22 | LL | let bad32: f32 = 1.123_456_789; - | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad32: f32 = 1.123_456_8; + | ~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:42:26 | LL | let bad32_suf: f32 = 1.123_456_789_f32; - | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + | ^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad32_suf: f32 = 1.123_456_8_f32; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:43:21 | LL | let bad32_inf = 1.123_456_789_f32; - | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + | ^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad32_inf = 1.123_456_8_f32; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:53:36 | LL | let bad_vec32: Vec<f32> = vec![0.123_456_789]; - | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_vec32: Vec<f32> = vec![0.123_456_79]; + | ~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:54:36 | LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78]; + | ~~~~~~~~~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:58:24 | LL | let bad_e32: f32 = 1.123_456_788_888e-10; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_e32: f32 = 1.123_456_8e-10; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:61:27 | LL | let bad_bige32: f32 = 1.123_456_788_888E-10; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_bige32: f32 = 1.123_456_8E-10; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:70:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let _ = 2.225_073_858_507_201e-308_f64; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:73:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let _ = 0_f64; + | ~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:83:20 | LL | const _: f64 = 3.0000000000000000e+00; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `3.0` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const _: f64 = 3.0; + | ~~~ error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs index c25e79a3617..28b477b6921 100644 --- a/src/tools/clippy/tests/ui/explicit_counter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs @@ -278,3 +278,16 @@ mod issue_10058 { } } } + +mod issue_13123 { + pub fn test() { + let mut vec = vec![1, 2, 3, 4]; + let mut _index = 0; + 'label: for v in vec { + _index += 1; + if v == 1 { + break 'label; + } + } + } +} diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr index e28f8783f9c..1b2d1f8570a 100644 --- a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr @@ -57,5 +57,11 @@ LL | for _item in slice { | = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate` -error: aborting due to 9 previous errors +error: the variable `_index` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:286:9 + | +LL | 'label: for v in vec { + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `'label: for (_index, v) in vec.into_iter().enumerate()` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr index e5bb8d13269..a05b7138bc9 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr @@ -2,106 +2,190 @@ error: casting function pointer `foo` to `i8` --> tests/ui/fn_to_numeric_cast_any.rs:23:13 | LL | let _ = foo as i8; - | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8` + | ^^^^^^^^^ | = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_any)]` +help: did you mean to invoke the function? + | +LL | let _ = foo() as i8; + | ~~~~~~~~~~~ error: casting function pointer `foo` to `i16` --> tests/ui/fn_to_numeric_cast_any.rs:26:13 | LL | let _ = foo as i16; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i16; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `i32` --> tests/ui/fn_to_numeric_cast_any.rs:28:13 | LL | let _ = foo as i32; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i32; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `i64` --> tests/ui/fn_to_numeric_cast_any.rs:30:13 | LL | let _ = foo as i64; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i64; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `i128` --> tests/ui/fn_to_numeric_cast_any.rs:32:13 | LL | let _ = foo as i128; - | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128` + | ^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i128; + | ~~~~~~~~~~~~~ error: casting function pointer `foo` to `isize` --> tests/ui/fn_to_numeric_cast_any.rs:34:13 | LL | let _ = foo as isize; - | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize` + | ^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as isize; + | ~~~~~~~~~~~~~~ error: casting function pointer `foo` to `u8` --> tests/ui/fn_to_numeric_cast_any.rs:37:13 | LL | let _ = foo as u8; - | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8` + | ^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u8; + | ~~~~~~~~~~~ error: casting function pointer `foo` to `u16` --> tests/ui/fn_to_numeric_cast_any.rs:39:13 | LL | let _ = foo as u16; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u16; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `u32` --> tests/ui/fn_to_numeric_cast_any.rs:41:13 | LL | let _ = foo as u32; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u32; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `u64` --> tests/ui/fn_to_numeric_cast_any.rs:43:13 | LL | let _ = foo as u64; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u64; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `u128` --> tests/ui/fn_to_numeric_cast_any.rs:45:13 | LL | let _ = foo as u128; - | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128` + | ^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u128; + | ~~~~~~~~~~~~~ error: casting function pointer `foo` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:47:13 | LL | let _ = foo as usize; - | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize` + | ^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as usize; + | ~~~~~~~~~~~~~~ error: casting function pointer `Struct::static_method` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:52:13 | LL | let _ = Struct::static_method as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = Struct::static_method() as usize; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting function pointer `f` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:57:5 | LL | f as usize - | ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | f() as usize + | error: casting function pointer `T::static_method` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:62:5 | LL | T::static_method as usize - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | T::static_method() as usize + | error: casting function pointer `(clos as fn(u32) -> u32)` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:69:13 | LL | let _ = (clos as fn(u32) -> u32) as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = (clos as fn(u32) -> u32)() as usize; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting function pointer `foo` to `*const ()` --> tests/ui/fn_to_numeric_cast_any.rs:74:13 | LL | let _ = foo as *const (); - | ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()` + | ^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as *const (); + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/for_kv_map.fixed b/src/tools/clippy/tests/ui/for_kv_map.fixed index a2112d7b730..1733b29128f 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.fixed +++ b/src/tools/clippy/tests/ui/for_kv_map.fixed @@ -40,6 +40,16 @@ fn main() { let _k = k; } + let m: HashMap<u64, u64> = HashMap::new(); + let rm = &m; + 'label: for k in rm.keys() { + //~^ ERROR: you seem to want to iterate on a map's keys + let _k = k; + if *k == 0u64 { + break 'label; + } + } + // The following should not produce warnings. let m: HashMap<u64, u64> = HashMap::new(); diff --git a/src/tools/clippy/tests/ui/for_kv_map.rs b/src/tools/clippy/tests/ui/for_kv_map.rs index 1b7959b8f92..de465a7c8e6 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.rs +++ b/src/tools/clippy/tests/ui/for_kv_map.rs @@ -40,6 +40,16 @@ fn main() { let _k = k; } + let m: HashMap<u64, u64> = HashMap::new(); + let rm = &m; + 'label: for (k, _value) in rm { + //~^ ERROR: you seem to want to iterate on a map's keys + let _k = k; + if *k == 0u64 { + break 'label; + } + } + // The following should not produce warnings. let m: HashMap<u64, u64> = HashMap::new(); diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr index f4ce473d095..adcc3ab8fdb 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.stderr +++ b/src/tools/clippy/tests/ui/for_kv_map.stderr @@ -55,5 +55,16 @@ help: use the corresponding method LL | for k in rm.keys() { | ~ ~~~~~~~~~ -error: aborting due to 5 previous errors +error: you seem to want to iterate on a map's keys + --> tests/ui/for_kv_map.rs:45:32 + | +LL | 'label: for (k, _value) in rm { + | ^^ + | +help: use the corresponding method + | +LL | 'label: for k in rm.keys() { + | ~ ~~~~~~~~~ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr index a08b6657dcc..0f8b279da1e 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.stderr +++ b/src/tools/clippy/tests/ui/get_unwrap.stderr @@ -1,14 +1,18 @@ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:37:17 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> tests/ui/get_unwrap.rs:9:9 | LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ +help: using `[]` is clearer and more concise + | +LL | let _ = &boxed_slice[1]; + | ~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:37:17 @@ -21,11 +25,16 @@ LL | let _ = boxed_slice.get(1).unwrap(); = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:38:17 | LL | let _ = some_slice.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_slice[0]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:38:17 @@ -36,11 +45,16 @@ LL | let _ = some_slice.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:39:17 | LL | let _ = some_vec.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vec[0]; + | ~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:39:17 @@ -51,11 +65,16 @@ LL | let _ = some_vec.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a VecDeque --> tests/ui/get_unwrap.rs:40:17 | LL | let _ = some_vecdeque.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vecdeque[0]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:40:17 @@ -66,11 +85,16 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a HashMap --> tests/ui/get_unwrap.rs:41:17 | LL | let _ = some_hashmap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_hashmap[&1]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:41:17 @@ -81,11 +105,16 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a BTreeMap --> tests/ui/get_unwrap.rs:42:17 | LL | let _ = some_btreemap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_btreemap[&1]; + | ~~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:42:17 @@ -96,11 +125,16 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:46:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _: u8 = boxed_slice[1]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:46:22 @@ -111,11 +145,16 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui/get_unwrap.rs:51:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | boxed_slice[0] = 1; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:51:10 @@ -126,11 +165,16 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui/get_unwrap.rs:52:9 | LL | *some_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_slice[0] = 1; + | ~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:52:10 @@ -141,11 +185,16 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:53:9 | LL | *some_vec.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vec[0] = 1; + | ~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:53:10 @@ -156,11 +205,16 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a VecDeque --> tests/ui/get_unwrap.rs:54:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vecdeque[0] = 1; + | ~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:54:10 @@ -171,11 +225,16 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:66:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:66:17 @@ -186,11 +245,16 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:67:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:67:17 @@ -201,29 +265,49 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:77:24 | LL | let _x: &i32 = f.get(1 + 2).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _x: &i32 = &f[1 + 2]; + | ~~~~~~~~~ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:80:18 | LL | let _x = f.get(1 + 2).unwrap().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _x = f[1 + 2].to_string(); + | ~~~~~~~~ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:83:18 | LL | let _x = f.get(1 + 2).unwrap().abs(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _x = f[1 + 2].abs(); + | ~~~~~~~~ -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui/get_unwrap.rs:100:33 | LL | let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut rest[linidx(j, k) - linidx(i, k) - 1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let b = &mut rest[linidx(j, k) - linidx(i, k) - 1]; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/if_let_mutex.rs b/src/tools/clippy/tests/ui/if_let_mutex.rs index cb6915e0eba..bb0eadfca1c 100644 --- a/src/tools/clippy/tests/ui/if_let_mutex.rs +++ b/src/tools/clippy/tests/ui/if_let_mutex.rs @@ -1,4 +1,5 @@ #![warn(clippy::if_let_mutex)] +#![allow(clippy::redundant_pattern_matching)] use std::ops::Deref; use std::sync::Mutex; @@ -50,4 +51,12 @@ fn mutex_ref(mutex: &Mutex<i32>) { }; } +fn multiple_mutexes(m1: &Mutex<()>, m2: &Mutex<()>) { + if let Ok(_) = m1.lock() { + m2.lock(); + } else { + m1.lock(); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/if_let_mutex.stderr b/src/tools/clippy/tests/ui/if_let_mutex.stderr index 6e0115c23af..45df4ac4d67 100644 --- a/src/tools/clippy/tests/ui/if_let_mutex.stderr +++ b/src/tools/clippy/tests/ui/if_let_mutex.stderr @@ -1,5 +1,5 @@ error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock - --> tests/ui/if_let_mutex.rs:10:5 + --> tests/ui/if_let_mutex.rs:11:5 | LL | if let Err(locked) = m.lock() { | ^ - this Mutex will remain locked for the entire `if let`-block... @@ -19,7 +19,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::if_let_mutex)]` error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock - --> tests/ui/if_let_mutex.rs:23:5 + --> tests/ui/if_let_mutex.rs:24:5 | LL | if let Some(locked) = m.lock().unwrap().deref() { | ^ - this Mutex will remain locked for the entire `if let`-block... @@ -37,7 +37,7 @@ LL | | }; = help: move the lock call outside of the `if let ...` expression error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock - --> tests/ui/if_let_mutex.rs:45:5 + --> tests/ui/if_let_mutex.rs:46:5 | LL | if let Ok(i) = mutex.lock() { | ^ ----- this Mutex will remain locked for the entire `if let`-block... @@ -53,5 +53,21 @@ LL | | }; | = help: move the lock call outside of the `if let ...` expression -error: aborting due to 3 previous errors +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> tests/ui/if_let_mutex.rs:55:5 + | +LL | if let Ok(_) = m1.lock() { + | ^ -- this Mutex will remain locked for the entire `if let`-block... + | _____| + | | +LL | | m2.lock(); +LL | | } else { +LL | | m1.lock(); + | | -- ... and is tried to lock again here, which will always deadlock. +LL | | } + | |_____^ + | + = help: move the lock call outside of the `if let ...` expression + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed new file mode 100644 index 00000000000..ad13372a68b --- /dev/null +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed @@ -0,0 +1,119 @@ +#![warn(clippy::if_then_some_else_none)] +#![allow(clippy::redundant_pattern_matching, clippy::unnecessary_lazy_evaluations)] + +fn main() { + // Should issue an error. + let _ = foo().then(|| { println!("true!"); "foo" }); + + // Should issue an error when macros are used. + let _ = matches!(true, true).then(|| { println!("true!"); matches!(true, false) }); + + // Should issue an error. Binary expression `o < 32` should be parenthesized. + let x = Some(5); + let _ = x.and_then(|o| (o < 32).then_some(o)); + //~^ ERROR: this could be simplified with `bool::then_some` + + // Should issue an error. Unary expression `!x` should be parenthesized. + let x = true; + let _ = (!x).then_some(0); + //~^ ERROR: this could be simplified with `bool::then_some` + + // Should not issue an error since the `else` block has a statement besides `None`. + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + eprintln!("false..."); + None + }; + + // Should not issue an error since there are more than 2 blocks in the if-else chain. + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else if bar() { + println!("bar true!"); + Some("bar") + } else { + None + }; + + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else { + bar().then(|| { + println!("bar true!"); + "bar" + }) + }; + + // Should not issue an error since the `then` block has `None`, not `Some`. + let _ = if foo() { None } else { Some("foo is false") }; + + // Should not issue an error since the `else` block doesn't use `None` directly. + let _ = if foo() { Some("foo is true") } else { into_none() }; + + // Should not issue an error since the `then` block doesn't use `Some` directly. + let _ = if foo() { into_some("foo") } else { None }; +} + +#[clippy::msrv = "1.49"] +fn _msrv_1_49() { + // `bool::then` was stabilized in 1.50. Do not lint this + let _ = if foo() { + println!("true!"); + Some(149) + } else { + None + }; +} + +#[clippy::msrv = "1.50"] +fn _msrv_1_50() { + let _ = foo().then(|| { println!("true!"); 150 }); +} + +fn foo() -> bool { + unimplemented!() +} + +fn bar() -> bool { + unimplemented!() +} + +fn into_some<T>(v: T) -> Option<T> { + Some(v) +} + +fn into_none<T>() -> Option<T> { + None +} + +// Should not warn +fn f(b: bool, v: Option<()>) -> Option<()> { + if b { + v?; // This is a potential early return, is not equivalent with `bool::then` + + Some(()) + } else { + None + } +} + +fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { + let x = if b { + #[allow(clippy::let_unit_value)] + let _ = v?; + Some(()) + } else { + None + }; + + Ok(()) +} + +const fn issue12103(x: u32) -> Option<u32> { + // Should not issue an error in `const` context + if x > 42 { Some(150) } else { None } +} diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs index ccde154bd56..73edbb7da2a 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs @@ -1,5 +1,5 @@ #![warn(clippy::if_then_some_else_none)] -#![allow(clippy::redundant_pattern_matching)] +#![allow(clippy::redundant_pattern_matching, clippy::unnecessary_lazy_evaluations)] fn main() { // Should issue an error. diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr index e0a95aebdc1..aed01e026cb 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr @@ -9,9 +9,8 @@ LL | | Some("foo") LL | | } else { LL | | None LL | | }; - | |_____^ + | |_____^ help: try: `foo().then(|| { println!("true!"); "foo" })` | - = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })` = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::if_then_some_else_none)]` @@ -26,25 +25,19 @@ LL | | Some(matches!(true, false)) LL | | } else { LL | | None LL | | }; - | |_____^ - | - = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` + | |_____^ help: try: `matches!(true, true).then(|| { println!("true!"); matches!(true, false) })` error: this could be simplified with `bool::then_some` --> tests/ui/if_then_some_else_none.rs:25:28 | LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `bool::then_some` like: `(o < 32).then_some(o)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(o < 32).then_some(o)` error: this could be simplified with `bool::then_some` --> tests/ui/if_then_some_else_none.rs:30:13 | LL | let _ = if !x { Some(0) } else { None }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `bool::then_some` like: `(!x).then_some(0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(!x).then_some(0)` error: this could be simplified with `bool::then` --> tests/ui/if_then_some_else_none.rs:86:13 @@ -57,9 +50,7 @@ LL | | Some(150) LL | | } else { LL | | None LL | | }; - | |_____^ - | - = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })` + | |_____^ help: try: `foo().then(|| { println!("true!"); 150 })` error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/implicit_hasher.fixed b/src/tools/clippy/tests/ui/implicit_hasher.fixed new file mode 100644 index 00000000000..2d6dc0274cf --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_hasher.fixed @@ -0,0 +1,102 @@ +//@aux-build:proc_macros.rs +#![deny(clippy::implicit_hasher)] + +#[macro_use] +extern crate proc_macros; + +use std::cmp::Eq; +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasher, Hash}; + +pub trait Foo<T>: Sized { + fn make() -> (Self, Self); +} + +impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + // OK, don't suggest to modify these + let _: HashMap<i32, i32> = HashMap::new(); + let _: HashSet<i32> = HashSet::new(); + + (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + } +} +impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) { + fn make() -> (Self, Self) { + ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),)) + } +} +impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + } +} + +impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} +impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} + +impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + } +} +impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + } +} + +impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} +impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} + +pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {} + +pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {} + +#[inline_macros] +pub mod gen { + use super::*; + inline! { + impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + } + } + + pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + } +} + +// When the macro is in a different file, the suggestion spans can't be combined properly +// and should not cause an ICE +// See #2707 +#[macro_use] +#[path = "auxiliary/test_macro.rs"] +pub mod test_macro; +__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A); + +// #4260 +external! { + pub fn f(input: &HashMap<u32, u32>) {} +} + +// #7712 +pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs index f7cd541741b..0a334357bd1 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.rs +++ b/src/tools/clippy/tests/ui/implicit_hasher.rs @@ -1,11 +1,8 @@ //@aux-build:proc_macros.rs -//@no-rustfix #![deny(clippy::implicit_hasher)] -#![allow(unused)] #[macro_use] extern crate proc_macros; -use proc_macros::external; use std::cmp::Eq; use std::collections::{HashMap, HashSet}; @@ -68,9 +65,11 @@ impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> { } } -pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} +pub fn map(map: &mut HashMap<i32, i32>) {} -#[proc_macros::inline_macros] +pub fn set(set: &mut HashSet<i32>) {} + +#[inline_macros] pub mod gen { use super::*; inline! { diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr index a3df8edf389..48c6ebc209c 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.stderr +++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr @@ -1,40 +1,121 @@ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:14:1 +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:15:35 + | +LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> { + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/implicit_hasher.rs:2:9 + | +LL | #![deny(clippy::implicit_hasher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add a type parameter for `BuildHasher` + | +LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> { +LL | fn make() -> (Self, Self) { +... +LL | +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | + +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:24:36 + | +LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) { + | ^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) { +LL | fn make() -> (Self, Self) { +LL ~ ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),)) + | + +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:29:19 + | +LL | impl Foo<i16> for HashMap<String, String> { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | + +error: impl for `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:46:32 + | +LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> { + | ^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) | -LL | pub trait Foo<T>: Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:71:1 +error: impl for `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:51:19 + | +LL | impl Foo<i16> for HashSet<String> { + | ^^^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) | -LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:74:1 +error: parameter of type `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:68:22 + | +LL | pub fn map(map: &mut HashMap<i32, i32>) {} + | ^^^^^^^^^^^^^^^^^ | -LL | pub mod gen { - | ^^^^^^^^^^^ +help: add a type parameter for `BuildHasher` + | +LL | pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:92:1 +error: parameter of type `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:70:22 + | +LL | pub fn set(set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^ | -LL | pub mod test_macro; - | ^^^^^^^^^^^^^^^^^^^ +help: add a type parameter for `BuildHasher` + | +LL | pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:96:1 +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:76:43 + | +LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> { + | ^^^^^^^^^^^^^ | -LL | external! { - | ^^^^^^^^^ + = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add a type parameter for `BuildHasher` + | +LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) | - = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:101:1 +error: parameter of type `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:100:35 | LL | pub async fn election_vote(_data: HashMap<i32, i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr index f06d4e983c5..3b06f26f5a0 100644 --- a/src/tools/clippy/tests/ui/implicit_return.stderr +++ b/src/tools/clippy/tests/ui/implicit_return.stderr @@ -2,88 +2,157 @@ error: missing `return` statement --> tests/ui/implicit_return.rs:15:5 | LL | true - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ | = note: `-D clippy::implicit-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::implicit_return)]` +help: add `return` as shown + | +LL | return true + | error: missing `return` statement --> tests/ui/implicit_return.rs:19:15 | LL | if true { true } else { false } - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | if true { return true } else { false } + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:19:29 | LL | if true { true } else { false } - | ^^^^^ help: add `return` as shown: `return false` + | ^^^^^ + | +help: add `return` as shown + | +LL | if true { true } else { return false } + | ~~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:25:17 | LL | true => false, - | ^^^^^ help: add `return` as shown: `return false` + | ^^^^^ + | +help: add `return` as shown + | +LL | true => return false, + | ~~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:26:20 | LL | false => { true }, - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | false => { return true }, + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:39:9 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:46:13 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:54:13 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:72:18 | LL | let _ = || { true }; - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | let _ = || { return true }; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:73:16 | LL | let _ = || true; - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | let _ = || return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:81:5 | LL | format!("test {}", "test") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `return` as shown + | +LL | return format!("test {}", "test") + | error: missing `return` statement --> tests/ui/implicit_return.rs:90:5 | LL | m!(true, false) - | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` + | ^^^^^^^^^^^^^^^ + | +help: add `return` as shown + | +LL | return m!(true, false) + | error: missing `return` statement --> tests/ui/implicit_return.rs:96:13 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:101:17 | LL | break 'outer false; - | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` + | ^^^^^^^^^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return false; + | ~~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:116:5 @@ -104,7 +173,12 @@ error: missing `return` statement --> tests/ui/implicit_return.rs:130:5 | LL | true - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | return true + | error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr index b5a07418734..3026854e317 100644 --- a/src/tools/clippy/tests/ui/lossy_float_literal.stderr +++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr @@ -2,70 +2,124 @@ error: literal cannot be represented as the underlying type without loss of prec --> tests/ui/lossy_float_literal.rs:14:18 | LL | let _: f32 = 16_777_217.0; - | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0` + | ^^^^^^^^^^^^ | = note: `-D clippy::lossy-float-literal` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::lossy_float_literal)]` +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_216.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:15:18 | LL | let _: f32 = 16_777_219.0; - | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:16:18 | LL | let _: f32 = 16_777_219.; - | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:17:18 | LL | let _: f32 = 16_777_219.000; - | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:18:13 | LL | let _ = 16_777_219f32; - | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _ = 16_777_220_f32; + | ~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:19:19 | LL | let _: f32 = -16_777_219.0; - | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = -16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:21:18 | LL | let _: f64 = 9_007_199_254_740_993.0; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = 9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:22:18 | LL | let _: f64 = 9_007_199_254_740_993.; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = 9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:23:18 | LL | let _: f64 = 9_007_199_254_740_993.00; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = 9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:24:13 | LL | let _ = 9_007_199_254_740_993f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _ = 9_007_199_254_740_992_f64; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:25:19 | LL | let _: f64 = -9_007_199_254_740_993.0; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = -9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/missing_trait_methods.rs b/src/tools/clippy/tests/ui/missing_trait_methods.rs index 1b09b717ff7..55b904bde59 100644 --- a/src/tools/clippy/tests/ui/missing_trait_methods.rs +++ b/src/tools/clippy/tests/ui/missing_trait_methods.rs @@ -49,4 +49,12 @@ impl B for Complete { } } +trait MissingMultiple { + fn one() {} + fn two() {} + fn three() {} +} + +impl MissingMultiple for Partial {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_trait_methods.stderr b/src/tools/clippy/tests/ui/missing_trait_methods.stderr index f5d5d4418b2..9c4968b022d 100644 --- a/src/tools/clippy/tests/ui/missing_trait_methods.stderr +++ b/src/tools/clippy/tests/ui/missing_trait_methods.stderr @@ -24,5 +24,41 @@ help: implement the method LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: missing trait method provided by default: `one` + --> tests/ui/missing_trait_methods.rs:58:1 + | +LL | impl MissingMultiple for Partial {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> tests/ui/missing_trait_methods.rs:53:5 + | +LL | fn one() {} + | ^^^^^^^^ + +error: missing trait method provided by default: `two` + --> tests/ui/missing_trait_methods.rs:58:1 + | +LL | impl MissingMultiple for Partial {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> tests/ui/missing_trait_methods.rs:54:5 + | +LL | fn two() {} + | ^^^^^^^^ + +error: missing trait method provided by default: `three` + --> tests/ui/missing_trait_methods.rs:58:1 + | +LL | impl MissingMultiple for Partial {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> tests/ui/missing_trait_methods.rs:55:5 + | +LL | fn three() {} + | ^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed index 5478372cbe0..c1dc8b5e8d0 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed @@ -333,4 +333,12 @@ fn main() { f(&y); // Don't lint f("".to_owned()); // Lint } + { + fn takes_writer<T: std::io::Write>(_: T) {} + + fn issue_12856(mut buffer: &mut Vec<u8>) { + takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later + buffer.extend(b"\n"); + } + } } diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs index 2643815d939..c7f66824d58 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs @@ -333,4 +333,12 @@ fn main() { f(&y); // Don't lint f(&"".to_owned()); // Lint } + { + fn takes_writer<T: std::io::Write>(_: T) {} + + fn issue_12856(mut buffer: &mut Vec<u8>) { + takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later + buffer.extend(b"\n"); + } + } } diff --git a/src/tools/clippy/tests/ui/needless_pub_self.stderr b/src/tools/clippy/tests/ui/needless_pub_self.stderr index 0bff2e4b8b7..1fdd8416565 100644 --- a/src/tools/clippy/tests/ui/needless_pub_self.stderr +++ b/src/tools/clippy/tests/ui/needless_pub_self.stderr @@ -2,22 +2,27 @@ error: unnecessary `pub(self)` --> tests/ui/needless_pub_self.rs:13:1 | LL | pub(self) fn a() {} - | ^^^^^^^^^ help: remove it + | ^^^^^^^^^ | = note: `-D clippy::needless-pub-self` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pub_self)]` + = help: remove it error: unnecessary `pub(in self)` --> tests/ui/needless_pub_self.rs:14:1 | LL | pub(in self) fn b() {} - | ^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^ + | + = help: remove it error: unnecessary `pub(self)` --> tests/ui/needless_pub_self.rs:20:5 | LL | pub(self) fn f() {} - | ^^^^^^^^^ help: remove it + | ^^^^^^^^^ + | + = help: remove it error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index d117e8bf9c7..52b0155a762 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -177,3 +177,9 @@ fn issue_12371(x: usize) -> bool { // Should not warn! !x != 0 } + +// Not linted because it is slow to do so +// https://github.com/rust-lang/rust-clippy/issues/13206 +fn many_ops(a: bool, b: bool, c: bool, d: bool, e: bool, f: bool) -> bool { + (a && c && f) || (!a && b && !d) || (!b && !c && !e) || (d && e && !f) +} diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed index 332cba97155..feaec33ac15 100644 --- a/src/tools/clippy/tests/ui/patterns.fixed +++ b/src/tools/clippy/tests/ui/patterns.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::all)] #![allow(unused)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs index 45d907688e3..53812c7deec 100644 --- a/src/tools/clippy/tests/ui/patterns.rs +++ b/src/tools/clippy/tests/ui/patterns.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::all)] #![allow(unused)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index d70c9f8d06c..b810fd8224f 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -13,7 +13,10 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] +#![allow(clippy::manual_find_map)] +#![allow(clippy::manual_filter_map)] #![allow(clippy::useless_conversion)] +#![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] @@ -47,6 +50,7 @@ #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] #![allow(let_underscore_drop)] +#![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] @@ -55,65 +59,72 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] -#![warn(clippy::almost_complete_range)] -#![warn(clippy::disallowed_names)] -#![warn(clippy::blocks_in_conditions)] -#![warn(clippy::blocks_in_conditions)] -#![warn(clippy::blocks_in_conditions)] -#![warn(clippy::box_collection)] -#![warn(clippy::redundant_static_lifetimes)] -#![warn(clippy::cognitive_complexity)] -#![warn(clippy::derived_hash_with_manual_eq)] -#![warn(clippy::disallowed_methods)] -#![warn(clippy::disallowed_types)] -#![warn(clippy::mixed_read_write_in_expression)] -#![warn(clippy::useless_conversion)] -#![warn(clippy::match_result_ok)] -#![warn(clippy::non_canonical_clone_impl)] -#![warn(clippy::non_canonical_partial_ord_impl)] -#![warn(clippy::arithmetic_side_effects)] -#![warn(clippy::overly_complex_bool_expr)] -#![warn(clippy::new_without_default)] -#![warn(clippy::bind_instead_of_map)] -#![warn(clippy::expect_used)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::panicking_overflow_checks)] -#![warn(clippy::needless_borrow)] -#![warn(clippy::expect_used)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::single_char_add_str)] -#![warn(clippy::module_name_repetitions)] -#![warn(clippy::missing_const_for_thread_local)] -#![warn(clippy::recursive_format_impl)] -#![warn(clippy::unwrap_or_default)] -#![warn(clippy::invisible_characters)] -#![warn(invalid_reference_casting)] -#![warn(suspicious_double_ref_op)] -#![warn(invalid_nan_comparisons)] -#![warn(drop_bounds)] -#![warn(dropping_copy_types)] -#![warn(dropping_references)] -#![warn(useless_ptr_null_checks)] -#![warn(for_loops_over_fallibles)] -#![warn(for_loops_over_fallibles)] -#![warn(for_loops_over_fallibles)] -#![warn(forgetting_copy_types)] -#![warn(forgetting_references)] -#![warn(array_into_iter)] -#![warn(invalid_atomic_ordering)] -#![warn(invalid_value)] -#![warn(invalid_from_utf8_unchecked)] -#![warn(let_underscore_drop)] -#![warn(enum_intrinsics_non_enums)] -#![warn(non_fmt_panics)] -#![warn(named_arguments_used_positionally)] -#![warn(temporary_cstring_as_ptr)] -#![warn(undropped_manually_drops)] -#![warn(unknown_lints)] -#![warn(unused_labels)] -#![warn(ambiguous_wide_pointer_comparisons)] +#![allow(clippy::reversed_empty_ranges)] +#![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` +#![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` +#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` +#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` +#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` +#![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime` +#![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` +#![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` +#![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` +#![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` +#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` +#![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` +#![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` +#![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` +#![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` +#![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` +#![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` +#![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` +#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or` +#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` +#![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` +#![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref` +#![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` +#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` +#![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` +#![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` +#![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display` +#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` +#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` +#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` +#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` +#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` +#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` +#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` +#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` +#![warn(temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` +#![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` +#![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` +#![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 8d0ac3c8f95..e03df1658ee 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -13,7 +13,10 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] +#![allow(clippy::manual_find_map)] +#![allow(clippy::manual_filter_map)] #![allow(clippy::useless_conversion)] +#![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] @@ -47,6 +50,7 @@ #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] #![allow(let_underscore_drop)] +#![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] @@ -55,65 +59,72 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] -#![warn(clippy::almost_complete_letter_range)] -#![warn(clippy::blacklisted_name)] -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::box_vec)] -#![warn(clippy::const_static_lifetime)] -#![warn(clippy::cyclomatic_complexity)] -#![warn(clippy::derive_hash_xor_eq)] -#![warn(clippy::disallowed_method)] -#![warn(clippy::disallowed_type)] -#![warn(clippy::eval_order_dependence)] -#![warn(clippy::identity_conversion)] -#![warn(clippy::if_let_some_result)] -#![warn(clippy::incorrect_clone_impl_on_copy_type)] -#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] -#![warn(clippy::integer_arithmetic)] -#![warn(clippy::logic_bug)] -#![warn(clippy::new_without_default_derive)] -#![warn(clippy::option_and_then_some)] -#![warn(clippy::option_expect_used)] -#![warn(clippy::option_map_unwrap_or)] -#![warn(clippy::option_map_unwrap_or_else)] -#![warn(clippy::option_unwrap_used)] -#![warn(clippy::overflow_check_conditional)] -#![warn(clippy::ref_in_deref)] -#![warn(clippy::result_expect_used)] -#![warn(clippy::result_map_unwrap_or_else)] -#![warn(clippy::result_unwrap_used)] -#![warn(clippy::single_char_push_str)] -#![warn(clippy::stutter)] -#![warn(clippy::thread_local_initializer_can_be_made_const)] -#![warn(clippy::to_string_in_display)] -#![warn(clippy::unwrap_or_else_default)] -#![warn(clippy::zero_width_space)] -#![warn(clippy::cast_ref_to_mut)] -#![warn(clippy::clone_double_ref)] -#![warn(clippy::cmp_nan)] -#![warn(clippy::drop_bounds)] -#![warn(clippy::drop_copy)] -#![warn(clippy::drop_ref)] -#![warn(clippy::fn_null_check)] -#![warn(clippy::for_loop_over_option)] -#![warn(clippy::for_loop_over_result)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::forget_copy)] -#![warn(clippy::forget_ref)] -#![warn(clippy::into_iter_on_array)] -#![warn(clippy::invalid_atomic_ordering)] -#![warn(clippy::invalid_ref)] -#![warn(clippy::invalid_utf8_in_unchecked)] -#![warn(clippy::let_underscore_drop)] -#![warn(clippy::mem_discriminant_non_enum)] -#![warn(clippy::panic_params)] -#![warn(clippy::positional_named_format_parameters)] -#![warn(clippy::temporary_cstring_as_ptr)] -#![warn(clippy::undropped_manually_drops)] -#![warn(clippy::unknown_clippy_lints)] -#![warn(clippy::unused_label)] -#![warn(clippy::vtable_address_comparisons)] +#![allow(clippy::reversed_empty_ranges)] +#![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` +#![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` +#![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` +#![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` +#![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` +#![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime` +#![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` +#![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` +#![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` +#![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` +#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` +#![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` +#![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` +#![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` +#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` +#![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` +#![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` +#![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` +#![warn(clippy::option_map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or` +#![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` +#![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` +#![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref` +#![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` +#![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` +#![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` +#![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` +#![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display` +#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` +#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` +#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` +#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` +#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` +#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` +#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` +#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` +#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` +#![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` +#![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index d6637324a03..46d9f0fac59 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:58:9 + --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,358 +8,394 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:59:9 + --> tests/ui/rename.rs:64:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:60:9 + --> tests/ui/rename.rs:65:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:61:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:62:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:63:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:64:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` + --> tests/ui/rename.rs:75:9 + | +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` + +error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` + --> tests/ui/rename.rs:76:9 + | +LL | #![warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` + error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` +error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` + --> tests/ui/rename.rs:78:9 + | +LL | #![warn(clippy::if_let_redundant_pattern_matching)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` + error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` +error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` + --> tests/ui/rename.rs:118:9 + | +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` + error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` + --> tests/ui/rename.rs:120:9 + | +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` + error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 60 previous errors +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` + --> tests/ui/rename.rs:128:9 + | +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` + +error: aborting due to 66 previous errors diff --git a/src/tools/clippy/tests/ui/set_contains_or_insert.rs b/src/tools/clippy/tests/ui/set_contains_or_insert.rs index 8465007402a..d3a3e1c878b 100644 --- a/src/tools/clippy/tests/ui/set_contains_or_insert.rs +++ b/src/tools/clippy/tests/ui/set_contains_or_insert.rs @@ -3,17 +3,78 @@ #![allow(clippy::needless_borrow)] #![warn(clippy::set_contains_or_insert)] -use std::collections::HashSet; +use std::collections::{BTreeSet, HashSet}; -fn main() { - should_warn_cases(); +fn should_warn_hashset() { + let mut set = HashSet::new(); + let value = 5; + + if !set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if !set.contains(&value) { + set.insert(value); + } - should_not_warn_cases(); + if !!set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if (&set).contains(&value) { + set.insert(value); + } + + let borrow_value = &6; + if !set.contains(borrow_value) { + set.insert(*borrow_value); + } + + let borrow_set = &mut set; + if !borrow_set.contains(&value) { + borrow_set.insert(value); + } } -fn should_warn_cases() { +fn should_not_warn_hashset() { let mut set = HashSet::new(); let value = 5; + let another_value = 6; + + if !set.contains(&value) { + set.insert(another_value); + } + + if !set.contains(&value) { + println!("Just a comment"); + } + + if simply_true() { + set.insert(value); + } + + if !set.contains(&value) { + set.replace(value); //it is not insert + println!("Just a comment"); + } + + if set.contains(&value) { + println!("value is already in set"); + } else { + set.insert(value); + } +} + +fn should_warn_btreeset() { + let mut set = BTreeSet::new(); + let value = 5; if !set.contains(&value) { set.insert(value); @@ -49,8 +110,8 @@ fn should_warn_cases() { } } -fn should_not_warn_cases() { - let mut set = HashSet::new(); +fn should_not_warn_btreeset() { + let mut set = BTreeSet::new(); let value = 5; let another_value = 6; @@ -81,3 +142,11 @@ fn should_not_warn_cases() { fn simply_true() -> bool { true } + +// This is placed last in order to be able to add new tests without changing line numbers +fn main() { + should_warn_hashset(); + should_warn_btreeset(); + should_not_warn_hashset(); + should_not_warn_btreeset(); +} diff --git a/src/tools/clippy/tests/ui/set_contains_or_insert.stderr b/src/tools/clippy/tests/ui/set_contains_or_insert.stderr index 507e20964fc..14ad6300544 100644 --- a/src/tools/clippy/tests/ui/set_contains_or_insert.stderr +++ b/src/tools/clippy/tests/ui/set_contains_or_insert.stderr @@ -1,5 +1,5 @@ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:18:13 + --> tests/ui/set_contains_or_insert.rs:12:13 | LL | if !set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | set.insert(value); = help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]` error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:23:12 + --> tests/ui/set_contains_or_insert.rs:17:12 | LL | if set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:28:13 + --> tests/ui/set_contains_or_insert.rs:22:13 | LL | if !set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:32:14 + --> tests/ui/set_contains_or_insert.rs:26:14 | LL | if !!set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:37:15 + --> tests/ui/set_contains_or_insert.rs:31:15 | LL | if (&set).contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:42:13 + --> tests/ui/set_contains_or_insert.rs:36:13 | LL | if !set.contains(borrow_value) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -50,12 +50,68 @@ LL | set.insert(*borrow_value); | ^^^^^^^^^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:47:20 + --> tests/ui/set_contains_or_insert.rs:41:20 | LL | if !borrow_set.contains(&value) { | ^^^^^^^^^^^^^^^^ LL | borrow_set.insert(value); | ^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:79:13 + | +LL | if !set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:84:12 + | +LL | if set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:89:13 + | +LL | if !set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:93:14 + | +LL | if !!set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:98:15 + | +LL | if (&set).contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:103:13 + | +LL | if !set.contains(borrow_value) { + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | set.insert(*borrow_value); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:108:20 + | +LL | if !borrow_set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | borrow_set.insert(value); + | ^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index acd70416d8b..5249c440889 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -253,3 +253,46 @@ mod issue8634 { } } } + +fn issue11365() { + enum Foo { + A, + B, + C, + } + use Foo::{A, B, C}; + + match Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match Some(A) { + Some(A | B) => println!(), + Some { 0: C } | None => {}, + } + + match [A, A] { + [A, _] => println!(), + [_, A | B | C] => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(B | C)) => {}, + } + + if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() } + + match &Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + &Some(A | B | C) => println!(), + None => {}, + } + + if let Some(A | B) = &Some(A) { println!() } +} diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index bde78199810..882098a56e7 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -311,3 +311,52 @@ mod issue8634 { } } } + +fn issue11365() { + enum Foo { + A, + B, + C, + } + use Foo::{A, B, C}; + + match Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match Some(A) { + Some(A | B) => println!(), + Some { 0: C } | None => {}, + } + + match [A, A] { + [A, _] => println!(), + [_, A | B | C] => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(B | C)) => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(_)) => {}, + } + + match &Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + &Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + Some(A | B) => println!(), + None | Some(_) => {}, + } +} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index a249c120ee4..ceb2a193bf7 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -198,5 +198,23 @@ LL + } LL + } | -error: aborting due to 18 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:343:5 + | +LL | / match Ok::<_, u32>(Some(A)) { +LL | | Ok(Some(A)) => println!(), +LL | | Err(_) | Ok(None | Some(_)) => {}, +LL | | } + | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:358:5 + | +LL | / match &Some(A) { +LL | | Some(A | B) => println!(), +LL | | None | Some(_) => {}, +LL | | } + | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 6ede7bfcd9f..227b98c683e 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -75,8 +75,17 @@ mod std_in_proc_macro_derive { struct B {} } -fn main() { - std_instead_of_core(); - std_instead_of_alloc(); - alloc_instead_of_core(); +// Some intrinsics are usable on stable but live in an unstable module, but should still suggest +// replacing std -> core +fn intrinsic(a: *mut u8, b: *mut u8) { + unsafe { + core::intrinsics::copy(a, b, 1); + //~^ std_instead_of_core + } } + +#[clippy::msrv = "1.76"] +fn msrv_1_76(_: std::net::IpAddr) {} + +#[clippy::msrv = "1.77"] +fn msrv_1_77(_: core::net::IpAddr) {} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index e22b4f61f3e..01bb78dd3bf 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -75,8 +75,17 @@ mod std_in_proc_macro_derive { struct B {} } -fn main() { - std_instead_of_core(); - std_instead_of_alloc(); - alloc_instead_of_core(); +// Some intrinsics are usable on stable but live in an unstable module, but should still suggest +// replacing std -> core +fn intrinsic(a: *mut u8, b: *mut u8) { + unsafe { + std::intrinsics::copy(a, b, 1); + //~^ std_instead_of_core + } } + +#[clippy::msrv = "1.76"] +fn msrv_1_76(_: std::net::IpAddr) {} + +#[clippy::msrv = "1.77"] +fn msrv_1_77(_: std::net::IpAddr) {} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index 22cb9db7050..45d60d235ce 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -85,5 +85,17 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 13 previous errors +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:82:9 + | +LL | std::intrinsics::copy(a, b, 1); + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:91:17 + | +LL | fn msrv_1_77(_: std::net::IpAddr) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr b/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr index 4faf0237c17..43b03676b1d 100644 --- a/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr +++ b/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr @@ -2,51 +2,84 @@ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:19:13 | LL | let _ = 2 ^ 5; - | ^^^^^ help: did you mean to write: `2.pow(5)` + | ^^^^^ | = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suspicious_xor_used_as_pow)]` +help: did you mean to write + | +LL | let _ = 2.pow(5); + | ~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:22:13 | LL | let _ = 2i32 ^ 9i32; - | ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(9i32)` + | ^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 2i32.pow(9i32); + | ~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:24:13 | LL | let _ = 2i32 ^ 2i32; - | ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(2i32)` + | ^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 2i32.pow(2i32); + | ~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:26:13 | LL | let _ = 50i32 ^ 3i32; - | ^^^^^^^^^^^^ help: did you mean to write: `50i32.pow(3i32)` + | ^^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 50i32.pow(3i32); + | ~~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:28:13 | LL | let _ = 5i32 ^ 8i32; - | ^^^^^^^^^^^ help: did you mean to write: `5i32.pow(8i32)` + | ^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 5i32.pow(8i32); + | ~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:30:13 | LL | let _ = 2i32 ^ 32i32; - | ^^^^^^^^^^^^ help: did you mean to write: `2i32.pow(32i32)` + | ^^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 2i32.pow(32i32); + | ~~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:13:9 | LL | 1 ^ 2 // should warn even if inside macro - | ^^^^^ help: did you mean to write: `1.pow(2)` + | ^^^^^ ... LL | macro_test_inside!(); | -------------------- in this macro invocation | = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info) +help: did you mean to write + | +LL | 1.pow(2) // should warn even if inside macro + | ~~~~~~~~ error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed index aae4f8ac47f..f149bfb7774 100644 --- a/src/tools/clippy/tests/ui/try_err.fixed +++ b/src/tools/clippy/tests/ui/try_err.fixed @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs - +#![feature(try_blocks)] #![deny(clippy::try_err)] #![allow( clippy::unnecessary_wraps, @@ -152,3 +152,11 @@ pub fn try_return(x: bool) -> Result<i32, i32> { } Ok(0) } + +// Test that the lint is suppressed in try block. +pub fn try_block() -> Result<(), i32> { + let _: Result<_, i32> = try { + Err(1)?; + }; + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs index 927eccf2d54..841ec6b5d5c 100644 --- a/src/tools/clippy/tests/ui/try_err.rs +++ b/src/tools/clippy/tests/ui/try_err.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs - +#![feature(try_blocks)] #![deny(clippy::try_err)] #![allow( clippy::unnecessary_wraps, @@ -152,3 +152,11 @@ pub fn try_return(x: bool) -> Result<i32, i32> { } Ok(0) } + +// Test that the lint is suppressed in try block. +pub fn try_block() -> Result<(), i32> { + let _: Result<_, i32> = try { + Err(1)?; + }; + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index c069b9adf2d..0cc77a8775d 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -1,5 +1,6 @@ #![warn(clippy::uninit_vec)] +use std::cell::UnsafeCell; use std::mem::MaybeUninit; #[derive(Default)] @@ -12,6 +13,12 @@ union MyOwnMaybeUninit { uninit: (), } +// https://github.com/rust-lang/rust/issues/119620 +unsafe fn requires_paramenv<S>() { + let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1); + vec.set_len(1); +} + fn main() { // with_capacity() -> set_len() should be detected let mut vec: Vec<u8> = Vec::with_capacity(1000); diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 8e93466c2cc..e8b77d653f0 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -1,5 +1,17 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:17:5 + --> tests/ui/uninit_vec.rs:18:5 + | +LL | let mut vec = Vec::<UnsafeCell<*mut S>>::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + = note: `-D clippy::uninit-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:24:5 | LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,11 +20,9 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ | = help: initialize the buffer or wrap the content in `MaybeUninit` - = note: `-D clippy::uninit-vec` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:24:5 + --> tests/ui/uninit_vec.rs:31:5 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -23,7 +33,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:31:5 + --> tests/ui/uninit_vec.rs:38:5 | LL | let mut vec: Vec<u8> = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +42,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:38:5 + --> tests/ui/uninit_vec.rs:45:5 | LL | let mut vec: Vec<u8> = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +51,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:44:5 + --> tests/ui/uninit_vec.rs:51:5 | LL | let mut vec: Vec<u8> = Vec::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +60,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:61:5 + --> tests/ui/uninit_vec.rs:68:5 | LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +71,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:71:5 + --> tests/ui/uninit_vec.rs:78:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +82,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:77:5 + --> tests/ui/uninit_vec.rs:84:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +93,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:52:9 + --> tests/ui/uninit_vec.rs:59:9 | LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +104,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:56:9 + --> tests/ui/uninit_vec.rs:63:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -105,7 +115,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:132:9 + --> tests/ui/uninit_vec.rs:139:9 | LL | let mut vec: Vec<T> = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -115,5 +125,5 @@ LL | vec.set_len(10); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs index 0dc21f4ce94..1d42f81711b 100644 --- a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs +++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::unneeded_field_pattern)] -#![allow(dead_code, unused)] +#![allow(dead_code, unused, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/unused_result_ok.fixed b/src/tools/clippy/tests/ui/unused_result_ok.fixed new file mode 100644 index 00000000000..e78fde5c9e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_result_ok.fixed @@ -0,0 +1,40 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::unused_result_ok)] +#![allow(dead_code)] + +#[macro_use] +extern crate proc_macros; + +fn bad_style(x: &str) { + let _ = x.parse::<u32>(); +} + +fn good_style(x: &str) -> Option<u32> { + x.parse::<u32>().ok() +} + +#[rustfmt::skip] +fn strange_parse(x: &str) { + let _ = x . parse::<i32>(); +} + +macro_rules! v { + () => { + Ok::<(), ()>(()) + }; +} + +macro_rules! w { + () => { + let _ = Ok::<(), ()>(()); + }; +} + +fn main() { + let _ = v!(); + w!(); + + external! { + Ok::<(),()>(()).ok(); + }; +} diff --git a/src/tools/clippy/tests/ui/unused_result_ok.rs b/src/tools/clippy/tests/ui/unused_result_ok.rs new file mode 100644 index 00000000000..117d64c4cec --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_result_ok.rs @@ -0,0 +1,40 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::unused_result_ok)] +#![allow(dead_code)] + +#[macro_use] +extern crate proc_macros; + +fn bad_style(x: &str) { + x.parse::<u32>().ok(); +} + +fn good_style(x: &str) -> Option<u32> { + x.parse::<u32>().ok() +} + +#[rustfmt::skip] +fn strange_parse(x: &str) { + x . parse::<i32>() . ok (); +} + +macro_rules! v { + () => { + Ok::<(), ()>(()) + }; +} + +macro_rules! w { + () => { + Ok::<(), ()>(()).ok(); + }; +} + +fn main() { + v!().ok(); + w!(); + + external! { + Ok::<(),()>(()).ok(); + }; +} diff --git a/src/tools/clippy/tests/ui/unused_result_ok.stderr b/src/tools/clippy/tests/ui/unused_result_ok.stderr new file mode 100644 index 00000000000..241e0c71261 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_result_ok.stderr @@ -0,0 +1,52 @@ +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:9:5 + | +LL | x.parse::<u32>().ok(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unused-result-ok` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_result_ok)]` +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = x.parse::<u32>(); + | ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:18:5 + | +LL | x . parse::<i32>() . ok (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = x . parse::<i32>(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:34:5 + | +LL | v!().ok(); + | ^^^^^^^^^ + | +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = v!(); + | ~~~~~~~~~~~~ + +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:29:9 + | +LL | Ok::<(), ()>(()).ok(); + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | w!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `w` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = Ok::<(), ()>(()); + | ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs index 96368a07053..a56bd0a8d07 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs @@ -31,7 +31,7 @@ fn main() { // Don't trigger on unwrap_err on an option Some(3).unwrap_err(); - Some(3).expect_err("Hellow none!"); + Some(3).expect_err("Hello none!"); // Issue #11245: The `Err` variant can never be constructed so do not lint this. let x: Result<(), !> = Ok(()); diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed index 59b5c858d04..b8087c6e000 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -456,6 +456,15 @@ fn fn_once_closure() { }); } +fn issue13123() { + let mut it = 0..20; + 'label: for n in it { + if n % 25 == 0 { + break 'label; + } + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs index 559513d5694..8e02f59b512 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -456,6 +456,15 @@ fn fn_once_closure() { }); } +fn issue13123() { + let mut it = 0..20; + 'label: while let Some(n) = it.next() { + if n % 25 == 0 { + break 'label; + } + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr index 8ff1f23644b..d96b26acf34 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -160,8 +160,14 @@ LL | while let Some(x) = it.next() { error: this loop could be written as a `for` loop --> tests/ui/while_let_on_iterator.rs:461:5 | +LL | 'label: while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:470:5 + | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index eba5405e67e..68328333937 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] diff --git a/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml b/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml index 85a91cd2dec..98b4fb7aa50 100644 --- a/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml +++ b/src/tools/clippy/tests/workspace_test/path_dep/Cargo.toml @@ -1,3 +1,6 @@ [package] name = "path_dep" version = "0.1.0" + +[features] +primary_package_test = [] diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html index 0c0f28e4fbd..300c9de178f 100644 --- a/src/tools/clippy/util/gh-pages/index.html +++ b/src/tools/clippy/util/gh-pages/index.html @@ -541,7 +541,7 @@ Otherwise, have a great day =^.^= <div class="col-12 col-md-5 search-control"> <div class="input-group"> <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label> - <input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input" + <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input" ng-model="search" ng-blur="updatePath()" ng-keyup="$event.keyCode == 13 && updatePath()" ng-model-options="{debounce: 50}" /> <span class="input-group-btn"> @@ -605,7 +605,7 @@ Otherwise, have a great day =^.^= <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> </div> <!-- Jump to source --> - <div class="lint-additional-info-item"> + <div class="lint-additional-info-item" ng-if="lint.id_span"> <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a> </div> </div> diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 661f80a6d34..ed1e090e1b5 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -1,5 +1,5 @@ (function () { - var md = window.markdownit({ + const md = window.markdownit({ html: true, linkify: true, typographer: true, @@ -17,7 +17,7 @@ }); function scrollToLint(lintId) { - var target = document.getElementById(lintId); + const target = document.getElementById(lintId); if (!target) { return; } @@ -25,21 +25,17 @@ } function scrollToLintByURL($scope, $location) { - var removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { + const removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { scrollToLint($location.path().substring(1)); removeListener(); }); } function selectGroup($scope, selectedGroup) { - var groups = $scope.groups; - for (var group in groups) { + const groups = $scope.groups; + for (const group in groups) { if (groups.hasOwnProperty(group)) { - if (group === selectedGroup) { - groups[group] = true; - } else { - groups[group] = false; - } + groups[group] = group === selectedGroup; } } } @@ -108,7 +104,7 @@ }) .controller("lintList", function ($scope, $http, $location, $timeout) { // Level filter - var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; + const LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; $scope.levels = { ...LEVEL_FILTERS_DEFAULT }; $scope.byLevels = function (lint) { return $scope.levels[lint.level]; @@ -367,7 +363,7 @@ } $scope.clearVersionFilters = function () { - for (let filter in $scope.versionFilters) { + for (const filter in $scope.versionFilters) { $scope.versionFilters[filter] = { enabled: false, minorVersion: null }; } } @@ -378,7 +374,7 @@ $scope.updateVersionFilters = function() { for (const filter in $scope.versionFilters) { - let minorVersion = $scope.versionFilters[filter].minorVersion; + const minorVersion = $scope.versionFilters[filter].minorVersion; // 1.29.0 and greater if (minorVersion && minorVersion > 28) { @@ -391,14 +387,14 @@ } $scope.byVersion = function(lint) { - let filters = $scope.versionFilters; + const filters = $scope.versionFilters; for (const filter in filters) { if (filters[filter].enabled) { - let minorVersion = filters[filter].minorVersion; + const minorVersion = filters[filter].minorVersion; // Strip the "pre " prefix for pre 1.29.0 lints - let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; - let lintMinorVersion = lintVersion.substring(2, 4); + const lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + const lintMinorVersion = lintVersion.substring(2, 4); switch (filter) { // "=" gets the highest priority, since all filters are inclusive @@ -441,8 +437,8 @@ // Search the description // The use of `for`-loops instead of `foreach` enables us to return early - let terms = searchStr.split(" "); - let docsLowerCase = lint.docs.toLowerCase(); + const terms = searchStr.split(" "); + const docsLowerCase = lint.docs.toLowerCase(); for (index = 0; index < terms.length; index++) { // This is more likely and will therefore be checked first if (docsLowerCase.indexOf(terms[index]) !== -1) { @@ -479,7 +475,7 @@ const clipboard = document.getElementById("clipboard-" + lint.id); if (clipboard) { let resetClipboardTimeout = null; - let resetClipboardIcon = clipboard.innerHTML; + const resetClipboardIcon = clipboard.innerHTML; function resetClipboard() { resetClipboardTimeout = null; @@ -511,7 +507,7 @@ $scope.data = data; $scope.loading = false; - var selectedGroup = getQueryVariable("sel"); + const selectedGroup = getQueryVariable("sel"); if (selectedGroup) { selectGroup($scope, selectedGroup.toLowerCase()); } @@ -519,7 +515,7 @@ scrollToLintByURL($scope, $location); setTimeout(function () { - var el = document.getElementById('filter-input'); + const el = document.getElementById('filter-input'); if (el) { el.focus() } }, 0); }) @@ -531,10 +527,10 @@ })(); function getQueryVariable(variable) { - var query = window.location.search.substring(1); - var vars = query.split('&'); - for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split('='); + const query = window.location.search.substring(1); + const vars = query.split('&'); + for (const entry of vars) { + const pair = entry.split('='); if (decodeURIComponent(pair[0]) == variable) { return decodeURIComponent(pair[1]); } @@ -579,6 +575,32 @@ function setTheme(theme, store) { } } +function handleShortcut(ev) { + if (ev.ctrlKey || ev.altKey || ev.metaKey) { + return; + } + + if (document.activeElement.tagName === "INPUT") { + if (ev.key === "Escape") { + document.activeElement.blur(); + } + } else { + switch (ev.key) { + case "s": + case "S": + case "/": + ev.preventDefault(); // To prevent the key to be put into the input. + document.getElementById("search-input").focus(); + break; + default: + break; + } + } +} + +document.addEventListener("keypress", handleShortcut); +document.addEventListener("keydown", handleShortcut); + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); const theme = localStorage.getItem('clippy-lint-list-theme'); diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index d0820cfc2a0..edf9e5c5393 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -2,6 +2,8 @@ name = "collect-license-metadata" version = "0.1.0" edition = "2021" +description = "Runs the reuse tool and caches the output, so rust toolchain devs don't need to have reuse installed" +license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.65" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index ca6aa01d78c..dce36bb17b6 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -8,6 +8,11 @@ use anyhow::Error; use crate::licenses::LicensesInterner; +/// The entry point to the binary. +/// +/// You should probably let `bootstrap` execute this program instead of running it directly. +/// +/// Run `x.py run collect-license-metadata` fn main() -> Result<(), Error> { let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into(); let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into(); diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index 0706f3bee05..50c909793f5 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -170,6 +170,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-32bit", "only-64bit", "only-aarch64", + "only-aarch64-unknown-linux-gnu", "only-apple", "only-arm", "only-avr", @@ -204,6 +205,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-watchos", "only-windows", "only-windows-gnu", + "only-windows-msvc", "only-x86", "only-x86_64", "only-x86_64-fortanix-unknown-sgx", diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index 899ef0f8a6c..404101abd41 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -2,10 +2,14 @@ name = "generate-copyright" version = "0.1.0" edition = "2021" +description = "Produces a manifest of all the copyrighted materials in the Rust Toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.65" +cargo_metadata = "0.18.1" +rinja = "0.3.0" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" +thiserror = "1" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs new file mode 100644 index 00000000000..c85e4aa371a --- /dev/null +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -0,0 +1,191 @@ +//! Gets metadata about a workspace from Cargo + +use std::collections::BTreeMap; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; + +/// Describes how this module can fail +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("I/O Error: {0:?}")] + Io(#[from] std::io::Error), + #[error("Failed get output from cargo-metadata: {0:?}")] + GettingMetadata(#[from] cargo_metadata::Error), + #[error("Failed to run cargo vendor: {0:?}")] + LaunchingVendor(std::io::Error), + #[error("Failed to complete cargo vendor")] + RunningVendor, + #[error("Bad path {0:?} whilst scraping files")] + Scraping(PathBuf), +} + +/// Uniquely describes a package on crates.io +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Package { + /// The name of the package + pub name: String, + /// The version number + pub version: String, +} + +/// Extra data about a package +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PackageMetadata { + /// The license it is under + pub license: String, + /// The list of authors from the package metadata + pub authors: Vec<String>, + /// A list of important files from the package, with their contents. + /// + /// This includes *COPYRIGHT*, *NOTICE*, *AUTHOR*, *LICENSE*, and *LICENCE* files, case-insensitive. + pub notices: BTreeMap<String, String>, + /// If this is true, this dep is in the Rust Standard Library + pub is_in_libstd: Option<bool>, +} + +/// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data. +/// +/// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can +/// grab the license files. +/// +/// Any dependency with a path beginning with `root_path` is ignored, as we +/// assume `reuse` has covered it already. +pub fn get_metadata_and_notices( + cargo: &Path, + dest: &Path, + root_path: &Path, + manifest_paths: &[&Path], +) -> Result<BTreeMap<Package, PackageMetadata>, Error> { + let mut output = get_metadata(cargo, root_path, manifest_paths)?; + + // Now do a cargo-vendor and grab everything + let vendor_path = dest.join("vendor"); + println!("Vendoring deps into {}...", vendor_path.display()); + run_cargo_vendor(cargo, &vendor_path, manifest_paths)?; + + // Now for each dependency we found, go and grab any important looking files + for (package, metadata) in output.iter_mut() { + load_important_files(package, metadata, &vendor_path)?; + } + + Ok(output) +} + +/// Use `cargo metadata` to get a list of dependencies and their license data. +/// +/// Any dependency with a path beginning with `root_path` is ignored, as we +/// assume `reuse` has covered it already. +pub fn get_metadata( + cargo: &Path, + root_path: &Path, + manifest_paths: &[&Path], +) -> Result<BTreeMap<Package, PackageMetadata>, Error> { + let mut output = BTreeMap::new(); + // Look at the metadata for each manifest + for manifest_path in manifest_paths { + if manifest_path.file_name() != Some(OsStr::new("Cargo.toml")) { + panic!("cargo_manifest::get requires a path to a Cargo.toml file"); + } + let metadata = cargo_metadata::MetadataCommand::new() + .cargo_path(cargo) + .env("RUSTC_BOOTSTRAP", "1") + .manifest_path(manifest_path) + .exec()?; + for package in metadata.packages { + let manifest_path = package.manifest_path.as_path(); + if manifest_path.starts_with(root_path) { + // it's an in-tree dependency and reuse covers it + continue; + } + // otherwise it's an out-of-tree dependency + let package_id = Package { name: package.name, version: package.version.to_string() }; + output.insert( + package_id, + PackageMetadata { + license: package.license.unwrap_or_else(|| String::from("Unspecified")), + authors: package.authors, + notices: BTreeMap::new(), + is_in_libstd: None, + }, + ); + } + } + + Ok(output) +} + +/// Run cargo-vendor, fetching into the given dir +fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Result<(), Error> { + let mut vendor_command = std::process::Command::new(cargo); + vendor_command.env("RUSTC_BOOTSTRAP", "1"); + vendor_command.arg("vendor"); + vendor_command.arg("--quiet"); + vendor_command.arg("--versioned-dirs"); + for manifest_path in manifest_paths { + vendor_command.arg("-s"); + vendor_command.arg(manifest_path); + } + vendor_command.arg(dest); + + let vendor_status = vendor_command.status().map_err(Error::LaunchingVendor)?; + + if !vendor_status.success() { + return Err(Error::RunningVendor); + } + + Ok(()) +} + +/// Add important files off disk into this dependency. +/// +/// Maybe one-day Cargo.toml will contain enough information that we don't need +/// to do this manual scraping. +fn load_important_files( + package: &Package, + dep: &mut PackageMetadata, + vendor_root: &Path, +) -> Result<(), Error> { + let name_version = format!("{}-{}", package.name, package.version); + println!("Scraping notices for {}...", name_version); + let dep_vendor_path = vendor_root.join(name_version); + for entry in std::fs::read_dir(dep_vendor_path)? { + let entry = entry?; + let metadata = entry.metadata()?; + let path = entry.path(); + let Some(filename) = path.file_name() else { + return Err(Error::Scraping(path)); + }; + let lc_filename = filename.to_ascii_lowercase(); + let lc_filename_str = lc_filename.to_string_lossy(); + let mut keep = false; + for m in ["copyright", "licence", "license", "author", "notice"] { + if lc_filename_str.contains(m) { + keep = true; + break; + } + } + if keep { + if metadata.is_dir() { + for inner_entry in std::fs::read_dir(entry.path())? { + let inner_entry = inner_entry?; + if inner_entry.metadata()?.is_file() { + let inner_filename = inner_entry.file_name(); + let inner_filename_str = inner_filename.to_string_lossy(); + let qualified_filename = + format!("{}/{}", lc_filename_str, inner_filename_str); + println!("Scraping {}", qualified_filename); + dep.notices.insert( + qualified_filename.to_string(), + std::fs::read_to_string(inner_entry.path())?, + ); + } + } + } else if metadata.is_file() { + let filename = filename.to_string_lossy(); + println!("Scraping {}", filename); + dep.notices.insert(filename.to_string(), std::fs::read_to_string(path)?); + } + } + } + Ok(()) +} diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index dce1a558697..afa75d0d671 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -1,79 +1,70 @@ -use std::io::Write; -use std::path::PathBuf; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; use anyhow::Error; +use rinja::Template; +mod cargo_metadata; + +#[derive(Template)] +#[template(path = "COPYRIGHT.html")] +struct CopyrightTemplate { + in_tree: Node, + dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>, +} + +/// The entry point to the binary. +/// +/// You should probably let `bootstrap` execute this program instead of running it directly. +/// +/// Run `x.py run generate-copyright` fn main() -> Result<(), Error> { - let dest = env_path("DEST")?; + let dest_file = env_path("DEST")?; + let out_dir = env_path("OUT_DIR")?; + let cargo = env_path("CARGO")?; let license_metadata = env_path("LICENSE_METADATA")?; - let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?; + let collected_tree_metadata: Metadata = + serde_json::from_slice(&std::fs::read(&license_metadata)?)?; - let mut buffer = Vec::new(); - render_recursive(&metadata.files, &mut buffer, 0)?; + let root_path = std::path::absolute(".")?; + let workspace_paths = [ + Path::new("./Cargo.toml"), + Path::new("./src/tools/cargo/Cargo.toml"), + Path::new("./library/Cargo.toml"), + ]; + let mut collected_cargo_metadata = + cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?; - std::fs::write(&dest, &buffer)?; - - Ok(()) -} + let stdlib_set = + cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?; -fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> { - let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>(); - - match node { - Node::Root { children } => { - for child in children { - render_recursive(child, buffer, depth)?; - } - } - Node::Directory { name, children, license } => { - render_license(&prefix, std::iter::once(name), license.as_ref(), buffer)?; - if !children.is_empty() { - writeln!(buffer, "{prefix}")?; - writeln!(buffer, "{prefix}*Exceptions:*")?; - for child in children { - writeln!(buffer, "{prefix}")?; - render_recursive(child, buffer, depth + 1)?; - } - } - } - Node::Group { files, directories, license } => { - render_license(&prefix, directories.iter().chain(files.iter()), Some(license), buffer)?; - } - Node::File { name, license } => { - render_license(&prefix, std::iter::once(name), Some(license), buffer)?; - } + for (key, value) in collected_cargo_metadata.iter_mut() { + value.is_in_libstd = Some(stdlib_set.contains_key(key)); } - Ok(()) -} + let template = CopyrightTemplate { + in_tree: collected_tree_metadata.files, + dependencies: collected_cargo_metadata, + }; -fn render_license<'a>( - prefix: &str, - names: impl Iterator<Item = &'a String>, - license: Option<&License>, - buffer: &mut Vec<u8>, -) -> Result<(), Error> { - for name in names { - writeln!(buffer, "{prefix}**`{name}`** ")?; - } - if let Some(license) = license { - writeln!(buffer, "{prefix}License: `{}`", license.spdx)?; - for copyright in license.copyright.iter() { - writeln!(buffer, "{prefix}Copyright: {copyright}")?; - } - } + let output = template.render()?; + + std::fs::write(&dest_file, output)?; Ok(()) } +/// Describes a tree of metadata for our filesystem tree #[derive(serde::Deserialize)] struct Metadata { files: Node, } -#[derive(serde::Deserialize)] +/// Describes one node in our metadata tree +#[derive(serde::Deserialize, rinja::Template)] #[serde(rename_all = "kebab-case", tag = "type")] +#[template(path = "Node.html")] pub(crate) enum Node { Root { children: Vec<Node> }, Directory { name: String, children: Vec<Node>, license: Option<License> }, @@ -81,12 +72,14 @@ pub(crate) enum Node { Group { files: Vec<String>, directories: Vec<String>, license: License }, } +/// A License has an SPDX license name and a list of copyright holders. #[derive(serde::Deserialize)] struct License { spdx: String, copyright: Vec<String>, } +/// Grab an environment variable as a PathBuf, or fail nicely. fn env_path(var: &str) -> Result<PathBuf, Error> { if let Some(var) = std::env::var_os(var) { Ok(var.into()) diff --git a/src/tools/generate-copyright/templates/COPYRIGHT.html b/src/tools/generate-copyright/templates/COPYRIGHT.html new file mode 100644 index 00000000000..ccb177a54d4 --- /dev/null +++ b/src/tools/generate-copyright/templates/COPYRIGHT.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Copyright notices for The Rust Toolchain</title> +</head> +<body> + +<h1>Copyright notices for The Rust Toolchain</h1> + +<p>This file describes the copyright and licensing information for the source +code within The Rust Project git tree, and the third-party dependencies used +when building the Rust toolchain (including the Rust Standard Library).</p> + +<h2>Table of Contents</h2> +<ul> + <li><a href="#in-tree-files">In-tree files</a></li> + <li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li> +</ul> + +<h2 id="in-tree-files">In-tree files</h2> + +<p>The following licenses cover the in-tree source files that were used in this +release:</p> + +{{ in_tree|safe }} + +<h2 id="out-of-tree-dependencies">Out-of-tree dependencies</h2> + +<p>The following licenses cover the out-of-tree crates that were used in this +release:</p> + +{% for (key, value) in dependencies %} + <h3>📦 {{key.name}}-{{key.version}}</h3> + <p><b>URL:</b> <a href="https://crates.io/crates/{{ key.name }}/{{ key.version }}">https://crates.io/crates/{{ key.name }}/{{ key.version }}</a></p> + <p><b>In libstd:</b> {% if value.is_in_libstd.unwrap() %} Yes {% else %} No {% endif %}</p> + <p><b>Authors:</b> {{ value.authors|join(", ") }}</p> + <p><b>License:</b> {{ value.license }}</p> + {% let len = value.notices.len() %} + {% if len > 0 %} + <p><b>Notices:</b> + {% for (notice_name, notice_text) in value.notices %} + <details> + <summary><code>{{ notice_name }}</code></summary> + <pre> +{{ notice_text }} + </pre> + </details> + {% endfor %} + </p> + {% endif %} +{% endfor %} +</body> +</html> \ No newline at end of file diff --git a/src/tools/generate-copyright/templates/Node.html b/src/tools/generate-copyright/templates/Node.html new file mode 100644 index 00000000000..a71a1bf3b73 --- /dev/null +++ b/src/tools/generate-copyright/templates/Node.html @@ -0,0 +1,71 @@ +{% match self %} + +{% when Node::Root { children } %} + +{% for child in children %} +{{ child|safe }} +{% endfor %} + +{% when Node::Directory { name, children, license } %} + +<div style="border:1px solid black; padding: 5px;"> + + <p> + <b>File/Directory:</b> <code>{{ name }}</code> + </p> + + {% if let Some(license) = license %} + + <p><b>License:</b> {{ license.spdx }}</p> + {% for copyright in license.copyright.iter() %} + <p><b>Copyright:</b> {{ copyright }}</p> + {% endfor %} + + {% endif %} + + {% if !children.is_empty() %} + + <p><b>Exceptions:</b></p> + {% for child in children %} + {{ child|safe }} + {% endfor %} + + {% endif %} + +</div> + +{% when Node::File { name, license } %} + +<div style="border:1px solid black; padding: 5px;"> + <p> + <b>File/Directory:</b> <code>{{ name }}</code> + </p> + + <p><b>License:</b> {{ license.spdx }}</p> + {% for copyright in license.copyright.iter() %} + <p><b>Copyright:</b> {{ copyright }}</p> + {% endfor %} +</div> + +{% when Node::Group { files, directories, license } %} + +<div style="border:1px solid black; padding: 5px;"> + + <p> + <b>File/Directory:</b> + {% for name in files %} + <code>{{ name }}</code> + {% endfor %} + {% for name in directories %} + <code>{{ name }}</code> + {% endfor %} + </p> + + <p><b>License:</b> {{ license.spdx }}</p> + {% for copyright in license.copyright.iter() %} + <p><b>Copyright:</b> {{ copyright }}</p> + {% endfor %} + +</div> + +{% endmatch %} diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index a53dd7eac1e..f72591f0c4b 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -256,7 +256,7 @@ pub struct Thread<'tcx> { /// which then forwards it to 'Resume'. However this argument is implicit in MIR, /// so we have to store it out-of-band. When there are multiple active unwinds, /// the innermost one is always caught first, so we can store them as a stack. - pub(crate) panic_payloads: Vec<Scalar>, + pub(crate) panic_payloads: Vec<ImmTy<'tcx>>, /// Last OS error location in memory. It is a 32-bit integer. pub(crate) last_error: Option<MPlaceTy<'tcx>>, @@ -377,10 +377,6 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { return_place, locals, extra, - body: _, - instance: _, - return_to_block: _, - loc: _, // There are some private fields we cannot access; they contain no tags. .. } = self; @@ -952,7 +948,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( instance, start_abi, - &[*func_arg], + &[func_arg], Some(&ret_place), StackPopCleanup::Root { cleanup: true }, )?; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 53e87751708..1173da46975 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -307,7 +307,8 @@ pub fn create_ecx<'tcx>( // First argument is constructed later, because it's skipped if the entry function uses #[start]. // Second argument (argc): length of `config.args`. - let argc = Scalar::from_target_usize(u64::try_from(config.args.len()).unwrap(), &ecx); + let argc = + ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize); // Third argument (`argv`): created from `config.args`. let argv = { // Put each argument in memory, collect pointers. @@ -334,13 +335,11 @@ pub fn create_ecx<'tcx>( ecx.write_immediate(arg, &place)?; } ecx.mark_immutable(&argvs_place); - // A pointer to that place is the 3rd argument for main. - let argv = argvs_place.to_ref(&ecx); // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`. { let argc_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?; - ecx.write_scalar(argc, &argc_place)?; + ecx.write_immediate(*argc, &argc_place)?; ecx.mark_immutable(&argc_place); ecx.machine.argc = Some(argc_place.ptr()); @@ -348,7 +347,7 @@ pub fn create_ecx<'tcx>( ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, MiriMemoryKind::Machine.into(), )?; - ecx.write_immediate(argv, &argv_place)?; + ecx.write_pointer(argvs_place.ptr(), &argv_place)?; ecx.mark_immutable(&argv_place); ecx.machine.argv = Some(argv_place.ptr()); } @@ -369,7 +368,7 @@ pub fn create_ecx<'tcx>( } ecx.mark_immutable(&cmd_place); } - argv + ecx.mplace_to_ref(&argvs_place)? }; // Return place (in static memory so that it does not count as leak). @@ -405,10 +404,14 @@ pub fn create_ecx<'tcx>( start_instance, Abi::Rust, &[ - Scalar::from_pointer(main_ptr, &ecx).into(), - argc.into(), + ImmTy::from_scalar( + Scalar::from_pointer(main_ptr, &ecx), + // FIXME use a proper fn ptr type + ecx.machine.layouts.const_raw_ptr, + ), + argc, argv, - Scalar::from_u8(sigpipe).into(), + ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8), ], Some(&ret_place), StackPopCleanup::Root { cleanup: true }, @@ -418,7 +421,7 @@ pub fn create_ecx<'tcx>( ecx.call_function( entry_instance, Abi::Rust, - &[argc.into(), argv], + &[argc, argv], Some(&ret_place), StackPopCleanup::Root { cleanup: true }, )?; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 8bc8188f053..1bdf9f06dcd 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -12,13 +12,14 @@ use rustc_apfloat::Float; use rustc_hir::{ def::{DefKind, Namespace}, def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}, + Safety, }; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir; -use rustc_middle::ty::layout::MaybeResult; +use rustc_middle::ty::layout::{FnAbiOf, MaybeResult}; use rustc_middle::ty::{ self, layout::{LayoutOf, TyAndLayout}, @@ -492,48 +493,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, f: ty::Instance<'tcx>, caller_abi: Abi, - args: &[Immediate<Provenance>], + args: &[ImmTy<'tcx>], dest: Option<&MPlaceTy<'tcx>>, stack_pop: StackPopCleanup, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private. - let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi(); - if callee_abi != caller_abi { - throw_ub_format!( - "calling a function with ABI {} using caller ABI {}", - callee_abi.name(), - caller_abi.name() - ) - } - // Push frame. + // Get MIR. let mir = this.load_mir(f.def, None)?; let dest = match dest { Some(dest) => dest.clone(), None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?), }; - this.push_stack_frame(f, mir, &dest, stack_pop)?; - - // Initialize arguments. - let mut callee_args = this.frame().body.args_iter(); - for arg in args { - let local = callee_args - .next() - .ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?; - // Make the local live, and insert the initial value. - this.storage_live(local)?; - let callee_arg = this.local_to_place(local)?; - this.write_immediate(*arg, &callee_arg)?; - } - if callee_args.next().is_some() { - throw_ub_format!("callee has more arguments than expected"); - } - - // Initialize remaining locals. - this.storage_live_for_always_live_locals()?; - Ok(()) + // Construct a function pointer type representing the caller perspective. + let sig = this.tcx.mk_fn_sig( + args.iter().map(|a| a.layout.ty), + dest.layout.ty, + /*c_variadic*/ false, + Safety::Safe, + caller_abi, + ); + let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + + this.init_stack_frame( + f, + mir, + caller_fn_abi, + &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(), + /*with_caller_location*/ false, + &dest, + stack_pop, + ) } /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter @@ -1114,12 +1105,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Make an attempt to get at the instance of the function this is inlined from. let instance: Option<_> = try { let scope = frame.current_source_info()?.scope; - let inlined_parent = frame.body.source_scopes[scope].inlined_parent_scope?; - let source = &frame.body.source_scopes[inlined_parent]; + let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?; + let source = &frame.body().source_scopes[inlined_parent]; source.inlined.expect("inlined_parent_scope points to scope without inline info").0 }; // Fall back to the instance of the function itself. - let instance = instance.unwrap_or(frame.instance); + let instance = instance.unwrap_or(frame.instance()); // Now check the crate it is in. We could try to be clever here and e.g. check if this is // the same crate as `start_fn`, but that would not work for running std tests in Miri, so // we'd need some more hacks anyway. So we just check the name of the crate. If someone @@ -1359,9 +1350,9 @@ impl<'tcx> MiriMachine<'tcx> { /// This is the source of truth for the `is_user_relevant` flag in our `FrameExtra`. pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool { - let def_id = frame.instance.def_id(); + let def_id = frame.instance().def_id(); (def_id.is_local() || self.local_crates.contains(&def_id.krate)) - && !frame.instance.def.requires_caller_location(self.tcx) + && !frame.instance().def.requires_caller_location(self.tcx) } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 3e45a3a7e1a..94598e7d2e3 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1352,7 +1352,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> { // Start recording our event before doing anything else let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() { - let fn_name = frame.instance.to_string(); + let fn_name = frame.instance().to_string(); let entry = ecx.machine.string_cache.entry(fn_name.clone()); let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name)); @@ -1443,7 +1443,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our // concurrency and what it prints is just plain wrong. So we print our own information // instead. (Cc https://github.com/rust-lang/miri/issues/2266) - info!("Leaving {}", ecx.frame().instance); + info!("Leaving {}", ecx.frame().instance()); Ok(()) } @@ -1473,7 +1473,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Needs to be done after dropping frame to show up on the right nesting level. // (Cc https://github.com/rust-lang/miri/issues/2266) if !ecx.active_thread_stack().is_empty() { - info!("Continuing in {}", ecx.frame().instance); + info!("Continuing in {}", ecx.frame().instance()); } res } @@ -1486,7 +1486,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else { panic!("after_local_allocated should only be called on fresh allocations"); }; - let local_decl = &ecx.frame().body.local_decls[local]; + let local_decl = &ecx.frame().body().local_decls[local]; let span = local_decl.source_info.span; ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None)); Ok(()) diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 42babb4c78d..9bb6777a9b0 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -46,8 +46,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut data = Vec::new(); for frame in this.active_thread_stack().iter().rev() { // Match behavior of debuginfo (`FunctionCx::adjusted_span_and_dbg_scope`). - let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body.span); - data.push((frame.instance, span.lo())); + let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body().span); + data.push((frame.instance(), span.lo())); } let ptrs: Vec<_> = data diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 306dce5edcd..ab705ddccab 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -25,7 +25,7 @@ pub struct CatchUnwindData<'tcx> { /// The `catch_fn` callback to call in case of a panic. catch_fn: Pointer, /// The `data` argument for that callback. - data: Scalar, + data: ImmTy<'tcx>, /// The return place from the original call to `try`. dest: MPlaceTy<'tcx>, /// The return block from the original call to `try`. @@ -48,9 +48,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - trace!("miri_start_unwind: {:?}", this.frame().instance); + trace!("miri_start_unwind: {:?}", this.frame().instance()); - let payload = this.read_scalar(payload)?; + let payload = this.read_immediate(payload)?; let thread = this.active_thread_mut(); thread.panic_payloads.push(payload); @@ -80,7 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Get all the arguments. let [try_fn, data, catch_fn] = check_arg_count(args)?; let try_fn = this.read_pointer(try_fn)?; - let data = this.read_scalar(data)?; + let data = this.read_immediate(data)?; let catch_fn = this.read_pointer(catch_fn)?; // Now we make a function call, and pass `data` as first and only argument. @@ -89,7 +89,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( f_instance, Abi::Rust, - &[data.into()], + &[data.clone()], None, // Directly return to caller. StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue }, @@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and we are unwinding, so we should catch that. trace!( "unwinding: found catch_panic frame during unwinding: {:?}", - this.frame().instance + this.frame().instance() ); // We set the return value of `try` to 1, since there was a panic. @@ -140,7 +140,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( f_instance, Abi::Rust, - &[catch_unwind.data.into(), payload.into()], + &[catch_unwind.data, payload], None, // Directly return to caller of `try`. StackPopCleanup::Goto { @@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic, Abi::Rust, - &[msg.to_ref(this)], + &[this.mplace_to_ref(&msg)?], None, StackPopCleanup::Goto { ret: None, unwind }, ) @@ -188,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic, Abi::Rust, - &[msg.to_ref(this)], + &[this.mplace_to_ref(&msg)?], None, StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable }, ) @@ -207,9 +207,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Forward to `panic_bounds_check` lang item. // First arg: index. - let index = this.read_scalar(&this.eval_operand(index, None)?)?; + let index = this.read_immediate(&this.eval_operand(index, None)?)?; // Second arg: len. - let len = this.read_scalar(&this.eval_operand(len, None)?)?; + let len = this.read_immediate(&this.eval_operand(len, None)?)?; // Call the lang item. let panic_bounds_check = this.tcx.lang_items().panic_bounds_check_fn().unwrap(); @@ -217,7 +217,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic_bounds_check, Abi::Rust, - &[index.into(), len.into()], + &[index, len], None, StackPopCleanup::Goto { ret: None, unwind }, )?; @@ -226,9 +226,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Forward to `panic_misaligned_pointer_dereference` lang item. // First arg: required. - let required = this.read_scalar(&this.eval_operand(required, None)?)?; + let required = this.read_immediate(&this.eval_operand(required, None)?)?; // Second arg: found. - let found = this.read_scalar(&this.eval_operand(found, None)?)?; + let found = this.read_immediate(&this.eval_operand(found, None)?)?; // Call the lang item. let panic_misaligned_pointer_dereference = @@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic_misaligned_pointer_dereference, Abi::Rust, - &[required.into(), found.into()], + &[required, found], None, StackPopCleanup::Goto { ret: None, unwind }, )?; diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 87074468789..52d83cd7299 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -315,6 +315,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: Technically, the reason should be `DLL_PROCESS_DETACH` when the main thread exits // but std treats both the same. let reason = this.eval_windows("c", "DLL_THREAD_DETACH"); + let null_ptr = + ImmTy::from_scalar(Scalar::null_ptr(this), this.machine.layouts.const_raw_ptr); // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown @@ -322,7 +324,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( thread_callback, Abi::System { unwind: false }, - &[Scalar::null_ptr(this).into(), reason.into(), Scalar::null_ptr(this).into()], + &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr], None, StackPopCleanup::Root { cleanup: true }, )?; @@ -343,7 +345,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( instance, Abi::C { unwind: false }, - &[data.into()], + &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)], None, StackPopCleanup::Root { cleanup: true }, )?; @@ -380,7 +382,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( instance, Abi::C { unwind: false }, - &[ptr.into()], + &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)], None, StackPopCleanup::Root { cleanup: true }, )?; diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 83bb95c797d..56e8270aa62 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -1,5 +1,4 @@ use crate::*; -use rustc_middle::ty::layout::LayoutOf; use rustc_target::spec::abi::Abi; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -24,7 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { start_routine, Abi::C { unwind: false }, func_arg, - this.layout_of(this.tcx.types.usize)?, + this.machine.layouts.mut_raw_ptr, )?; Ok(()) diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs index 96dd99e8844..39b1c3007cb 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs @@ -1,11 +1,12 @@ //@ignore-target-windows: No pthreads on Windows +//~^ERROR: calling a function with more arguments than it expected //! The thread function must have exactly one argument. use std::{mem, ptr}; extern "C" fn thread_start() -> *mut libc::c_void { - panic!() //~ ERROR: callee has fewer arguments than expected + panic!() } fn main() { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr index ca6a05ac7dd..aa67420c753 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr @@ -1,14 +1,10 @@ -error: Undefined Behavior: callee has fewer arguments than expected - --> $DIR/libc_pthread_create_too_few_args.rs:LL:CC - | -LL | panic!() - | ^^^^^^^^ callee has fewer arguments than expected +error: Undefined Behavior: calling a function with more arguments than it expected | + = note: calling a function with more arguments than it expected + = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE on thread `unnamed-ID`: - = note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs index d8fbc68d344..fc2ab71dff7 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs @@ -1,11 +1,12 @@ //@ignore-target-windows: No pthreads on Windows +//~^ERROR: calling a function with fewer arguments than it requires //! The thread function must have exactly one argument. use std::{mem, ptr}; extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void { - panic!() //~ ERROR: callee has more arguments than expected + panic!() } fn main() { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr index 6ab48a76666..4de947b1694 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr @@ -1,14 +1,10 @@ -error: Undefined Behavior: callee has more arguments than expected - --> $DIR/libc_pthread_create_too_many_args.rs:LL:CC - | -LL | panic!() - | ^^^^^^^^ callee has more arguments than expected +error: Undefined Behavior: calling a function with fewer arguments than it requires | + = note: calling a function with fewer arguments than it requires + = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE on thread `unnamed-ID`: - = note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs index fd667fbe454..6a7a26710d1 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs @@ -9,7 +9,7 @@ fn main() { // Make sure we check the ABI when Miri itself invokes a function // as part of a shim implementation. std::intrinsics::catch_unwind( - //~^ ERROR: calling a function with ABI C using caller ABI Rust + //~^ ERROR: calling a function with calling convention C using calling convention Rust std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn), std::ptr::null_mut(), |_, _| unreachable!(), diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr index 501f17c86d6..890fed09e48 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with ABI C using caller ABI Rust +error: Undefined Behavior: calling a function with calling convention C using calling convention Rust --> $DIR/check_callback_abi.rs:LL:CC | LL | / std::intrinsics::catch_unwind( @@ -7,7 +7,7 @@ LL | | std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn), LL | | std::ptr::null_mut(), LL | | |_, _| unreachable!(), LL | | ); - | |_________^ calling a function with ABI C using caller ABI Rust + | |_________^ calling a function with calling convention C using calling convention Rust | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs index a226783155b..40b8e23a33c 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.rs +++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs @@ -1,7 +1,7 @@ #![feature(start)] #![no_std] -//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort -//@normalize-stderr-test: "id 20" -> "id $$ALLOC" +//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort +//@normalize-stderr-test: "id 21" -> "id $$ALLOC" //@only-target-linux: alloc IDs differ between OSes (due to extern static allocations) extern "Rust" { diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs index fb22780eaa0..f8d1666adda 100644 --- a/src/tools/run-make-support/src/external_deps/c_build.rs +++ b/src/tools/run-make-support/src/external_deps/c_build.rs @@ -12,14 +12,31 @@ use crate::targets::{is_darwin, is_msvc, is_windows}; /// Built from a C file. #[track_caller] pub fn build_native_static_lib(lib_name: &str) -> PathBuf { + build_native_static_lib_internal(lib_name, false) +} + +/// Builds an optimized static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C file. +#[track_caller] +pub fn build_native_static_lib_optimized(lib_name: &str) -> PathBuf { + build_native_static_lib_internal(lib_name, true) +} + +#[track_caller] +fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf { let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; let src = format!("{lib_name}.c"); let lib_path = static_lib_name(lib_name); - if is_msvc() { - cc().arg("-c").out_exe(&obj_file).input(src).run(); - } else { - cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); - }; + + let mut cc = cc(); + if !is_msvc() { + cc.arg("-v"); + } + if optimzed { + cc.optimize(); + } + cc.arg("-c").out_exe(&obj_file).input(src).optimize().run(); + let obj_file = if is_msvc() { PathBuf::from(format!("{lib_name}.obj")) } else { diff --git a/src/tools/run-make-support/src/external_deps/cc.rs b/src/tools/run-make-support/src/external_deps/cc.rs index 36cef15781f..011ad89e170 100644 --- a/src/tools/run-make-support/src/external_deps/cc.rs +++ b/src/tools/run-make-support/src/external_deps/cc.rs @@ -115,6 +115,17 @@ impl Cc { self.cmd.arg(path.as_ref()); self } + + /// Optimize the output. + /// Equivalent to `-O3` for GNU-compatible linkers or `-O2` for MSVC linkers. + pub fn optimize(&mut self) -> &mut Self { + if is_msvc() { + self.cmd.arg("-O2"); + } else { + self.cmd.arg("-O3"); + } + self + } } /// `EXTRACFLAGS` diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index fc7e5ceae40..a44dd00ad79 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -45,7 +45,7 @@ pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rust // These rely on external dependencies. pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc}; -pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx}; +pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_optimized, build_native_static_lib_cxx}; pub use clang::{clang, Clang}; pub use htmldocck::htmldocck; pub use llvm::{ diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index a2cfdea712e..14f0a9cd23d 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,6 +1,5 @@ run-make/branch-protection-check-IBT/Makefile run-make/cat-and-grep-sanity-check/Makefile -run-make/cross-lang-lto-upstream-rlibs/Makefile run-make/dep-info-doesnt-run-much/Makefile run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile @@ -13,27 +12,15 @@ run-make/libs-through-symlinks/Makefile run-make/libtest-json/Makefile run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile -run-make/long-linker-command-lines-cmd-exe/Makefile -run-make/long-linker-command-lines/Makefile run-make/macos-deployment-target/Makefile run-make/min-global-align/Makefile run-make/native-link-modifier-bundle/Makefile run-make/no-alloc-shim/Makefile -run-make/pdb-buildinfo-cl-cmd/Makefile -run-make/pgo-gen-lto/Makefile -run-make/pgo-indirect-call-promotion/Makefile -run-make/raw-dylib-alt-calling-convention/Makefile -run-make/raw-dylib-c/Makefile -run-make/redundant-libs/Makefile run-make/remap-path-prefix-dwarf/Makefile run-make/reproducible-build/Makefile run-make/rlib-format-packed-bundled-libs/Makefile -run-make/simd-ffi/Makefile run-make/split-debuginfo/Makefile -run-make/staticlib-dylib-linkage/Makefile run-make/symbol-mangling-hashed/Makefile run-make/sysroot-crates-are-unstable/Makefile -run-make/thumb-none-cortex-m/Makefile -run-make/thumb-none-qemu/Makefile run-make/translation/Makefile run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index e23e931b0eb..89011bbb48f 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -192,6 +192,7 @@ const EXCEPTIONS_RUSTBOOK: ExceptionList = &[ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ // tidy-alphabetical-start ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), + ("cranelift-bitset", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"), @@ -511,6 +512,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "bumpalo", "cfg-if", "cranelift-bforest", + "cranelift-bitset", "cranelift-codegen", "cranelift-codegen-meta", "cranelift-codegen-shared", diff --git a/tests/assembly/x86-return-float.rs b/tests/assembly/x86-return-float.rs index c4a2c1ad44e..f9ebf53dddc 100644 --- a/tests/assembly/x86-return-float.rs +++ b/tests/assembly/x86-return-float.rs @@ -8,9 +8,9 @@ // Force frame pointers to make ASM more consistent between targets //@ compile-flags: -O -C force-frame-pointers //@ filecheck-flags: --implicit-check-not fld --implicit-check-not fst -//@ revisions: unix windows -//@[unix] ignore-windows -//@[windows] only-windows +//@ revisions: normal win +//@[normal] ignore-windows +//@[win] only-windows #![crate_type = "lib"] #![feature(f16, f128)] @@ -190,10 +190,10 @@ pub unsafe fn call_f64_f64(x: &mut (f64, f64)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f64_f64 - // unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] - // windows: movsd (%esp), %[[VAL1:.*]] - // windows-NEXT: movsd 8(%esp), %[[VAL2:.*]] + // normal: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] + // win: movsd (%esp), %[[VAL1:.*]] + // win-NEXT: movsd 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]]) // CHECK-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) *x = get_f64_f64(); @@ -207,13 +207,13 @@ pub unsafe fn call_f32_f64(x: &mut (f32, f64)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f32_f64 - // unix: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] - // windows: movss (%esp), %[[VAL1:.*]] - // windows-NEXT: movsd 8(%esp), %[[VAL2:.*]] + // normal: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] + // win: movss (%esp), %[[VAL1:.*]] + // win-NEXT: movsd 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movss %[[VAL1]], (%[[PTR]]) - // unix-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) - // windows-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) + // normal-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) + // win-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) *x = get_f32_f64(); } @@ -225,10 +225,10 @@ pub unsafe fn call_f64_f32(x: &mut (f64, f32)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f64_f32 - // unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] - // windows: movsd (%esp), %[[VAL1:.*]] - // windows-NEXT: movss 8(%esp), %[[VAL2:.*]] + // normal: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] + // win: movsd (%esp), %[[VAL1:.*]] + // win-NEXT: movss 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]]) // CHECK-NEXT: movss %[[VAL2]], 8(%[[PTR]]) *x = get_f64_f32(); @@ -257,10 +257,10 @@ pub unsafe fn call_f64_other(x: &mut (f64, usize)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f64_other - // unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] - // windows: movsd (%esp), %[[VAL1:.*]] - // windows-NEXT: movl 8(%esp), %[[VAL2:.*]] + // normal: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] + // win: movsd (%esp), %[[VAL1:.*]] + // win-NEXT: movl 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]]) // CHECK-NEXT: movl %[[VAL2]], 8(%[[PTR]]) *x = get_f64_other(); @@ -289,13 +289,13 @@ pub unsafe fn call_other_f64(x: &mut (usize, f64)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_other_f64 - // unix: movl [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] - // windows: movl (%esp), %[[VAL1:.*]] - // windows-NEXT: movsd 8(%esp), %[[VAL2:.*]] + // normal: movl [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] + // win: movl (%esp), %[[VAL1:.*]] + // win-NEXT: movsd 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movl %[[VAL1]], (%[[PTR]]) - // unix-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) - // windows-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) + // normal-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) + // win-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) *x = get_other_f64(); } diff --git a/tests/codegen/aarch64-struct-align-128.rs b/tests/codegen/aarch64-struct-align-128.rs index d1b4132d501..3fed19d96b1 100644 --- a/tests/codegen/aarch64-struct-align-128.rs +++ b/tests/codegen/aarch64-struct-align-128.rs @@ -1,12 +1,12 @@ // Test that structs aligned to 128 bits are passed with the correct ABI on aarch64. -//@ revisions:linux darwin windows +//@ revisions: linux darwin win //@[linux] compile-flags: --target aarch64-unknown-linux-gnu //@[darwin] compile-flags: --target aarch64-apple-darwin -//@[windows] compile-flags: --target aarch64-pc-windows-msvc +//@[win] compile-flags: --target aarch64-pc-windows-msvc //@[linux] needs-llvm-components: aarch64 //@[darwin] needs-llvm-components: aarch64 -//@[windows] needs-llvm-components: aarch64 +//@[win] needs-llvm-components: aarch64 #![feature(no_core, lang_items)] #![crate_type = "lib"] @@ -39,9 +39,9 @@ pub struct Wrapped8 { } extern "C" { - // linux: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) - // darwin: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) - // windows: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + // linux: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + // darwin: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + // win: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) fn test_8(a: Align8, b: Transparent8, c: Wrapped8); } @@ -69,9 +69,9 @@ pub struct Wrapped16 { } extern "C" { - // linux: declare void @test_16([2 x i64], [2 x i64], i128) - // darwin: declare void @test_16(i128, i128, i128) - // windows: declare void @test_16(i128, i128, i128) + // linux: declare void @test_16([2 x i64], [2 x i64], i128) + // darwin: declare void @test_16(i128, i128, i128) + // win: declare void @test_16(i128, i128, i128) fn test_16(a: Align16, b: Transparent16, c: Wrapped16); } @@ -94,9 +94,9 @@ pub struct WrappedI128 { } extern "C" { - // linux: declare void @test_i128(i128, i128, i128) - // darwin: declare void @test_i128(i128, i128, i128) - // windows: declare void @test_i128(i128, i128, i128) + // linux: declare void @test_i128(i128, i128, i128) + // darwin: declare void @test_i128(i128, i128, i128) + // win: declare void @test_i128(i128, i128, i128) fn test_i128(a: I128, b: TransparentI128, c: WrappedI128); } @@ -121,9 +121,9 @@ pub struct WrappedPacked { } extern "C" { - // linux: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) - // darwin: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) - // windows: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) + // linux: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) + // darwin: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) + // win: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) fn test_packed(a: Packed, b: TransparentPacked, c: WrappedPacked); } diff --git a/tests/codegen/cold-call-declare-and-call.rs b/tests/codegen/cold-call-declare-and-call.rs index cd41c0a6dfb..b18565ee6c3 100644 --- a/tests/codegen/cold-call-declare-and-call.rs +++ b/tests/codegen/cold-call-declare-and-call.rs @@ -1,8 +1,8 @@ -//@ revisions: NORMAL WINDOWS +//@ revisions: NORMAL WIN //@ compile-flags: -C no-prepopulate-passes //@[NORMAL] ignore-windows -//@[WINDOWS] only-windows -//@[WINDOWS] only-x86_64 +//@[WIN] only-windows +//@[WIN] only-x86_64 #![crate_type = "lib"] #![feature(rust_cold_cc)] @@ -14,8 +14,8 @@ // See the comment in `Target::adjust_abi` for why this differs -// WINDOWS: define void @this_should_never_happen(i16 -// WINDOWS: call void @this_should_never_happen(i16 +// WIN: define void @this_should_never_happen(i16 +// WIN: call void @this_should_never_happen(i16 #[no_mangle] pub extern "rust-cold" fn this_should_never_happen(x: u16) {} diff --git a/tests/codegen/default-requires-uwtable.rs b/tests/codegen/default-requires-uwtable.rs index 567bd55ecc3..3cb35cea022 100644 --- a/tests/codegen/default-requires-uwtable.rs +++ b/tests/codegen/default-requires-uwtable.rs @@ -1,9 +1,9 @@ -//@ revisions: WINDOWS ANDROID +//@ revisions: WINDOWS_ ANDROID_ //@ compile-flags: -C panic=abort -Copt-level=0 -//@ [WINDOWS] compile-flags: --target=x86_64-pc-windows-msvc -//@ [WINDOWS] needs-llvm-components: x86 -//@ [ANDROID] compile-flags: --target=armv7-linux-androideabi -//@ [ANDROID] needs-llvm-components: arm +//@ [WINDOWS_] compile-flags: --target=x86_64-pc-windows-msvc +//@ [WINDOWS_] needs-llvm-components: x86 +//@ [ANDROID_] compile-flags: --target=armv7-linux-androideabi +//@ [ANDROID_] needs-llvm-components: arm #![feature(no_core, lang_items)] #![crate_type = "lib"] diff --git a/tests/codegen/instrument-coverage/testprog.rs b/tests/codegen/instrument-coverage/testprog.rs index eea4d9cb3cf..655fe779fca 100644 --- a/tests/codegen/instrument-coverage/testprog.rs +++ b/tests/codegen/instrument-coverage/testprog.rs @@ -1,7 +1,7 @@ //@ edition: 2021 //@ compile-flags: -Zno-profiler-runtime //@ compile-flags: -Cinstrument-coverage -Copt-level=0 -//@ revisions: LINUX DARWIN WINDOWS +//@ revisions: LINUX DARWIN WIN //@ [LINUX] only-linux //@ [LINUX] filecheck-flags: -DINSTR_PROF_DATA=__llvm_prf_data @@ -19,13 +19,13 @@ //@ [DARWIN] filecheck-flags: -DINSTR_PROF_COVFUN=__LLVM_COV,__llvm_covfun //@ [DARWIN] filecheck-flags: -DCOMDAT_IF_SUPPORTED= -//@ [WINDOWS] only-windows -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_DATA=.lprfd$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_NAME=.lprfn$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_CNTS=.lprfc$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_COVMAP=.lcovmap$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_COVFUN=.lcovfun$M -//@ [WINDOWS] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat' +//@ [WIN] only-windows +//@ [WIN] filecheck-flags: -DINSTR_PROF_DATA=.lprfd$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_NAME=.lprfn$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_CNTS=.lprfc$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_COVMAP=.lcovmap$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_COVFUN=.lcovfun$M +//@ [WIN] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat' // ignore-tidy-linelength @@ -71,7 +71,7 @@ fn main() { // Check for metadata, variables, declarations, and function definitions injected // into LLVM IR when compiling with -Cinstrument-coverage. -// WINDOWS: $__llvm_profile_runtime_user = comdat any +// WIN: $__llvm_profile_runtime_user = comdat any // CHECK: @__llvm_coverage_mapping = private constant // CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 @@ -79,7 +79,7 @@ fn main() { // CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant // CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8 -// WINDOWS: @__llvm_profile_runtime = external{{.*}}global i32 +// WIN: @__llvm_profile_runtime = external{{.*}}global i32 // CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global // CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8 @@ -111,10 +111,10 @@ fn main() { // CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] -// WINDOWS: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat { -// WINDOWS-NEXT: %1 = load i32, ptr @__llvm_profile_runtime -// WINDOWS-NEXT: ret i32 %1 -// WINDOWS-NEXT: } +// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat { +// WIN-NEXT: %1 = load i32, ptr @__llvm_profile_runtime +// WIN-NEXT: ret i32 %1 +// WIN-NEXT: } // CHECK: attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind } -// WINDOWS: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline } +// WIN: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline } diff --git a/tests/codegen/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen/issues/issue-107681-unwrap_unchecked.rs new file mode 100644 index 00000000000..7d9679d2322 --- /dev/null +++ b/tests/codegen/issues/issue-107681-unwrap_unchecked.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -O +//@ min-llvm-version: 19 + +// Test for #107681. +// Make sure we don't create `br` or `select` instructions. + +#![crate_type = "lib"] + +use std::iter::Copied; +use std::slice::Iter; + +#[no_mangle] +pub unsafe fn foo(x: &mut Copied<Iter<'_, u32>>) -> u32 { + // CHECK-LABEL: @foo( + // CHECK-NOT: br + // CHECK-NOT: select + // CHECK: [[RET:%.*]] = load i32, ptr + // CHECK-NEXT: ret i32 [[RET]] + x.next().unwrap_unchecked() +} diff --git a/tests/codegen/issues/issue-118306.rs b/tests/codegen/issues/issue-118306.rs new file mode 100644 index 00000000000..8af1c6a971c --- /dev/null +++ b/tests/codegen/issues/issue-118306.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -O +//@ min-llvm-version: 19 +//@ only-x86_64 + +// Test for #118306. +// Make sure we don't create `br` or `select` instructions. + +#![crate_type = "lib"] + +#[no_mangle] +pub fn branchy(input: u64) -> u64 { + // CHECK-LABEL: @branchy( + // CHECK-NEXT: start: + // CHECK-NEXT: [[_2:%.*]] = and i64 [[INPUT:%.*]], 3 + // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i64], ptr @switch.table.branchy, i64 0, i64 [[_2]] + // CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] + // CHECK-NEXT: ret i64 [[SWITCH_LOAD]] + match input % 4 { + 1 | 2 => 1, + 3 => 2, + _ => 0, + } +} diff --git a/tests/codegen/issues/issue-126585.rs b/tests/codegen/issues/issue-126585.rs new file mode 100644 index 00000000000..a468efd728d --- /dev/null +++ b/tests/codegen/issues/issue-126585.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Copt-level=s +//@ min-llvm-version: 19 +//@ only-x86_64 + +// Test for #126585. +// Ensure that this IR doesn't have extra undef phi input, which also guarantees that this asm +// doesn't have subsequent labels and unnecessary `jmp` instructions. + +#![crate_type = "lib"] + +#[no_mangle] +fn checked_div_round(a: u64, b: u64) -> Option<u64> { + // CHECK-LABEL: @checked_div_round + // CHECK: phi + // CHECK-NOT: undef + // CHECK: phi + // CHECK-NOT: undef + match b { + 0 => None, + 1 => Some(a), + // `a / b` is computable and `(a % b) * 2` can not overflow since `b >= 2`. + b => Some(a / b + if (a % b) * 2 >= b { 1 } else { 0 }), + } +} diff --git a/tests/codegen/repr/transparent-sysv64.rs b/tests/codegen/repr/transparent-sysv64.rs index afb06dcc1bd..068414976c5 100644 --- a/tests/codegen/repr/transparent-sysv64.rs +++ b/tests/codegen/repr/transparent-sysv64.rs @@ -1,12 +1,12 @@ -//@ revisions: linux apple windows +//@ revisions: linux apple win //@ compile-flags: -O -C no-prepopulate-passes //@[linux] compile-flags: --target x86_64-unknown-linux-gnu //@[linux] needs-llvm-components: x86 //@[apple] compile-flags: --target x86_64-apple-darwin //@[apple] needs-llvm-components: x86 -//@[windows] compile-flags: --target x86_64-pc-windows-msvc -//@[windows] needs-llvm-components: x86 +//@[win] compile-flags: --target x86_64-pc-windows-msvc +//@[win] needs-llvm-components: x86 #![feature(no_core, lang_items)] #![crate_type = "lib"] diff --git a/tests/codegen/sse42-implies-crc32.rs b/tests/codegen/sse42-implies-crc32.rs index 94fcd77bc88..8a9c496a3a5 100644 --- a/tests/codegen/sse42-implies-crc32.rs +++ b/tests/codegen/sse42-implies-crc32.rs @@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 { _mm_crc32_u8(out, v) } -// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}} +// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32.*"}} diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 1e2c364dbbc..f38a1ae72de 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -1,7 +1,7 @@ //@ revisions: COMPAT INCOMPAT //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu -Copt-level=3 -//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx +//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2 //@ [INCOMPAT] compile-flags: -Ctarget-feature=-avx2,-avx // See also tests/assembly/target-feature-multiple.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,+avx" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 7f0805bc1b4..1b4596ae2cb 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -8,7 +8,7 @@ // is LLVM-14 we can remove the optional regex matching for this feature. //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0 -// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" } +// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 // DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" } diff --git a/tests/crashes/121127.rs b/tests/crashes/121127.rs index 2e64bf68b82..e50dc7763fc 100644 --- a/tests/crashes/121127.rs +++ b/tests/crashes/121127.rs @@ -1,5 +1,5 @@ //@ known-bug: #121127 -//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes -C debuginfo=2 +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes -C debuginfo=2 // Note that as of PR#123949 this only crashes with debuginfo enabled #![feature(specialization)] diff --git a/tests/crashes/122909.rs b/tests/crashes/122909.rs deleted file mode 100644 index 90bba772b91..00000000000 --- a/tests/crashes/122909.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes -//@ known-bug: #122909 - - -use std::sync::{Arc, Context, Weak}; - -pub struct WeakOnce<T>(); -impl<T> WeakOnce<T> { - extern "rust-call" fn try_get(&self) -> Option<Arc<T>> {} - - pub fn get(&self) -> Arc<T> { - self.try_get() - .unwrap_or_else(|| panic!("Singleton {} not available", std::any::type_name::<T>())) - } -} diff --git a/tests/crashes/126272.rs b/tests/crashes/126272.rs deleted file mode 100644 index 3412c7d8f0f..00000000000 --- a/tests/crashes/126272.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ known-bug: rust-lang/rust#126272 - -#![feature(adt_const_params)] -#![allow(incomplete_features)] - -use std::marker::ConstParamTy; - -#[derive(Debug, PartialEq, Eq, ConstParamTy)] -struct Foo { - value: i32, - nested: &'static Bar<std::fmt::Debug>, -} - -#[derive(Debug, PartialEq, Eq, ConstParamTy)] -struct Bar<T>(T); - -struct Test<const F: Foo>; - -fn main() { - let x: Test< - { - Foo { - value: 3, - nested: &Bar(4), - } - }, - > = Test; -} diff --git a/tests/crashes/126896.rs b/tests/crashes/126896.rs index 35bf9d5207a..49c539d7acc 100644 --- a/tests/crashes/126896.rs +++ b/tests/crashes/126896.rs @@ -1,5 +1,5 @@ //@ known-bug: rust-lang/rust#126896 -//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes #![feature(type_alias_impl_trait)] type Two<'a, 'b> = impl std::fmt::Debug; diff --git a/tests/crashes/127299.rs b/tests/crashes/127299.rs deleted file mode 100644 index 7eb78387997..00000000000 --- a/tests/crashes/127299.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: rust-lang/rust#127299 -trait Qux { - fn bar() -> i32; -} - -pub struct Lint { - pub desc: &'static Qux, -} - -static FOO: &Lint = &Lint { desc: "desc" }; - -fn main() {} diff --git a/tests/debuginfo/thread-names.rs b/tests/debuginfo/thread-names.rs index 1b9ada6fc52..6b3b0c7f4b2 100644 --- a/tests/debuginfo/thread-names.rs +++ b/tests/debuginfo/thread-names.rs @@ -1,8 +1,8 @@ //@ compile-flags:-g -//@ revisions: macos windows +//@ revisions: macos win // We can't set the main thread name on Linux because it renames the process (#97191) //@[macos] only-macos -//@[windows] only-windows +//@[win] only-windows //@ ignore-sgx //@ ignore-windows-gnu diff --git a/tests/run-make/crate-loading/multiple-dep-versions-1.rs b/tests/run-make/crate-loading/multiple-dep-versions-1.rs new file mode 100644 index 00000000000..2d351633829 --- /dev/null +++ b/tests/run-make/crate-loading/multiple-dep-versions-1.rs @@ -0,0 +1,6 @@ +#![crate_name = "dependency"] +#![crate_type = "rlib"] +pub struct Type; +pub trait Trait {} +impl Trait for Type {} +pub fn do_something<X: Trait>(_: X) {} diff --git a/tests/run-make/crate-loading/multiple-dep-versions-2.rs b/tests/run-make/crate-loading/multiple-dep-versions-2.rs new file mode 100644 index 00000000000..a5df3dc61ed --- /dev/null +++ b/tests/run-make/crate-loading/multiple-dep-versions-2.rs @@ -0,0 +1,6 @@ +#![crate_name = "dependency"] +#![crate_type = "rlib"] +pub struct Type(pub i32); +pub trait Trait {} +impl Trait for Type {} +pub fn do_something<X: Trait>(_: X) {} diff --git a/tests/run-make/crate-loading/multiple-dep-versions.rs b/tests/run-make/crate-loading/multiple-dep-versions.rs new file mode 100644 index 00000000000..5a6cb03aaa4 --- /dev/null +++ b/tests/run-make/crate-loading/multiple-dep-versions.rs @@ -0,0 +1,8 @@ +extern crate dep_2_reexport; +extern crate dependency; +use dep_2_reexport::do_something; +use dependency::Type; + +fn main() { + do_something(Type); +} diff --git a/tests/run-make/crate-loading/rmake.rs b/tests/run-make/crate-loading/rmake.rs new file mode 100644 index 00000000000..fd5b66ae879 --- /dev/null +++ b/tests/run-make/crate-loading/rmake.rs @@ -0,0 +1,27 @@ +//@ only-linux +//@ ignore-wasm32 +//@ ignore-wasm64 + +use run_make_support::rfs::copy; +use run_make_support::{assert_contains, rust_lib_name, rustc}; + +fn main() { + rustc().input("multiple-dep-versions-1.rs").run(); + rustc().input("multiple-dep-versions-2.rs").extra_filename("2").metadata("2").run(); + + let out = rustc() + .input("multiple-dep-versions.rs") + .extern_("dependency", rust_lib_name("dependency")) + .extern_("dep_2_reexport", rust_lib_name("dependency2")) + .run_fail() + .assert_stderr_contains( + "you have multiple different versions of crate `dependency` in your dependency graph", + ) + .assert_stderr_contains( + "two types coming from two different versions of the same crate are different types \ + even if they look the same", + ) + .assert_stderr_contains("this type doesn't implement the required trait") + .assert_stderr_contains("this type implements the required trait") + .assert_stderr_contains("this is the required trait"); +} diff --git a/tests/run-make/cross-lang-lto-upstream-rlibs/Makefile b/tests/run-make/cross-lang-lto-upstream-rlibs/Makefile deleted file mode 100644 index 6f1caa31a80..00000000000 --- a/tests/run-make/cross-lang-lto-upstream-rlibs/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -include ../tools.mk - -# ignore windows due to libLLVM being present in PATH and the PATH and library path being the same -# (so fixing it is harder). See #57765 for context -ifndef IS_WINDOWS - -# This test makes sure that we don't loose upstream object files when compiling -# staticlibs with -C linker-plugin-lto - -all: staticlib.rs upstream.rs - $(RUSTC) upstream.rs -C linker-plugin-lto -Ccodegen-units=1 - - # Check No LTO - $(RUSTC) staticlib.rs -C linker-plugin-lto -Ccodegen-units=1 -L. -o $(TMPDIR)/staticlib.a - (cd $(TMPDIR); "$(LLVM_BIN_DIR)"/llvm-ar x ./staticlib.a) - # Make sure the upstream object file was included - ls $(TMPDIR)/upstream.*.rcgu.o - - # Cleanup - rm $(TMPDIR)/* - - # Check ThinLTO - $(RUSTC) upstream.rs -C linker-plugin-lto -Ccodegen-units=1 -Clto=thin - $(RUSTC) staticlib.rs -C linker-plugin-lto -Ccodegen-units=1 -Clto=thin -L. -o $(TMPDIR)/staticlib.a - (cd $(TMPDIR); "$(LLVM_BIN_DIR)"/llvm-ar x ./staticlib.a) - ls $(TMPDIR)/upstream.*.rcgu.o - -else - -all: - -endif diff --git a/tests/run-make/cross-lang-lto-upstream-rlibs/rmake.rs b/tests/run-make/cross-lang-lto-upstream-rlibs/rmake.rs new file mode 100644 index 00000000000..f0b8fa75bee --- /dev/null +++ b/tests/run-make/cross-lang-lto-upstream-rlibs/rmake.rs @@ -0,0 +1,57 @@ +// When using the flag -C linker-plugin-lto, static libraries could lose their upstream object +// files during compilation. This bug was fixed in #53031, and this test compiles a staticlib +// dependent on upstream, checking that the upstream object file still exists after no LTO and +// thin LTO. +// See https://github.com/rust-lang/rust/pull/53031 + +use run_make_support::{ + cwd, has_extension, has_prefix, has_suffix, llvm_ar, rfs, rustc, shallow_find_files, + static_lib_name, +}; + +fn main() { + // The test starts with no LTO enabled. + rustc().input("upstream.rs").arg("-Clinker-plugin-lto").codegen_units(1).run(); + rustc() + .input("staticlib.rs") + .arg("-Clinker-plugin-lto") + .codegen_units(1) + .output(static_lib_name("staticlib")) + .run(); + llvm_ar().extract().arg(static_lib_name("staticlib")).run(); + // Ensure the upstream object file was included. + assert_eq!( + shallow_find_files(cwd(), |path| { + has_prefix(path, "upstream.") && has_suffix(path, ".rcgu.o") + }) + .len(), + 1 + ); + // Remove all output files that are not source Rust code for cleanup. + for file in shallow_find_files(cwd(), |path| !has_extension(path, "rs")) { + rfs::remove_file(file) + } + + // Check it again, with Thin LTO. + rustc() + .input("upstream.rs") + .arg("-Clinker-plugin-lto") + .codegen_units(1) + .arg("-Clto=thin") + .run(); + rustc() + .input("staticlib.rs") + .arg("-Clinker-plugin-lto") + .codegen_units(1) + .arg("-Clto=thin") + .output(static_lib_name("staticlib")) + .run(); + llvm_ar().extract().arg(static_lib_name("staticlib")).run(); + assert_eq!( + shallow_find_files(cwd(), |path| { + has_prefix(path, "upstream.") && has_suffix(path, ".rcgu.o") + }) + .len(), + 1 + ); +} diff --git a/tests/run-make/dos-device-input/rmake.rs b/tests/run-make/dos-device-input/rmake.rs new file mode 100644 index 00000000000..dee3b86f095 --- /dev/null +++ b/tests/run-make/dos-device-input/rmake.rs @@ -0,0 +1,13 @@ +//@ only-windows +// Reason: dos devices are a Windows thing + +use std::path::Path; + +use run_make_support::{rustc, static_lib_name}; + +fn main() { + rustc().input(r"\\.\NUL").crate_type("staticlib").run(); + rustc().input(r"\\?\NUL").crate_type("staticlib").run(); + + assert!(Path::new(&static_lib_name("rust_out")).exists()); +} diff --git a/tests/run-make/dump-ice-to-disk/rmake.rs b/tests/run-make/dump-ice-to-disk/rmake.rs index 2fb5c825064..a64103fa516 100644 --- a/tests/run-make/dump-ice-to-disk/rmake.rs +++ b/tests/run-make/dump-ice-to-disk/rmake.rs @@ -4,6 +4,10 @@ // or full. // - Check that disabling ICE logging results in zero files created. // - Check that the ICE files contain some of the expected strings. +// - exercise the -Zmetrics-dir nightly flag +// - verify what happens when both the nightly flag and env variable are set +// - test the RUST_BACKTRACE=0 behavior against the file creation + // See https://github.com/rust-lang/rust/pull/108714 use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_files}; @@ -11,8 +15,8 @@ use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_ fn main() { rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); let default = get_text_from_ice(".").lines().count(); - clear_ice_files(); + clear_ice_files(); rustc().env("RUSTC_ICE", cwd()).input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); let ice_text = get_text_from_ice(cwd()); let default_set = ice_text.lines().count(); @@ -25,7 +29,28 @@ fn main() { ice_files.first().and_then(|f| f.file_name()).and_then(|n| n.to_str()).unwrap(); // Ensure that the ICE dump path doesn't contain `:`, because they cause problems on Windows. assert!(!ice_file_name.contains(":"), "{ice_file_name}"); + assert_eq!(default, default_set); + assert!(default > 0); + // Some of the expected strings in an ICE file should appear. + assert!(content.contains("thread 'rustc' panicked at")); + assert!(content.contains("stack backtrace:")); + + test_backtrace_short(default); + test_backtrace_full(default); + test_backtrace_disabled(default); + + clear_ice_files(); + // The ICE dump is explicitly disabled. Therefore, this should produce no files. + rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let ice_files = shallow_find_files(cwd(), |path| { + has_prefix(path, "rustc-ice") && has_extension(path, "txt") + }); + assert!(ice_files.is_empty()); // There should be 0 ICE files. + + metrics_dir(default); +} +fn test_backtrace_short(baseline: usize) { clear_ice_files(); rustc() .env("RUSTC_ICE", cwd()) @@ -34,6 +59,11 @@ fn main() { .arg("-Ztreat-err-as-bug=1") .run_fail(); let short = get_text_from_ice(cwd()).lines().count(); + // backtrace length in dump shouldn't be changed by RUST_BACKTRACE + assert_eq!(short, baseline); +} + +fn test_backtrace_full(baseline: usize) { clear_ice_files(); rustc() .env("RUSTC_ICE", cwd()) @@ -42,23 +72,49 @@ fn main() { .arg("-Ztreat-err-as-bug=1") .run_fail(); let full = get_text_from_ice(cwd()).lines().count(); + // backtrace length in dump shouldn't be changed by RUST_BACKTRACE + assert_eq!(full, baseline); +} + +fn test_backtrace_disabled(baseline: usize) { clear_ice_files(); + rustc() + .env("RUSTC_ICE", cwd()) + .input("lib.rs") + .env("RUST_BACKTRACE", "0") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + let disabled = get_text_from_ice(cwd()).lines().count(); + // backtrace length in dump shouldn't be changed by RUST_BACKTRACE + assert_eq!(disabled, baseline); +} - // The ICE dump is explicitly disabled. Therefore, this should produce no files. - rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); - let ice_files = shallow_find_files(cwd(), |path| { - has_prefix(path, "rustc-ice") && has_extension(path, "txt") - }); - assert!(ice_files.is_empty()); // There should be 0 ICE files. +fn metrics_dir(baseline: usize) { + test_flag_only(baseline); + test_flag_and_env(baseline); +} - // The line count should not change. - assert_eq!(short, default_set); - assert_eq!(short, default); - assert_eq!(full, default_set); - assert!(default > 0); - // Some of the expected strings in an ICE file should appear. - assert!(content.contains("thread 'rustc' panicked at")); - assert!(content.contains("stack backtrace:")); +fn test_flag_only(baseline: usize) { + clear_ice_files(); + let metrics_arg = format!("-Zmetrics-dir={}", cwd().display()); + rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").arg(metrics_arg).run_fail(); + let output = get_text_from_ice(cwd()).lines().count(); + assert_eq!(output, baseline); +} + +fn test_flag_and_env(baseline: usize) { + clear_ice_files(); + let metrics_arg = format!("-Zmetrics-dir={}", cwd().display()); + let real_dir = cwd().join("actually_put_ice_here"); + rfs::create_dir(real_dir.clone()); + rustc() + .input("lib.rs") + .env("RUSTC_ICE", real_dir.clone()) + .arg("-Ztreat-err-as-bug=1") + .arg(metrics_arg) + .run_fail(); + let output = get_text_from_ice(real_dir).lines().count(); + assert_eq!(output, baseline); } fn clear_ice_files() { diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs index d238ad23f27..b7ef8333267 100644 --- a/tests/run-make/link-args-order/rmake.rs +++ b/tests/run-make/link-args-order/rmake.rs @@ -3,15 +3,14 @@ // checks that linker arguments remain intact and in the order they were originally passed in. // See https://github.com/rust-lang/rust/pull/70665 -//@ ignore-msvc -// Reason: the ld linker does not exist on Windows. - -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { + let linker = if is_msvc() { "msvc" } else { "ld" }; + rustc() .input("empty.rs") - .linker_flavor("ld") + .linker_flavor(linker) .link_arg("a") .link_args("b c") .link_args("d e") @@ -20,7 +19,7 @@ fn main() { .assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#); rustc() .input("empty.rs") - .linker_flavor("ld") + .linker_flavor(linker) .arg("-Zpre-link-arg=a") .arg("-Zpre-link-args=b c") .arg("-Zpre-link-args=d e") diff --git a/tests/run-make/link-dedup/rmake.rs b/tests/run-make/link-dedup/rmake.rs index 9bff3a4b44c..6075f310954 100644 --- a/tests/run-make/link-dedup/rmake.rs +++ b/tests/run-make/link-dedup/rmake.rs @@ -5,20 +5,37 @@ // Without the --cfg flag, there should be a single -ltesta, no more, no less. // See https://github.com/rust-lang/rust/pull/84794 -//@ ignore-msvc +use std::fmt::Write; -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { rustc().input("depa.rs").run(); rustc().input("depb.rs").run(); rustc().input("depc.rs").run(); + let output = rustc().input("empty.rs").cfg("bar").run_fail(); - output.assert_stderr_contains(r#""-ltesta" "-ltestb" "-ltesta""#); - let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_contains(r#""-ltesta""#); - let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_not_contains(r#""-ltestb""#); + output.assert_stderr_contains(needle_from_libs(&["testa", "testb", "testa"])); + let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_not_contains(r#""-ltesta" "-ltesta" "-ltesta""#); + output.assert_stderr_contains(needle_from_libs(&["testa"])); + output.assert_stderr_not_contains(needle_from_libs(&["testb"])); + output.assert_stderr_not_contains(needle_from_libs(&["testa", "testa", "testa"])); + // Adjacent identical native libraries are no longer deduplicated if + // they come from different crates (https://github.com/rust-lang/rust/pull/103311) + // so the following will fail: + //output.assert_stderr_not_contains(needle_from_libs(&["testa", "testa"])); +} + +fn needle_from_libs(libs: &[&str]) -> String { + let mut needle = String::new(); + for lib in libs { + if is_msvc() { + let _ = needle.write_fmt(format_args!(r#""{lib}.lib" "#)); + } else { + let _ = needle.write_fmt(format_args!(r#""-l{lib}" "#)); + } + } + needle.pop(); // remove trailing space + needle } diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/Makefile b/tests/run-make/long-linker-command-lines-cmd-exe/Makefile deleted file mode 100644 index e43aab7f8e0..00000000000 --- a/tests/run-make/long-linker-command-lines-cmd-exe/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo.rs -g - cp foo.bat $(TMPDIR)/ - OUT_DIR="$(TMPDIR)" RUSTC="$(RUSTC_ORIGINAL)" $(call RUN,foo) diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs b/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs index 1d5202dcdb4..a28cc7909fe 100644 --- a/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs +++ b/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs @@ -1,16 +1,3 @@ -// Like the `long-linker-command-lines` test this test attempts to blow -// a command line limit for running the linker. Unlike that test, however, -// this test is testing `cmd.exe` specifically rather than the OS. -// -// Unfortunately `cmd.exe` has a 8192 limit which is relatively small -// in the grand scheme of things and anyone sripting rustc's linker -// is probably using a `*.bat` script and is likely to hit this limit. -// -// This test uses a `foo.bat` script as the linker which just simply -// delegates back to this program. The compiler should use a lower -// limit for arguments before passing everything via `@`, which -// means that everything should still succeed here. - use std::env; use std::fs::{self, File}; use std::io::{BufWriter, Read, Write}; @@ -18,13 +5,8 @@ use std::path::PathBuf; use std::process::Command; fn main() { - if !cfg!(windows) { - return; - } - - let tmpdir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let ok = tmpdir.join("ok"); - let not_ok = tmpdir.join("not_ok"); + let ok = PathBuf::from("ok"); + let not_ok = PathBuf::from("not_ok"); if env::var("YOU_ARE_A_LINKER").is_ok() { match env::args_os().find(|a| a.to_string_lossy().contains("@")) { Some(file) => { @@ -45,7 +27,7 @@ fn main() { for i in (1..).map(|i| i * 10) { println!("attempt: {}", i); - let file = tmpdir.join("bar.rs"); + let file = PathBuf::from("bar.rs"); let mut f = BufWriter::new(File::create(&file).unwrap()); let mut lib_name = String::new(); for _ in 0..i { @@ -63,8 +45,6 @@ fn main() { .arg(&file) .arg("-C") .arg(&bat_linker) - .arg("--out-dir") - .arg(&tmpdir) .env("YOU_ARE_A_LINKER", "1") .env("MY_LINKER", &me) .status() diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/rmake.rs b/tests/run-make/long-linker-command-lines-cmd-exe/rmake.rs new file mode 100644 index 00000000000..60ed2c5bcd2 --- /dev/null +++ b/tests/run-make/long-linker-command-lines-cmd-exe/rmake.rs @@ -0,0 +1,26 @@ +// Like the `long-linker-command-lines` test this test attempts to blow +// a command line limit for running the linker. Unlike that test, however, +// this test is testing `cmd.exe` specifically rather than the OS. +// +// Unfortunately, the maximum length of the string that you can use at the +// command prompt (`cmd.exe`) is 8191 characters. +// Anyone scripting rustc's linker +// is probably using a `*.bat` script and is likely to hit this limit. +// +// This test uses a `foo.bat` script as the linker which just simply +// delegates back to this program. The compiler should use a lower +// limit for arguments before passing everything via `@`, which +// means that everything should still succeed here. +// See https://github.com/rust-lang/rust/pull/47507 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed +//@ only-windows +// Reason: this test is specific to Windows executables + +use run_make_support::{run, rustc}; + +fn main() { + rustc().input("foo.rs").arg("-g").run(); + run("foo"); +} diff --git a/tests/run-make/long-linker-command-lines/Makefile b/tests/run-make/long-linker-command-lines/Makefile deleted file mode 100644 index b573038e344..00000000000 --- a/tests/run-make/long-linker-command-lines/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) - -all: - $(RUSTC) foo.rs -g -O - RUSTC="$(RUSTC_ORIGINAL)" $(call RUN,foo) diff --git a/tests/run-make/long-linker-command-lines/foo.rs b/tests/run-make/long-linker-command-lines/foo.rs index 9d4a701ad87..5b30c06fac9 100644 --- a/tests/run-make/long-linker-command-lines/foo.rs +++ b/tests/run-make/long-linker-command-lines/foo.rs @@ -1,12 +1,3 @@ -// This is a test which attempts to blow out the system limit with how many -// arguments can be passed to a process. This'll successively call rustc with -// larger and larger argument lists in an attempt to find one that's way too -// big for the system at hand. This file itself is then used as a "linker" to -// detect when the process creation succeeds. -// -// Eventually we should see an argument that looks like `@` as we switch from -// passing literal arguments to passing everything in the file. - use std::collections::HashSet; use std::env; use std::fs::{self, File}; @@ -43,8 +34,7 @@ fn read_linker_args(path: &Path) -> String { } fn main() { - let tmpdir = PathBuf::from(env::var_os("TMPDIR").unwrap()); - let ok = tmpdir.join("ok"); + let ok = PathBuf::from("ok"); if env::var("YOU_ARE_A_LINKER").is_ok() { if let Some(file) = env::args_os().find(|a| a.to_string_lossy().contains("@")) { let file = file.to_str().expect("non-utf8 file argument"); @@ -53,11 +43,11 @@ fn main() { return; } - let rustc = env::var_os("RUSTC").unwrap_or("rustc".into()); + let rustc = env::var_os("RUSTC").unwrap(); let me_as_linker = format!("linker={}", env::current_exe().unwrap().display()); for i in (1..).map(|i| i * 100) { println!("attempt: {}", i); - let file = tmpdir.join("bar.rs"); + let file = PathBuf::from("bar.rs"); let mut expected_libs = write_test_case(&file, i); drop(fs::remove_file(&ok)); @@ -65,8 +55,6 @@ fn main() { .arg(&file) .arg("-C") .arg(&me_as_linker) - .arg("--out-dir") - .arg(&tmpdir) .env("YOU_ARE_A_LINKER", "1") .output() .unwrap(); diff --git a/tests/run-make/long-linker-command-lines/rmake.rs b/tests/run-make/long-linker-command-lines/rmake.rs new file mode 100644 index 00000000000..e832d7f03e2 --- /dev/null +++ b/tests/run-make/long-linker-command-lines/rmake.rs @@ -0,0 +1,19 @@ +// This is a test which attempts to blow out the system limit with how many +// arguments can be passed to a process. This'll successively call rustc with +// larger and larger argument lists in an attempt to find one that's way too +// big for the system at hand. This file itself is then used as a "linker" to +// detect when the process creation succeeds. +// +// Eventually we should see an argument that looks like `@` as we switch from +// passing literal arguments to passing everything in the file. +// See https://github.com/rust-lang/rust/issues/41190 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{run, rustc}; + +fn main() { + rustc().input("foo.rs").arg("-g").opt().run(); + run("foo"); +} diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h new file mode 100644 index 00000000000..a2292ae02a3 --- /dev/null +++ b/tests/run-make/mte-ffi/bar.h @@ -0,0 +1,43 @@ +#ifndef __BAR_H +#define __BAR_H + +#include <sys/mman.h> +#include <sys/auxv.h> +#include <sys/prctl.h> +#include <unistd.h> +#include <stdio.h> + +// Set the allocation tag on the destination address using the STG instruction. +#define set_tag(tagged_addr) do { \ + asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ +} while (0) + +int mte_enabled() { + return (getauxval(AT_HWCAP2)) & HWCAP2_MTE; +} + +void *alloc_page() { + // Enable MTE with synchronous checking + if (prctl(PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) + { + perror("prctl() failed"); + } + + // Using `mmap` allows us to ensure that, on systems which support MTE, the allocated + // memory is 16-byte aligned for MTE. + // This also allows us to explicitly specify whether the region should be protected by + // MTE or not. + if (mte_enabled()) { + void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), + PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + } else { + void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + } +} + +#endif // __BAR_H diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c new file mode 100644 index 00000000000..a1590f62765 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_float.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include "bar.h" + +extern void foo(char*); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x1f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } +} + +int main(void) +{ + float *ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (float *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 2.0f; + ptr[1] = 1.5f; + + foo(ptr); // should change the contents of the page and call `bar` + + if (ptr[0] != 0.5f || ptr[1] != 0.2f) { + fprintf(stderr, "invalid data in memory; expected '0.5 0.2', got '%f %f'\n", + ptr[0], ptr[1]); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_function.c b/tests/run-make/mte-ffi/bar_function.c new file mode 100644 index 00000000000..1fa48d32a0c --- /dev/null +++ b/tests/run-make/mte-ffi/bar_function.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include "bar.h" + +typedef void (*fp)(int (*)()); + +extern void foo(fp); + +void bar(int (*ptr)()) { + if (((uintptr_t)ptr >> 56) != 0x2f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } + + int r = (*ptr)(); + if (r != 32) { + fprintf(stderr, "invalid return value; expected 32, got '%d'\n", r); + exit(1); + } +} + +int main(void) +{ + fp ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (fp)((uintptr_t)&bar | 0x1fl << 56); + + foo(ptr); + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c new file mode 100644 index 00000000000..d1c79e95dc9 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_int.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include "bar.h" + +extern void foo(unsigned int *); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x1f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } +} + +int main(void) +{ + // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. + // It's only necessary that the tag is preserved across FFI bounds for this test. + unsigned int *ptr; + + ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 61; + ptr[1] = 62; + + foo(ptr); // should change the contents of the page to start with 0x63 0x64 and call `bar` + + if (ptr[0] != 0x63 || ptr[1] != 0x64) { + fprintf(stderr, "invalid data in memory; expected '63 64', got '%d %d'\n", ptr[0], ptr[1]); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c new file mode 100644 index 00000000000..5669ffd6695 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_string.c @@ -0,0 +1,48 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include "bar.h" + +extern void foo(char*); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x2f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } + + if (strcmp(ptr, "cd")) { + fprintf(stderr, "invalid data in memory; expected 'cd', got '%s'\n", ptr); + exit(1); + } +} + +int main(void) +{ + // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. + // It's only necessary that the tag is preserved across FFI bounds for this test. + char *ptr; + + ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 'a'; + ptr[1] = 'b'; + ptr[2] = '\0'; + + foo(ptr); + + return 0; +} diff --git a/tests/run-make/mte-ffi/foo_float.rs b/tests/run-make/mte-ffi/foo_float.rs new file mode 100644 index 00000000000..c1bedd52494 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_float.rs @@ -0,0 +1,19 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::os::raw::c_float; + +extern "C" { + fn bar(ptr: *const c_float); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *mut c_float) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + unsafe { + *ptr = 0.5; + *ptr.wrapping_add(1) = 0.2; + bar(ptr); + } +} diff --git a/tests/run-make/mte-ffi/foo_function.rs b/tests/run-make/mte-ffi/foo_function.rs new file mode 100644 index 00000000000..2c8e0b26238 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_function.rs @@ -0,0 +1,17 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +extern "C" fn ret32() -> i32 { + 32 +} + +#[no_mangle] +pub extern "C" fn foo(ptr: extern "C" fn(extern "C" fn() -> i32)) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + // Store an arbitrary tag in the tag bits, and convert back to the correct pointer type. + let p = ((ret32 as usize) | (0x2f << 56)) as *const (); + let p: extern "C" fn() -> i32 = unsafe { std::mem::transmute(p) }; + + unsafe { ptr(p) } +} diff --git a/tests/run-make/mte-ffi/foo_int.rs b/tests/run-make/mte-ffi/foo_int.rs new file mode 100644 index 00000000000..106d863cb81 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_int.rs @@ -0,0 +1,19 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::os::raw::c_uint; + +extern "C" { + fn bar(ptr: *const c_uint); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *mut c_uint) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + unsafe { + *ptr = 0x63; + *ptr.wrapping_add(1) = 0x64; + bar(ptr); + } +} diff --git a/tests/run-make/mte-ffi/foo_string.rs b/tests/run-make/mte-ffi/foo_string.rs new file mode 100644 index 00000000000..54744802448 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_string.rs @@ -0,0 +1,27 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::arch::asm; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +extern "C" { + fn bar(ptr: *const c_char); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *const c_char) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + let s = unsafe { CStr::from_ptr(ptr) }; + assert_eq!(s.to_str().unwrap(), "ab"); + + let s = CString::from_vec_with_nul("cd\0".into()).unwrap(); + let mut p = ((s.as_ptr() as usize) | (0x2f << 56)) as *const c_char; + unsafe { + #[cfg(target_feature = "mte")] + asm!("stg {p}, [{p}]", p = inout(reg) p); + + bar(p); + } +} diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs new file mode 100644 index 00000000000..f4fafb796e3 --- /dev/null +++ b/tests/run-make/mte-ffi/rmake.rs @@ -0,0 +1,38 @@ +// Tests that MTE tags and values stored in the top byte of a pointer (TBI) are +// preserved across FFI boundaries (C <-> Rust). +// This test does not require MTE: whilst the test will use MTE if available, if it is not, +// arbitrary tag bits are set using TBI. + +// This test is only valid for AArch64. +// The linker must be explicitly specified when cross-compiling, so it is limited to +// `aarch64-unknown-linux-gnu`. +//@ only-aarch64-unknown-linux-gnu + +use run_make_support::{cc, dynamic_lib_name, extra_c_flags, run, rustc, target}; + +fn main() { + run_test("int"); + run_test("float"); + run_test("string"); + run_test("function"); +} + +fn run_test(variant: &str) { + let flags = { + let mut flags = extra_c_flags(); + flags.push("-march=armv8.5-a+memtag"); + flags + }; + println!("{variant} test..."); + rustc() + .input(format!("foo_{variant}.rs")) + .target(target()) + .linker("aarch64-linux-gnu-gcc") + .run(); + cc().input(format!("bar_{variant}.c")) + .input(dynamic_lib_name("foo")) + .out_exe("test") + .args(&flags) + .run(); + run("test"); +} diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs new file mode 100644 index 00000000000..f00123f006b --- /dev/null +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -0,0 +1,89 @@ +#![feature(naked_functions, asm_const, linkage)] +#![crate_type = "dylib"] + +use std::arch::asm; + +pub trait TraitWithConst { + const COUNT: u32; +} + +struct Test; + +impl TraitWithConst for Test { + const COUNT: u32 = 1; +} + +#[no_mangle] +fn entry() { + private_vanilla(); + private_naked(); + + public_vanilla_generic::<Test>(); + public_naked_generic::<Test>(); +} + +extern "C" fn private_vanilla() -> u32 { + 42 +} + +#[naked] +extern "C" fn private_naked() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[no_mangle] +pub extern "C" fn public_vanilla() -> u32 { + 42 +} + +#[naked] +#[no_mangle] +pub extern "C" fn public_naked() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +pub extern "C" fn public_vanilla_generic<T: TraitWithConst>() -> u32 { + T::COUNT +} + +#[naked] +pub extern "C" fn public_naked_generic<T: TraitWithConst>() -> u32 { + unsafe { asm!("mov rax, {}", "ret", const T::COUNT, options(noreturn)) } +} + +#[linkage = "external"] +extern "C" fn vanilla_external_linkage() -> u32 { + 42 +} + +#[naked] +#[linkage = "external"] +extern "C" fn naked_external_linkage() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[cfg(not(windows))] +#[linkage = "weak"] +extern "C" fn vanilla_weak_linkage() -> u32 { + 42 +} + +#[naked] +#[cfg(not(windows))] +#[linkage = "weak"] +extern "C" fn naked_weak_linkage() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +// functions that are declared in an `extern "C"` block are currently not exported +// this maybe should change in the future, this is just tracking the current behavior +// reported in https://github.com/rust-lang/rust/issues/128071 +std::arch::global_asm! { + ".globl function_defined_in_global_asm", + "function_defined_in_global_asm:", + "ret", +} + +extern "C" { + pub fn function_defined_in_global_asm(); +} diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs new file mode 100644 index 00000000000..a32e62326eb --- /dev/null +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -0,0 +1,98 @@ +//@ ignore-windows +//@ only-x86_64 +use run_make_support::object::read::{File, Object, Symbol}; +use run_make_support::object::ObjectSymbol; +use run_make_support::targets::is_windows; +use run_make_support::{dynamic_lib_name, env_var, rfs, rustc}; + +fn main() { + let rdylib_name = dynamic_lib_name("a_rust_dylib"); + rustc().arg("-Zshare-generics=no").input("a_rust_dylib.rs").run(); + + let binary_data = rfs::read(&rdylib_name); + let rdylib = File::parse(&*binary_data).unwrap(); + + // naked should mirror vanilla + not_exported(&rdylib, "private_vanilla"); + not_exported(&rdylib, "private_naked"); + + global_function(&rdylib, "public_vanilla"); + global_function(&rdylib, "public_naked"); + + not_exported(&rdylib, "public_vanilla_generic"); + not_exported(&rdylib, "public_naked_generic"); + + global_function(&rdylib, "vanilla_external_linkage"); + global_function(&rdylib, "naked_external_linkage"); + + // FIXME: make this work on windows (gnu and msvc). See the PR + // https://github.com/rust-lang/rust/pull/128362 for some approaches + // that don't work + // + // #[linkage = "weak"] does not work well on windows, we get + // + // lib.def : error LNK2001: unresolved external symbol naked_weak_linkage␍ + // lib.def : error LNK2001: unresolved external symbol vanilla_weak_linkage + // + // so just skip weak symbols on windows (for now) + if !is_windows() { + weak_function(&rdylib, "vanilla_weak_linkage"); + weak_function(&rdylib, "naked_weak_linkage"); + } + + // functions that are declared in an `extern "C"` block are currently not exported + // this maybe should change in the future, this is just tracking the current behavior + // reported in https://github.com/rust-lang/rust/issues/128071 + not_exported(&rdylib, "function_defined_in_global_asm"); + + // share generics should expose the generic functions + rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run(); + let binary_data = rfs::read(&rdylib_name); + let rdylib = File::parse(&*binary_data).unwrap(); + + global_function(&rdylib, "public_vanilla_generic"); + global_function(&rdylib, "public_naked_generic"); +} + +#[track_caller] +fn global_function(file: &File, symbol_name: &str) { + let symbols = find_dynamic_symbol(file, symbol_name); + let [symbol] = symbols.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", symbols.len()) + }; + + assert!(symbol.is_definition(), "`{symbol_name}` is not a function"); + assert!(symbol.is_global(), "`{symbol_name}` is not marked as global"); +} + +#[track_caller] +fn weak_function(file: &File, symbol_name: &str) { + let symbols = find_dynamic_symbol(file, symbol_name); + let [symbol] = symbols.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", symbols.len()) + }; + + assert!(symbol.is_definition(), "`{symbol_name}` is not a function"); + assert!(symbol.is_weak(), "`{symbol_name}` is not marked as weak"); +} + +#[track_caller] +fn not_exported(file: &File, symbol_name: &str) { + assert_eq!(find_dynamic_symbol(file, symbol_name).len(), 0) +} + +fn find_subsequence(haystack: &[u8], needle: &[u8]) -> bool { + haystack.windows(needle.len()).any(|window| window == needle) +} + +fn find_dynamic_symbol<'file, 'data>( + file: &'file File<'data>, + expected: &str, +) -> Vec<Symbol<'data, 'file>> { + file.exports() + .unwrap() + .into_iter() + .filter(|e| find_subsequence(e.name(), expected.as_bytes())) + .filter_map(|e| file.symbol_by_name_bytes(e.name())) + .collect() +} diff --git a/tests/run-make/no-duplicate-libs/main.rs b/tests/run-make/no-duplicate-libs/main.rs index b25ef35ada6..d8d5d58bc47 100644 --- a/tests/run-make/no-duplicate-libs/main.rs +++ b/tests/run-make/no-duplicate-libs/main.rs @@ -1,6 +1,6 @@ -#[link(name = "foo")] // linker should drop this library, no symbols used -#[link(name = "bar")] // symbol comes from this library -#[link(name = "foo")] // now linker picks up `foo` b/c `bar` library needs it +#[link(name = "foo", kind = "static")] // linker should drop this library, no symbols used +#[link(name = "bar", kind = "static")] // symbol comes from this library +#[link(name = "foo", kind = "static")] // now linker picks up `foo` b/c `bar` library needs it extern "C" { fn bar(); } diff --git a/tests/run-make/no-duplicate-libs/rmake.rs b/tests/run-make/no-duplicate-libs/rmake.rs index 469348e266c..b67067909b2 100644 --- a/tests/run-make/no-duplicate-libs/rmake.rs +++ b/tests/run-make/no-duplicate-libs/rmake.rs @@ -9,9 +9,6 @@ //@ ignore-cross-compile // Reason: the compiled binary is executed -//@ ignore-msvc -// Reason: native compilation results in an unresolved external symbol - use run_make_support::{build_native_static_lib, run, rustc}; fn main() { diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile b/tests/run-make/pdb-buildinfo-cl-cmd/Makefile deleted file mode 100644 index a7be301a5b0..00000000000 --- a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -include ../tools.mk - -# only-windows-msvc - -# tests if the pdb contains the following information in the LF_BUILDINFO: -# 1. the commandline args to compile it (cmd) -# 2. full path to the compiler (cl) - -# we just do a stringsearch on the pdb, as these need to show up at least once, as the LF_BUILDINFO is created for each cgu -# actual parsing would be better, but this is a simple and good enough solution for now - -all: - $(RUSTC_ORIGINAL) main.rs -g --crate-name my_crate_name --crate-type bin -C metadata=dc9ef878b0a48666 --out-dir $(TMPDIR) - cat '$(TMPDIR)/my_crate_name.pdb' | grep -F '$(RUSTC_ORIGINAL)' -# using a file containing the string so I don't have problems with escaping quotes and spaces - cat '$(TMPDIR)/my_crate_name.pdb' | grep -f 'stringlist.txt' diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs new file mode 100644 index 00000000000..2ab9057b24c --- /dev/null +++ b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs @@ -0,0 +1,39 @@ +// Check if the pdb file contains the following information in the LF_BUILDINFO: +// 1. full path to the compiler (cl) +// 2. the commandline args to compile it (cmd) +// This is because these used to be missing in #96475. +// See https://github.com/rust-lang/rust/pull/113492 + +//@ only-windows-msvc +// Reason: pdb files are unique to this architecture + +use run_make_support::{assert_contains, bstr, env_var, rfs, rustc}; + +fn main() { + rustc() + .input("main.rs") + .arg("-g") + .crate_name("my_crate_name") + .crate_type("bin") + .metadata("dc9ef878b0a48666") + .run(); + let tests = [ + &env_var("RUSTC"), + r#""main.rs""#, + r#""-g""#, + r#""--crate-name""#, + r#""my_crate_name""#, + r#""--crate-type""#, + r#""bin""#, + r#""-Cmetadata=dc9ef878b0a48666""#, + ]; + for test in tests { + assert_pdb_contains(test); + } +} + +fn assert_pdb_contains(needle: &str) { + let needle = needle.as_bytes(); + use bstr::ByteSlice; + assert!(&rfs::read("my_crate_name.pdb").find(needle).is_some()); +} diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt b/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt deleted file mode 100644 index 634e9f19e89..00000000000 --- a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt +++ /dev/null @@ -1 +0,0 @@ -"main.rs" "-g" "--crate-name" "my_crate_name" "--crate-type" "bin" "-C" "metadata=dc9ef878b0a48666" "--out-dir" \ No newline at end of file diff --git a/tests/run-make/pgo-gen-lto/Makefile b/tests/run-make/pgo-gen-lto/Makefile deleted file mode 100644 index 54164c99522..00000000000 --- a/tests/run-make/pgo-gen-lto/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# needs-profiler-support -# ignore-cross-compile - -include ../tools.mk - -COMPILE_FLAGS=-Copt-level=3 -Clto=fat -Cprofile-generate="$(TMPDIR)" - -all: - $(RUSTC) $(COMPILE_FLAGS) test.rs - $(call RUN,test) || exit 1 - [ -e "$(TMPDIR)"/default_*.profraw ] || (echo "No .profraw file"; exit 1) diff --git a/tests/run-make/pgo-gen-lto/rmake.rs b/tests/run-make/pgo-gen-lto/rmake.rs new file mode 100644 index 00000000000..53d1623bf58 --- /dev/null +++ b/tests/run-make/pgo-gen-lto/rmake.rs @@ -0,0 +1,22 @@ +// A simple smoke test: when rustc compiles with profiling enabled, a profraw file +// should be generated. +// See https://github.com/rust-lang/rust/pull/48346 + +//@ needs-profiler-support +// Reason: this exercises LTO profiling +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cwd, has_extension, has_prefix, run, rustc, shallow_find_files}; + +fn main() { + rustc().opt_level("3").arg("-Clto=fat").profile_generate(cwd()).input("test.rs").run(); + run("test"); + assert_eq!( + shallow_find_files(cwd(), |path| { + has_prefix(path, "default_") && has_extension(path, "profraw") + }) + .len(), + 1 + ); +} diff --git a/tests/run-make/pgo-indirect-call-promotion/Makefile b/tests/run-make/pgo-indirect-call-promotion/Makefile deleted file mode 100644 index 8d1e69c4aba..00000000000 --- a/tests/run-make/pgo-indirect-call-promotion/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# needs-profiler-support -# ignore-cross-compile - -include ../tools.mk - -all: - # We don't compile `opaque` with either optimizations or instrumentation. - # We don't compile `opaque` with either optimizations or instrumentation. - $(RUSTC) $(COMMON_FLAGS) opaque.rs - # Compile the test program with instrumentation - mkdir -p "$(TMPDIR)"/prof_data_dir - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O -Ccodegen-units=1 - $(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O - # The argument below generates to the expected branch weights - $(call RUN,main) || exit 1 - "$(LLVM_BIN_DIR)"/llvm-profdata merge \ - -o "$(TMPDIR)"/prof_data_dir/merged.profdata \ - "$(TMPDIR)"/prof_data_dir - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-use="$(TMPDIR)"/prof_data_dir/merged.profdata -O \ - -Ccodegen-units=1 --emit=llvm-ir - cat "$(TMPDIR)"/interesting.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt diff --git a/tests/run-make/pgo-indirect-call-promotion/rmake.rs b/tests/run-make/pgo-indirect-call-promotion/rmake.rs new file mode 100644 index 00000000000..d0ccfd8a4d7 --- /dev/null +++ b/tests/run-make/pgo-indirect-call-promotion/rmake.rs @@ -0,0 +1,33 @@ +// This test checks that indirect call promotion is performed. The test +// programs calls the same function a thousand times through a function pointer. +// Only PGO data provides the information that it actually always is the same +// function. We verify that the indirect call promotion pass inserts a check +// whether it can make a direct call instead of the indirect call. +// See https://github.com/rust-lang/rust/pull/66631 + +//@ needs-profiler-support +// Reason: llvm_profdata is used +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{llvm_filecheck, llvm_profdata, rfs, run, rustc}; + +fn main() { + // We don't compile `opaque` with either optimizations or instrumentation. + rustc().input("opaque.rs").run(); + // Compile the test program with instrumentation + rfs::create_dir("prof_data_dir"); + rustc().input("interesting.rs").profile_generate("prof_data_dir").opt().codegen_units(1).run(); + rustc().input("main.rs").profile_generate("prof_data_dir").opt().run(); + // The argument below generates to the expected branch weights + run("main"); + llvm_profdata().merge().output("prof_data_dir/merged.profdata").input("prof_data_dir").run(); + rustc() + .input("interesting.rs") + .profile_use("prof_data_dir/merged.profdata") + .opt() + .codegen_units(1) + .emit("llvm-ir") + .run(); + llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run(); +} diff --git a/tests/run-make/raw-dylib-alt-calling-convention/Makefile b/tests/run-make/raw-dylib-alt-calling-convention/Makefile deleted file mode 100644 index 14d23a5d201..00000000000 --- a/tests/run-make/raw-dylib-alt-calling-convention/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions. - -# only-x86 -# only-windows - -include ../tools.mk - -all: - $(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs - $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" - $(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c) -ifdef IS_MSVC - $(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll -noimplib -else - $(CC) "$(TMPDIR)"/extern.obj -shared -o "$(TMPDIR)"/extern.dll -endif - - "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt - $(RUSTC_TEST_OP) "$(TMPDIR)"/output.txt output.txt - -ifdef IS_MSVC - "$(TMPDIR)"/driver true > "$(TMPDIR)"/output.msvc.txt - $(RUSTC_TEST_OP) "$(TMPDIR)"/output.msvc.txt output.msvc.txt -endif diff --git a/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs b/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs new file mode 100644 index 00000000000..1a1622f2754 --- /dev/null +++ b/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs @@ -0,0 +1,32 @@ +// `raw-dylib` is a Windows-specific attribute which emits idata sections for the items in the +// attached extern block, +// so they may be linked against without linking against an import library. +// To learn more, read https://github.com/rust-lang/rfcs/blob/master/text/2627-raw-dylib-kind.md +// This test uses this feature alongside alternative calling conventions, checking that both +// features are compatible and result in the expected output upon execution of the binary. +// See https://github.com/rust-lang/rust/pull/84171 + +//@ only-x86 +//@ only-windows + +use run_make_support::{build_native_dynamic_lib, diff, is_msvc, run, run_with_args, rustc}; + +fn main() { + rustc() + .crate_type("lib") + .crate_name("raw_dylib_alt_calling_convention_test") + .input("lib.rs") + .run(); + rustc().crate_type("bin").input("driver.rs").run(); + build_native_dynamic_lib("extern"); + let out = run("driver").stdout_utf8(); + diff().expected_file("output.txt").actual_text("actual", out).normalize(r#"\r"#, "").run(); + if is_msvc() { + let out_msvc = run_with_args("driver", &["true"]).stdout_utf8(); + diff() + .expected_file("output.msvc.txt") + .actual_text("actual", out_msvc) + .normalize(r#"\r"#, "") + .run(); + } +} diff --git a/tests/run-make/raw-dylib-c/Makefile b/tests/run-make/raw-dylib-c/Makefile deleted file mode 100644 index af5c4a6edd7..00000000000 --- a/tests/run-make/raw-dylib-c/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc - -# only-windows - -include ../tools.mk - -all: - $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs - $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" - $(RUSTC) --crate-type bin --crate-name raw_dylib_test_bin lib.rs - $(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c) - $(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c) -ifdef IS_MSVC - $(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll -noimplib - $(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll -noimplib -else - $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll - $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll -endif - "$(TMPDIR)"/driver | tr -d '\r' > "$(TMPDIR)"/output.txt - "$(TMPDIR)"/raw_dylib_test_bin > "$(TMPDIR)"/output_bin.txt - -ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/output.txt output.txt -else - $(DIFF) output.txt "$(TMPDIR)"/output.txt - $(DIFF) output.txt "$(TMPDIR)"/output_bin.txt -endif diff --git a/tests/run-make/raw-dylib-c/rmake.rs b/tests/run-make/raw-dylib-c/rmake.rs new file mode 100644 index 00000000000..3cfd8cb400b --- /dev/null +++ b/tests/run-make/raw-dylib-c/rmake.rs @@ -0,0 +1,29 @@ +// `raw-dylib` is a Windows-specific attribute which emits idata sections for the items in the +// attached extern block, +// so they may be linked against without linking against an import library. +// To learn more, read https://github.com/rust-lang/rfcs/blob/master/text/2627-raw-dylib-kind.md +// This test is the simplest of the raw-dylib tests, simply smoke-testing that the feature +// can be used to build an executable binary with an expected output with native C files +// compiling into dynamic libraries. +// See https://github.com/rust-lang/rust/pull/86419 + +//@ only-windows + +use run_make_support::{build_native_dynamic_lib, diff, run, rustc}; + +fn main() { + rustc().crate_type("lib").crate_name("raw_dylib_test").input("lib.rs").run(); + rustc().crate_type("bin").input("driver.rs").run(); + rustc().crate_type("bin").crate_name("raw_dylib_test_bin").input("lib.rs").run(); + build_native_dynamic_lib("extern_1"); + build_native_dynamic_lib("extern_2"); + let out_driver = run("driver").stdout_utf8(); + let out_raw = run("raw_dylib_test_bin").stdout_utf8(); + + diff() + .expected_file("output.txt") + .actual_text("actual", out_driver) + .normalize(r#"\r"#, "") + .run(); + diff().expected_file("output.txt").actual_text("actual", out_raw).normalize(r#"\r"#, "").run(); +} diff --git a/tests/run-make/redundant-libs/Makefile b/tests/run-make/redundant-libs/Makefile deleted file mode 100644 index 0a48b2b2801..00000000000 --- a/tests/run-make/redundant-libs/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows-msvc - -# rustc will remove one of the two redundant references to foo below. Depending -# on which one gets removed, we'll get a linker error on SOME platforms (like -# Linux). On these platforms, when a library is referenced, the linker will -# only pull in the symbols needed _at that point in time_. If a later library -# depends on additional symbols from the library, they will not have been pulled -# in, and you'll get undefined symbols errors. -# -# So in this example, we need to ensure that rustc keeps the _later_ reference -# to foo, and not the former one. -RUSTC_FLAGS = \ - -l static=bar \ - -l foo \ - -l static=baz \ - -l foo \ - --print link-args - -all: $(call DYLIB,foo) $(call STATICLIB,bar) $(call STATICLIB,baz) - $(RUSTC) $(RUSTC_FLAGS) main.rs - $(call RUN,main) diff --git a/tests/run-make/redundant-libs/foo.c b/tests/run-make/redundant-libs/foo.c index 339ee86c99e..551b85d1824 100644 --- a/tests/run-make/redundant-libs/foo.c +++ b/tests/run-make/redundant-libs/foo.c @@ -1,2 +1,8 @@ -void foo1() {} -void foo2() {} +#ifdef _MSC_VER +#define DllExport __declspec(dllexport) +#else +#define DllExport +#endif + +DllExport void foo1() {} +DllExport void foo2() {} diff --git a/tests/run-make/redundant-libs/rmake.rs b/tests/run-make/redundant-libs/rmake.rs new file mode 100644 index 00000000000..43bb30bde70 --- /dev/null +++ b/tests/run-make/redundant-libs/rmake.rs @@ -0,0 +1,28 @@ +// rustc will remove one of the two redundant references to foo below. Depending +// on which one gets removed, we'll get a linker error on SOME platforms (like +// Linux). On these platforms, when a library is referenced, the linker will +// only pull in the symbols needed _at that point in time_. If a later library +// depends on additional symbols from the library, they will not have been pulled +// in, and you'll get undefined symbols errors. +// +// So in this example, we need to ensure that rustc keeps the _later_ reference +// to foo, and not the former one. + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{ + build_native_dynamic_lib, build_native_static_lib, cwd, is_msvc, rfs, run, rustc, +}; + +fn main() { + build_native_dynamic_lib("foo"); + build_native_static_lib("bar"); + build_native_static_lib("baz"); + rustc() + .args(&["-lstatic=bar", "-lfoo", "-lstatic=baz", "-lfoo"]) + .input("main.rs") + .print("link-args") + .run(); + run("main"); +} diff --git a/tests/run-make/rust-lld-compress-debug-sections/main.rs b/tests/run-make/rust-lld-compress-debug-sections/main.rs new file mode 100644 index 00000000000..f328e4d9d04 --- /dev/null +++ b/tests/run-make/rust-lld-compress-debug-sections/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/rust-lld-compress-debug-sections/rmake.rs b/tests/run-make/rust-lld-compress-debug-sections/rmake.rs new file mode 100644 index 00000000000..df9691ccbcf --- /dev/null +++ b/tests/run-make/rust-lld-compress-debug-sections/rmake.rs @@ -0,0 +1,39 @@ +// Checks the `compress-debug-sections` option on rust-lld. + +//@ needs-rust-lld +//@ only-linux +//@ ignore-cross-compile + +// FIXME: This test isn't comprehensive and isn't covering all possible combinations. + +use run_make_support::{assert_contains, cmd, llvm_readobj, run_in_tmpdir, rustc}; + +fn check_compression(compression: &str, to_find: &str) { + run_in_tmpdir(|| { + let out = rustc() + .arg("-Zlinker-features=+lld") + .arg("-Clink-self-contained=+linker") + .arg("-Zunstable-options") + .arg("-Cdebuginfo=full") + .link_arg(&format!("-Wl,--compress-debug-sections={compression}")) + .input("main.rs") + .run_unchecked(); + let stderr = out.stderr_utf8(); + if stderr.is_empty() { + llvm_readobj().arg("-t").arg("main").run().assert_stdout_contains(to_find); + } else { + assert_contains( + stderr, + format!( + "LLVM was not built with LLVM_ENABLE_{to_find} \ + or did not find {compression} at build time" + ), + ); + } + }); +} + +fn main() { + check_compression("zlib", "ZLIB"); + check_compression("zstd", "ZSTD"); +} diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 87477c12230..d0bc19130d7 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -2,15 +2,17 @@ // see https://github.com/rust-lang/compiler-team/issues/510 for more info //@ needs-rust-lld -//@ ignore-msvc //@ ignore-s390x lld does not yet support s390x as target use std::process::Output; use run_make_support::regex::Regex; -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { + // lld-link is used if msvc, otherwise a gnu-compatible lld is used. + let linker_version_flag = if is_msvc() { "--version" } else { "-Wl,-v" }; + // Opt-in to lld and the self-contained linker, to link with rust-lld. We'll check that by // asking the linker to display its version number with a link-arg. let output = rustc() @@ -18,7 +20,7 @@ fn main() { .arg("-Zlinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .input("main.rs") .run(); assert!( @@ -27,10 +29,10 @@ fn main() { output.stderr_utf8() ); - // It should not be used when we explictly opt-out of lld. + // It should not be used when we explicitly opt-out of lld. let output = rustc() .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .arg("-Zlinker-features=-lld") .input("main.rs") .run(); @@ -44,7 +46,7 @@ fn main() { // times to rustc. let output = rustc() .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .arg("-Zlinker-features=-lld") diff --git a/tests/run-make/simd-ffi/Makefile b/tests/run-make/simd-ffi/Makefile deleted file mode 100644 index 29735347041..00000000000 --- a/tests/run-make/simd-ffi/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -include ../tools.mk - -TARGETS = -ifeq ($(filter arm,$(LLVM_COMPONENTS)),arm) -# construct a fairly exhaustive list of platforms that we -# support. These ones don't follow a pattern -TARGETS += arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi -endif - -ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86) -X86_ARCHS = i686 x86_64 -else -X86_ARCHS = -endif - -# these ones do, each OS lists the architectures it supports -LINUX=$(filter aarch64 mips,$(LLVM_COMPONENTS)) $(X86_ARCHS) -ifeq ($(filter mips,$(LLVM_COMPONENTS)),mips) -LINUX += mipsel -endif - -WINDOWS=$(X86_ARCHS) -# fails with: failed to get iphonesimulator SDK path: no such file or directory -#IOS=i386 aarch64 armv7 -DARWIN=$(X86_ARCHS) - -$(foreach arch,$(LINUX),$(eval TARGETS += $(arch)-unknown-linux-gnu)) -$(foreach arch,$(WINDOWS),$(eval TARGETS += $(arch)-pc-windows-gnu)) -#$(foreach arch,$(IOS),$(eval TARGETS += $(arch)-apple-ios)) -$(foreach arch,$(DARWIN),$(eval TARGETS += $(arch)-apple-darwin)) - -all: $(TARGETS) - -define MK_TARGETS -# compile the rust file to the given target, but only to asm and IR -# form, to avoid having to have an appropriate linker. -# -# we need some features because the integer SIMD instructions are not -# enabled by-default for i686 and ARM; these features will be invalid -# on some platforms, but LLVM just prints a warning so that's fine for -# now. -$(1): simd.rs - $$(RUSTC) --target=$(1) --emit=llvm-ir,asm simd.rs \ - -C target-feature='+neon,+sse2' -C extra-filename=-$(1) -endef - -$(foreach targetxxx,$(TARGETS),$(eval $(call MK_TARGETS,$(targetxxx)))) diff --git a/tests/run-make/simd-ffi/rmake.rs b/tests/run-make/simd-ffi/rmake.rs new file mode 100644 index 00000000000..04990c8bdf4 --- /dev/null +++ b/tests/run-make/simd-ffi/rmake.rs @@ -0,0 +1,63 @@ +// Using SIMD types in a program with foreign-function interfaces used to result in an ICE +// (internal compiler error). Since this was fixed in #21233, it should be checked that +// compilation of SIMD and FFI together should be successful on all the most common +// architectures. +// Note that this test does not check linking or binary execution. +// See https://github.com/rust-lang/rust/pull/21233 + +use run_make_support::{llvm_components_contain, rustc}; + +fn main() { + let mut targets = Vec::new(); + // arm-specific targets. + if llvm_components_contain("arm") { + targets.append(&mut vec![ + "arm-linux-androideabi".to_owned(), + "arm-unknown-linux-gnueabihf".to_owned(), + "arm-unknown-linux-gnueabi".to_owned(), + ]); + } + let mut x86_archs = Vec::new(); + if llvm_components_contain("x86") { + x86_archs.append(&mut vec!["i686", "x86_64"]); + } + // Linux has all x86 targets, plus aarch64 and mips. + let mut extra_targets = x86_archs.clone(); + if llvm_components_contain("aarch64") { + extra_targets.push("aarch64"); + } + if llvm_components_contain("mips") { + extra_targets.append(&mut vec!["mips", "mipsel"]); + } + + for target in extra_targets { + let linux = format!("{target}-unknown-linux-gnu"); + targets.push(linux); + } + + // Windows and Darwin (OSX) only receive x86 targets. + let extra_targets = x86_archs.clone(); + for target in extra_targets { + let windows = format!("{target}-pc-windows-gnu"); + let darwin = format!("{target}-apple-darwin"); + targets.push(windows); + targets.push(darwin); + } + + for target in targets { + // compile the rust file to the given target, but only to asm and IR + // form, to avoid having to have an appropriate linker. + // + // we need some features because the integer SIMD instructions are not + // enabled by-default for i686 and ARM; these features will be invalid + // on some platforms, but LLVM just prints a warning so that's fine for + // now. + rustc() + .target(&target) + .emit("llvm-ir,asm") + .input("simd.rs") + .arg("-Ctarget-feature=+neon,+sse") + .arg(&format!("-Cextra-filename=-{target}")) + .run(); + } +} diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile deleted file mode 100644 index a1e86a7ce4b..00000000000 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -include ../tools.mk - -# ignore-cross-compile -# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain -# ignore-wasm wasm doesn't support dynamic libraries - -all: - $(RUSTC) -C prefer-dynamic bar.rs - $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs \ - -Z staticlib-allow-rdylib-deps 2>&1 | grep 'note: native-static-libs: ' \ - | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt - cat $(TMPDIR)/libs.txt - -ifdef IS_MSVC - $(CC) $(CFLAGS) /c foo.c /Fo:$(TMPDIR)/foo.o - $(RUSTC_LINKER) $(TMPDIR)/foo.o $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo) -else - $(CC) $(CFLAGS) foo.c -L $(TMPDIR) -lfoo $$(cat $(TMPDIR)/libs.txt) -o $(call RUN_BINFILE,foo) -endif - - $(call RUN,foo) diff --git a/tests/run-make/staticlib-dylib-linkage/rmake.rs b/tests/run-make/staticlib-dylib-linkage/rmake.rs new file mode 100644 index 00000000000..8dd1ac0ffbd --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/rmake.rs @@ -0,0 +1,36 @@ +// A basic smoke test to check that rustc supports linking to a rust dylib with +// --crate-type staticlib. bar is a dylib, on which foo is dependent - the native +// static lib search paths are collected and used to compile foo.c, the final executable +// which depends on both foo and bar. +// See https://github.com/rust-lang/rust/pull/106560 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed. +//@ ignore-wasm +// Reason: WASM does not support dynamic libraries + +use run_make_support::{cc, is_msvc, regex, run, rustc, static_lib_name}; + +fn main() { + rustc().arg("-Cprefer-dynamic").input("bar.rs").run(); + let libs = rustc() + .input("foo.rs") + .crate_type("staticlib") + .print("native-static-libs") + .arg("-Zstaticlib-allow-rdylib-deps") + .run() + .assert_stderr_contains("note: native-static-libs: ") + .stderr_utf8(); + let re = regex::Regex::new(r#"note: native-static-libs:\s*(.+)"#).unwrap(); + let libs = re.find(&libs).unwrap().as_str().trim(); + // remove the note + let (_, native_link_args) = libs.split_once("note: native-static-libs: ").unwrap(); + // divide the command-line arguments in a vec + let mut native_link_args = native_link_args.split(' ').collect::<Vec<&str>>(); + if is_msvc() { + // For MSVC pass the arguments on to the linker. + native_link_args.insert(0, "-link"); + } + cc().input("foo.c").input(static_lib_name("foo")).args(native_link_args).out_exe("foo").run(); + run("foo"); +} diff --git a/tests/run-make/thumb-none-cortex-m/Makefile b/tests/run-make/thumb-none-cortex-m/Makefile deleted file mode 100644 index e941fc4a78e..00000000000 --- a/tests/run-make/thumb-none-cortex-m/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -include ../tools.mk - -# How to run this -# $ ./x.py clean -# $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make - -# Supported targets: -# - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1) -# - thumbv7em-none-eabi (Bare Cortex-M4, M7) -# - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat) -# - thumbv7m-none-eabi (Bare Cortex-M3) - -# only-thumb - -# For cargo setting -RUSTC := $(RUSTC_ORIGINAL) -LD_LIBRARY_PATH := $(HOST_RPATH_DIR) -# We need to be outside of 'src' dir in order to run cargo -WORK_DIR := $(TMPDIR) - -HERE := $(shell pwd) - -CRATE := cortex-m -CRATE_URL := https://github.com/rust-embedded/cortex-m -CRATE_SHA1 := a448e9156e2cb1e556e5441fd65426952ef4b927 # 0.5.0 - -# Don't make lints fatal, but they need to at least warn or they break Cargo's target info parsing. -export RUSTFLAGS := --cap-lints=warn - -all: - env - mkdir -p $(WORK_DIR) - -cd $(WORK_DIR) && rm -rf $(CRATE) - cd $(WORK_DIR) && bash -x $(HERE)/../git_clone_sha1.sh $(CRATE) $(CRATE_URL) $(CRATE_SHA1) - # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. - # These come from the top-level Rust workspace, that this crate is not a - # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. - cd $(WORK_DIR) && cd $(CRATE) && env RUSTC_BOOTSTRAP=1 $(BOOTSTRAP_CARGO) build --target $(TARGET) -v diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs new file mode 100644 index 00000000000..0ddb91d378f --- /dev/null +++ b/tests/run-make/thumb-none-cortex-m/rmake.rs @@ -0,0 +1,59 @@ +//! Test building of the `cortex-m` crate, a foundational crate in the embedded ecosystem +//! for a collection of thumb targets. This is a smoke test that verifies that both cargo +//! and rustc work in this case. +//! +//! How to run this +//! $ ./x.py clean +//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make +//! +//! Supported targets: +//! - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1) +//! - thumbv7em-none-eabi (Bare Cortex-M4, M7) +//! - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat) +//! - thumbv7m-none-eabi (Bare Cortex-M3) + +//@ only-thumb + +use std::path::PathBuf; + +use run_make_support::rfs::create_dir; +use run_make_support::{cmd, env_var, target}; + +const CRATE: &str = "cortex-m"; +const CRATE_URL: &str = "https://github.com/rust-embedded/cortex-m"; +const CRATE_SHA1: &str = "a448e9156e2cb1e556e5441fd65426952ef4b927"; // v0.5.0 + +fn main() { + // FIXME: requires an internet connection https://github.com/rust-lang/rust/issues/128733 + // See below link for git usage: + // https://stackoverflow.com/questions/3489173#14091182 + cmd("git").args(["clone", CRATE_URL, CRATE]).run(); + std::env::set_current_dir(CRATE).unwrap(); + cmd("git").args(["reset", "--hard", CRATE_SHA1]).run(); + + let target_dir = PathBuf::from("target"); + let manifest_path = PathBuf::from("Cargo.toml"); + + let path = env_var("PATH"); + let rustc = env_var("RUSTC"); + let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); + // FIXME: extract bootstrap cargo invocations to a proper command + // https://github.com/rust-lang/rust/issues/128734 + let mut cmd = cmd(bootstrap_cargo); + cmd.args(&[ + "build", + "--manifest-path", + manifest_path.to_str().unwrap(), + "-Zbuild-std=core", + "--target", + &target(), + ]) + .env("PATH", path) + .env("RUSTC", rustc) + .env("CARGO_TARGET_DIR", &target_dir) + // Don't make lints fatal, but they need to at least warn + // or they break Cargo's target info parsing. + .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes --cap-lints=warn"); + + cmd.run(); +} diff --git a/tests/run-make/thumb-none-qemu/Makefile b/tests/run-make/thumb-none-qemu/Makefile deleted file mode 100644 index eea6ca34992..00000000000 --- a/tests/run-make/thumb-none-qemu/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -include ../tools.mk - -# only-thumb - -# How to run this -# $ ./x.py clean -# $ ./x.py test --target thumbv7m-none-eabi tests/run-make - -# For cargo setting -export RUSTC := $(RUSTC_ORIGINAL) -export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) -# We need to be outside of 'src' dir in order to run cargo -export WORK_DIR := $(TMPDIR) -export HERE := $(shell pwd) - -## clean up unused env variables which might cause harm. -unexport RUSTC_LINKER -unexport RUSTC_BOOTSTRAP -unexport RUST_BUILD_STAGE -unexport RUST_TEST_THREADS -unexport RUST_TEST_TMPDIR -unexport AR -unexport CC -unexport CXX - -all: - bash script.sh diff --git a/tests/run-make/thumb-none-qemu/example/.cargo/config b/tests/run-make/thumb-none-qemu/example/.cargo/config.toml index 8b30310e7d4..7152e81b502 100644 --- a/tests/run-make/thumb-none-qemu/example/.cargo/config +++ b/tests/run-make/thumb-none-qemu/example/.cargo/config.toml @@ -1,6 +1,5 @@ [target.thumbv6m-none-eabi] -# FIXME: Should be Cortex-M0, but Qemu used by CI is too old -runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" +runner = "qemu-system-arm -cpu cortex-m0 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" [target.thumbv7m-none-eabi] runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" @@ -12,7 +11,7 @@ runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semiho runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" [target.thumbv8m.base-none-eabi] -# FIXME: Should be the Cortex-M23, bt Qemu does not currently support it +# FIXME: Should be the Cortex-M23, but Qemu does not currently support it runner = "qemu-system-arm -cpu cortex-m33 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" [target.thumbv8m.main-none-eabi] diff --git a/tests/run-make/thumb-none-qemu/rmake.rs b/tests/run-make/thumb-none-qemu/rmake.rs new file mode 100644 index 00000000000..d0f42bc8808 --- /dev/null +++ b/tests/run-make/thumb-none-qemu/rmake.rs @@ -0,0 +1,62 @@ +//! This test runs a basic application for thumb targets, using the cortex-m crate. +//! +//! These targets are very bare-metal: the first instruction the core runs on +//! power-on is already user code. The cortex-m-rt has to initialize the stack, .data, +//! .bss, enable the FPU if present, etc. +//! +//! This test builds and runs the applications for various thumb targets using qemu. +//! +//! How to run this +//! $ ./x.py clean +//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make +//! +//! For supported targets, see `example/.cargo/config.toml` +//! +//! FIXME: https://github.com/rust-lang/rust/issues/128733 this test uses external +//! dependencies, and needs an active internet connection +//! +//! FIXME: https://github.com/rust-lang/rust/issues/128734 extract bootstrap cargo +//! to a proper command + +//@ only-thumb + +use std::path::PathBuf; + +use run_make_support::{cmd, env_var, path_helpers, target}; + +const CRATE: &str = "example"; + +fn main() { + std::env::set_current_dir(CRATE).unwrap(); + + let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); + let path = env_var("PATH"); + let rustc = env_var("RUSTC"); + + let target_dir = path_helpers::path("target"); + let manifest_path = path_helpers::path("Cargo.toml"); + + let debug = { + let mut cmd = cmd(&bootstrap_cargo); + cmd.args(&["run", "--target", &target()]) + .env("RUSTFLAGS", "-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x") + .env("CARGO_TARGET_DIR", &target_dir) + .env("PATH", &path) + .env("RUSTC", &rustc); + cmd.run() + }; + + debug.assert_stdout_contains("x = 42"); + + let release = { + let mut cmd = cmd(&bootstrap_cargo); + cmd.args(&["run", "--release", "--target", &target()]) + .env("RUSTFLAGS", "-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x") + .env("CARGO_TARGET_DIR", &target_dir) + .env("PATH", &path) + .env("RUSTC", &rustc); + cmd.run() + }; + + release.assert_stdout_contains("x = 42"); +} diff --git a/tests/run-make/thumb-none-qemu/script.sh b/tests/run-make/thumb-none-qemu/script.sh deleted file mode 100644 index a8aa72af184..00000000000 --- a/tests/run-make/thumb-none-qemu/script.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -set -exuo pipefail - -CRATE=example - -env | sort -mkdir -p $WORK_DIR -pushd $WORK_DIR - rm -rf $CRATE || echo OK - cp -a $HERE/example . - pushd $CRATE - # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. - # These come from the top-level Rust workspace, that this crate is not a - # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. - env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ - $BOOTSTRAP_CARGO run --target $TARGET | grep "x = 42" - env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ - $BOOTSTRAP_CARGO run --target $TARGET --release | grep "x = 42" - popd -popd diff --git a/tests/run-make/zero-extend-abi-param-passing/param_passing.rs b/tests/run-make/zero-extend-abi-param-passing/param_passing.rs index c11f3cc72bd..addde6b8ee3 100644 --- a/tests/run-make/zero-extend-abi-param-passing/param_passing.rs +++ b/tests/run-make/zero-extend-abi-param-passing/param_passing.rs @@ -2,7 +2,7 @@ // LLVM optimization choices. See additional note below for an // example. -#[link(name = "bad")] +#[link(name = "bad", kind = "static")] extern "C" { pub fn c_read_value(a: u32, b: u32, c: u32) -> u16; } diff --git a/tests/run-make/zero-extend-abi-param-passing/rmake.rs b/tests/run-make/zero-extend-abi-param-passing/rmake.rs index aed27f7f5ab..96dbbd0627c 100644 --- a/tests/run-make/zero-extend-abi-param-passing/rmake.rs +++ b/tests/run-make/zero-extend-abi-param-passing/rmake.rs @@ -6,20 +6,13 @@ // while simultaneously interfacing with a C library and using the -O3 flag. // See https://github.com/rust-lang/rust/issues/97463 -//@ ignore-msvc -// Reason: the rustc compilation fails due to an unresolved external symbol - //@ ignore-cross-compile // Reason: The compiled binary is executed. - -use run_make_support::{cc, is_msvc, llvm_ar, run, rustc, static_lib_name}; +use run_make_support::{build_native_static_lib_optimized, run, rustc}; fn main() { - // The issue exercised by this test specifically needs needs `-O` - // flags (like `-O3`) to reproduce. Thus, we call `cc()` instead of - // the nicer `build_native_static_lib`. - cc().arg("-c").arg("-O3").out_exe("bad.o").input("bad.c").run(); - llvm_ar().obj_to_ar().output_input(static_lib_name("bad"), "bad.o").run(); - rustc().input("param_passing.rs").arg("-lbad").opt_level("3").run(); + // The issue exercised by this test specifically needs an optimized native static lib. + build_native_static_lib_optimized("bad"); + rustc().input("param_passing.rs").opt_level("3").run(); run("param_passing"); } diff --git a/tests/rustdoc-gui/search-result-impl-disambiguation.goml b/tests/rustdoc-gui/search-result-impl-disambiguation.goml index 3e49ac33025..bca52b46498 100644 --- a/tests/rustdoc-gui/search-result-impl-disambiguation.goml +++ b/tests/rustdoc-gui/search-result-impl-disambiguation.goml @@ -41,3 +41,24 @@ assert-document-property: ({ "URL": "struct.ZyxwvutMethodDisambiguation.html#method.method_impl_disambiguation-1" }, ENDS_WITH) assert: "section:target" + +// Checks that, if a type has two methods with the same name, +// and if it has multiple inherent impl blocks, that the numeric +// impl block's disambiguator is also acted upon. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->bool" +wait-for: "#search-tabs" +assert-count: ("a.result-method", 1) +assert-attribute: ("a.result-method", { + "href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockStruct/method.second_fn" +}) +click: "a.result-method" +wait-for: "details:has(summary > #impl-MultiImplBlockStruct-1) > div section[id='method.second_fn']:target" + +go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->u32" +wait-for: "#search-tabs" +assert-count: ("a.result-method", 1) +assert-attribute: ("a.result-method", { + "href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockTrait-for-MultiImplBlockStruct/method.second_fn" +}) +click: "a.result-method" +wait-for: "details:has(summary > #impl-MultiImplBlockTrait-for-MultiImplBlockStruct) > div section[id='method.second_fn-1']:target" diff --git a/tests/rustdoc-gui/src/lib2/another_mod/mod.rs b/tests/rustdoc-gui/src/lib2/another_mod/mod.rs index 9a4f007a2f0..77f83d29766 100644 --- a/tests/rustdoc-gui/src/lib2/another_mod/mod.rs +++ b/tests/rustdoc-gui/src/lib2/another_mod/mod.rs @@ -1 +1,19 @@ -pub fn tadam() {} +pub struct MultiImplBlockStruct; + +impl MultiImplBlockStruct { + pub fn first_fn() {} +} + +impl MultiImplBlockStruct { + pub fn second_fn(self) -> bool { true } +} + +pub trait MultiImplBlockTrait { + fn first_fn(); + fn second_fn(self) -> u32; +} + +impl MultiImplBlockTrait for MultiImplBlockStruct { + fn first_fn() {} + fn second_fn(self) -> u32 { 1 } +} diff --git a/tests/rustdoc-json/impls/pub_for_hidden_private.rs b/tests/rustdoc-json/impls/pub_for_hidden_private.rs new file mode 100644 index 00000000000..261ffbfeb4a --- /dev/null +++ b/tests/rustdoc-json/impls/pub_for_hidden_private.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --document-private-items --document-hidden-items + +pub trait TheTrait {} + +#[doc(hidden)] +struct Value {} + +//@ has '$.index[*][?(@.docs=="THE IMPL")]' +/// THE IMPL +impl TheTrait for Value {} diff --git a/tests/rustdoc-json/pub_mod_in_private_mod.rs b/tests/rustdoc-json/pub_mod_in_private_mod.rs new file mode 100644 index 00000000000..112ab9c68f0 --- /dev/null +++ b/tests/rustdoc-json/pub_mod_in_private_mod.rs @@ -0,0 +1,6 @@ +// See https://github.com/rust-lang/rust/issues/101105 + +//@ !has "$.index[*][?(@.name=='nucleus')]" +mod corpus { + pub mod nucleus {} +} diff --git a/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.rs b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.rs new file mode 100644 index 00000000000..571bc94e30f --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.rs @@ -0,0 +1,12 @@ +//@ compile-flags:--test +//@ check-pass +#![allow(rustdoc::invalid_codeblock_attributes)] + +// https://github.com/rust-lang/rust/pull/124577#issuecomment-2276034737 + +// Test that invalid langstrings don't get run. + +/// ```{rust,ignore} +/// panic!(); +/// ``` +pub struct Foo; diff --git a/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.stdout b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.stdout new file mode 100644 index 00000000000..e5c27bebbdb --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + diff --git a/tests/rustdoc-ui/intra-doc/.gitattributes b/tests/rustdoc-ui/intra-doc/.gitattributes index 6c125fac52f..0ac2e3c27f4 100644 --- a/tests/rustdoc-ui/intra-doc/.gitattributes +++ b/tests/rustdoc-ui/intra-doc/.gitattributes @@ -1 +1 @@ -warning-crlf.rs eol=crlf +warning-crlf.rs -text diff --git a/tests/rustdoc-ui/intra-doc/warning-crlf.rs b/tests/rustdoc-ui/intra-doc/warning-crlf.rs index ab096b860f8..9d3a4673c9d 100644 --- a/tests/rustdoc-ui/intra-doc/warning-crlf.rs +++ b/tests/rustdoc-ui/intra-doc/warning-crlf.rs @@ -1,26 +1,26 @@ -// ignore-tidy-cr -//@ check-pass - -// This file checks the spans of intra-link warnings in a file with CRLF line endings. The -// .gitattributes file in this directory should enforce it. - -/// [error] -pub struct A; -//~^^ WARNING `error` - -/// -/// docs [error1] -//~^ WARNING `error1` - -/// docs [error2] -/// -pub struct B; -//~^^^ WARNING `error2` - -/** - * This is a multi-line comment. - * - * It also has an [error]. - */ -pub struct C; -//~^^^ WARNING `error` +// ignore-tidy-cr +//@ check-pass + +// This file checks the spans of intra-link warnings in a file with CRLF line endings. The +// .gitattributes file in this directory should enforce it. + +/// [error] +pub struct A; +//~^^ WARNING `error` + +/// +/// docs [error1] +//~^ WARNING `error1` + +/// docs [error2] +/// +pub struct B; +//~^^^ WARNING `error2` + +/** + * This is a multi-line comment. + * + * It also has an [error]. + */ +pub struct C; +//~^^^ WARNING `error` diff --git a/tests/rustdoc-ui/remap-path-prefix-lint.rs b/tests/rustdoc-ui/remap-path-prefix-lint.rs new file mode 100644 index 00000000000..f27863e825d --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-lint.rs @@ -0,0 +1,10 @@ +// Regression test for remapped paths in rustdoc errors +// <https://github.com/rust-lang/rust/issues/69264>. + +//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ rustc-env:RUST_BACKTRACE=0 + +#![deny(rustdoc::invalid_html_tags)] + +/// </script> +pub struct Bar; diff --git a/tests/rustdoc-ui/remap-path-prefix-lint.stderr b/tests/rustdoc-ui/remap-path-prefix-lint.stderr new file mode 100644 index 00000000000..d7c1bb1965d --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-lint.stderr @@ -0,0 +1,14 @@ +error: unopened HTML tag `script` + --> remapped_path/remap-path-prefix-lint.rs:9:5 + | +LL | /// </script> + | ^^^^^^^^^ + | +note: the lint level is defined here + --> remapped_path/remap-path-prefix-lint.rs:7:9 + | +LL | #![deny(rustdoc::invalid_html_tags)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.rs b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.rs new file mode 100644 index 00000000000..f5f8fa51e37 --- /dev/null +++ b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.rs @@ -0,0 +1,15 @@ +trait Trait<Type> { + type Type; + + fn one(&self, val: impl Trait<Type: Default>); + //~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied + + fn two<T: Trait<Type: Default>>(&self) -> T; + //~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied + + fn three<T>(&self) -> T where + T: Trait<Type: Default>,; + //~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.stderr b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.stderr new file mode 100644 index 00000000000..06ab06003a1 --- /dev/null +++ b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.stderr @@ -0,0 +1,51 @@ +error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied + --> $DIR/name-same-as-generic-type-issue-128249.rs:4:30 + | +LL | fn one(&self, val: impl Trait<Type: Default>); + | ^^^^^ expected 1 generic argument + | +note: trait defined here, with 1 generic parameter: `Type` + --> $DIR/name-same-as-generic-type-issue-128249.rs:1:7 + | +LL | trait Trait<Type> { + | ^^^^^ ---- +help: add missing generic argument + | +LL | fn one(&self, val: impl Trait<Type, Type: Default>); + | +++++ + +error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied + --> $DIR/name-same-as-generic-type-issue-128249.rs:7:15 + | +LL | fn two<T: Trait<Type: Default>>(&self) -> T; + | ^^^^^ expected 1 generic argument + | +note: trait defined here, with 1 generic parameter: `Type` + --> $DIR/name-same-as-generic-type-issue-128249.rs:1:7 + | +LL | trait Trait<Type> { + | ^^^^^ ---- +help: add missing generic argument + | +LL | fn two<T: Trait<Type, Type: Default>>(&self) -> T; + | +++++ + +error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied + --> $DIR/name-same-as-generic-type-issue-128249.rs:11:12 + | +LL | T: Trait<Type: Default>,; + | ^^^^^ expected 1 generic argument + | +note: trait defined here, with 1 generic parameter: `Type` + --> $DIR/name-same-as-generic-type-issue-128249.rs:1:7 + | +LL | trait Trait<Type> { + | ^^^^^ ---- +help: add missing generic argument + | +LL | T: Trait<Type, Type: Default>,; + | +++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.rs b/tests/ui/async-await/async-closures/fn-exception-target-features.rs new file mode 100644 index 00000000000..de62fc8bf7e --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.rs @@ -0,0 +1,17 @@ +//@ edition: 2021 +//@ only-x86_64 + +#![feature(async_closure, target_feature_11)] +// `target_feature_11` just to test safe functions w/ target features. + +use std::pin::Pin; +use std::future::Future; + +#[target_feature(enable = "sse2")] +fn target_feature() -> Pin<Box<dyn Future<Output = ()> + 'static>> { todo!() } + +fn test(f: impl async Fn()) {} + +fn main() { + test(target_feature); //~ ERROR the trait bound +} diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr new file mode 100644 index 00000000000..396167f4070 --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception-target-features.rs:16:10 + | +LL | test(target_feature); + | ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception-target-features.rs:13:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/fn-exception.rs b/tests/ui/async-await/async-closures/fn-exception.rs new file mode 100644 index 00000000000..0e06ebf48a4 --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception.rs @@ -0,0 +1,21 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +use std::pin::Pin; +use std::future::Future; + +unsafe extern "Rust" { + pub unsafe fn unsafety() -> Pin<Box<dyn Future<Output = ()> + 'static>>; +} + +unsafe extern "C" { + pub safe fn abi() -> Pin<Box<dyn Future<Output = ()> + 'static>>; +} + +fn test(f: impl async Fn()) {} + +fn main() { + test(unsafety); //~ ERROR the trait bound + test(abi); //~ ERROR the trait bound +} diff --git a/tests/ui/async-await/async-closures/fn-exception.stderr b/tests/ui/async-await/async-closures/fn-exception.stderr new file mode 100644 index 00000000000..bacd079af0f --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception.rs:19:10 + | +LL | test(unsafety); + | ---- ^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {unsafety}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception.rs:16:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error[E0277]: the trait bound `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception.rs:20:10 + | +LL | test(abi); + | ---- ^^^ the trait `AsyncFn<()>` is not implemented for fn item `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception.rs:16:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/non-copy-arg-does-not-force-inner-move.rs b/tests/ui/async-await/async-closures/non-copy-arg-does-not-force-inner-move.rs new file mode 100644 index 00000000000..cd9d98d0799 --- /dev/null +++ b/tests/ui/async-await/async-closures/non-copy-arg-does-not-force-inner-move.rs @@ -0,0 +1,17 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ build-pass + +#![feature(async_closure)] + +extern crate block_on; + +fn wrapper(f: impl Fn(String)) -> impl async Fn(String) { + async move |s| f(s) +} + +fn main() { + block_on::block_on(async { + wrapper(|who| println!("Hello, {who}!"))(String::from("world")).await; + }); +} diff --git a/tests/ui/attributes/check-cfg_attr-ice.rs b/tests/ui/attributes/check-cfg_attr-ice.rs new file mode 100644 index 00000000000..5bf8baab141 --- /dev/null +++ b/tests/ui/attributes/check-cfg_attr-ice.rs @@ -0,0 +1,68 @@ +//! I missed a `cfg_attr` match in #128581, it should have had the same treatment as `cfg`. If +//! an invalid attribute starting with `cfg_attr` is passed, then it would trigger an ICE because +//! it was not considered "checked" (e.g. `#[cfg_attr::skip]` or `#[cfg_attr::no_such_thing]`). +//! +//! This test is not exhaustive, there's too many possible positions to check, instead it just does +//! a basic smoke test in a few select positions to make sure we don't ICE for e.g. +//! `#[cfg_attr::no_such_thing]`. +//! +//! issue: rust-lang/rust#128716 +#![crate_type = "lib"] + +#[cfg_attr::no_such_thing] +//~^ ERROR failed to resolve +mod we_are_no_strangers_to_love {} + +#[cfg_attr::no_such_thing] +//~^ ERROR failed to resolve +struct YouKnowTheRules { + #[cfg_attr::no_such_thing] + //~^ ERROR failed to resolve + and_so_do_i: u8, +} + +#[cfg_attr::no_such_thing] +//~^ ERROR failed to resolve +fn a_full_commitment() { + #[cfg_attr::no_such_thing] + //~^ ERROR failed to resolve + let is_what_i_am_thinking_of = (); +} + +#[cfg_attr::no_such_thing] +//~^ ERROR failed to resolve +union AnyOtherGuy { + owo: () +} +struct This; + +#[cfg_attr(FALSE, doc = "you wouldn't get this")] +impl From<AnyOtherGuy> for This { + #[cfg_attr::no_such_thing] + //~^ ERROR failed to resolve + fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This { + //~^ ERROR failed to resolve + #[cfg_attr::no_such_thing] + //~^ ERROR attributes on expressions are experimental + //~| ERROR failed to resolve + unreachable!() + } +} + +#[cfg_attr::no_such_thing] +//~^ ERROR failed to resolve +enum NeverGonna { + #[cfg_attr::no_such_thing] + //~^ ERROR failed to resolve + GiveYouUp(#[cfg_attr::no_such_thing] u8), + //~^ ERROR failed to resolve + LetYouDown { + #![cfg_attr::no_such_thing] + //~^ ERROR an inner attribute is not permitted in this context + never_gonna: (), + round_around: (), + #[cfg_attr::no_such_thing] + //~^ ERROR failed to resolve + and_desert_you: (), + }, +} diff --git a/tests/ui/attributes/check-cfg_attr-ice.stderr b/tests/ui/attributes/check-cfg_attr-ice.stderr new file mode 100644 index 00000000000..dbdf32597f7 --- /dev/null +++ b/tests/ui/attributes/check-cfg_attr-ice.stderr @@ -0,0 +1,101 @@ +error: an inner attribute is not permitted in this context + --> $DIR/check-cfg_attr-ice.rs:60:9 + | +LL | #![cfg_attr::no_such_thing] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + = note: outer attributes, like `#[test]`, annotate the item following them + +error[E0658]: attributes on expressions are experimental + --> $DIR/check-cfg_attr-ice.rs:45:9 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:52:3 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:55:7 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:57:17 + | +LL | GiveYouUp(#[cfg_attr::no_such_thing] u8), + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:64:11 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:41:7 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:43:15 + | +LL | fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This { + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:45:11 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:32:3 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:24:3 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:27:7 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:16:3 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:19:7 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` + --> $DIR/check-cfg_attr-ice.rs:12:3 + | +LL | #[cfg_attr::no_such_thing] + | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + +error: aborting due to 15 previous errors + +Some errors have detailed explanations: E0433, E0658. +For more information about an error, try `rustc --explain E0433`. diff --git a/tests/ui/attributes/no-sanitize.rs b/tests/ui/attributes/no-sanitize.rs new file mode 100644 index 00000000000..82b7a22d570 --- /dev/null +++ b/tests/ui/attributes/no-sanitize.rs @@ -0,0 +1,34 @@ +#![feature(no_sanitize)] +#![feature(stmt_expr_attributes)] +#![deny(unused_attributes)] +#![allow(dead_code)] + +fn invalid() { + #[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition + { + 1 + }; +} + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +type InvalidTy = (); + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +mod invalid_module {} + +fn main() { + let _ = #[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition + (|| 1); +} + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +struct F; + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +impl F { + #[no_sanitize(memory)] + fn valid(&self) {} +} + +#[no_sanitize(memory)] +fn valid() {} diff --git a/tests/ui/attributes/no-sanitize.stderr b/tests/ui/attributes/no-sanitize.stderr new file mode 100644 index 00000000000..f742ba0beed --- /dev/null +++ b/tests/ui/attributes/no-sanitize.stderr @@ -0,0 +1,55 @@ +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:7:5 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | / { +LL | | 1 +LL | | }; + | |_____- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:13:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | type InvalidTy = (); + | -------------------- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:16:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | mod invalid_module {} + | --------------------- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:20:13 + | +LL | let _ = #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | (|| 1); + | ------ not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:24:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | struct F; + | --------- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:27:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | / impl F { +LL | | #[no_sanitize(memory)] +LL | | fn valid(&self) {} +LL | | } + | |_- not a function definition + +error: aborting due to 6 previous errors + diff --git a/tests/ui/cfg/disallowed-cli-cfgs-allow.rs b/tests/ui/cfg/disallowed-cli-cfgs-allow.rs new file mode 100644 index 00000000000..0a9b07429d5 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs-allow.rs @@ -0,0 +1,4 @@ +//@ check-pass +//@ compile-flags: --cfg unix -Aexplicit_builtin_cfgs_in_flags + +fn main() {} diff --git a/tests/ui/cfg/disallowed-cli-cfgs.debug_assertions_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.debug_assertions_.stderr new file mode 100644 index 00000000000..ec3e15b85c7 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.debug_assertions_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg debug_assertions` flag + | + = note: config `debug_assertions` is only supposed to be controlled by `-C debug-assertions` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.overflow_checks_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.overflow_checks_.stderr new file mode 100644 index 00000000000..ba8ed3f2e65 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.overflow_checks_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg overflow_checks` flag + | + = note: config `overflow_checks` is only supposed to be controlled by `-C overflow-checks` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.panic_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.panic_.stderr new file mode 100644 index 00000000000..5b375694b13 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.panic_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg panic="abort"` flag + | + = note: config `panic` is only supposed to be controlled by `-C panic` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.proc_macro_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.proc_macro_.stderr new file mode 100644 index 00000000000..c2e29908857 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.proc_macro_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg proc_macro` flag + | + = note: config `proc_macro` is only supposed to be controlled by `--crate-type proc-macro` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.relocation_model_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.relocation_model_.stderr new file mode 100644 index 00000000000..65ada551cba --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.relocation_model_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg relocation_model="a"` flag + | + = note: config `relocation_model` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.rs b/tests/ui/cfg/disallowed-cli-cfgs.rs new file mode 100644 index 00000000000..714c01f4bc6 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.rs @@ -0,0 +1,35 @@ +//@ check-fail +//@ revisions: overflow_checks_ debug_assertions_ ub_checks_ sanitize_ +//@ revisions: sanitizer_cfi_generalize_pointers_ sanitizer_cfi_normalize_integers_ +//@ revisions: proc_macro_ panic_ target_feature_ unix_ windows_ target_abi_ +//@ revisions: target_arch_ target_endian_ target_env_ target_family_ target_os_ +//@ revisions: target_pointer_width_ target_vendor_ target_has_atomic_ +//@ revisions: target_has_atomic_equal_alignment_ target_has_atomic_load_store_ +//@ revisions: target_thread_local_ relocation_model_ + +//@ [overflow_checks_]compile-flags: --cfg overflow_checks +//@ [debug_assertions_]compile-flags: --cfg debug_assertions +//@ [ub_checks_]compile-flags: --cfg ub_checks +//@ [sanitize_]compile-flags: --cfg sanitize="cfi" +//@ [sanitizer_cfi_generalize_pointers_]compile-flags: --cfg sanitizer_cfi_generalize_pointers +//@ [sanitizer_cfi_normalize_integers_]compile-flags: --cfg sanitizer_cfi_normalize_integers +//@ [proc_macro_]compile-flags: --cfg proc_macro +//@ [panic_]compile-flags: --cfg panic="abort" +//@ [target_feature_]compile-flags: --cfg target_feature="sse3" +//@ [unix_]compile-flags: --cfg unix +//@ [windows_]compile-flags: --cfg windows +//@ [target_abi_]compile-flags: --cfg target_abi="gnu" +//@ [target_arch_]compile-flags: --cfg target_arch="arm" +//@ [target_endian_]compile-flags: --cfg target_endian="little" +//@ [target_env_]compile-flags: --cfg target_env +//@ [target_family_]compile-flags: --cfg target_family="unix" +//@ [target_os_]compile-flags: --cfg target_os="linux" +//@ [target_pointer_width_]compile-flags: --cfg target_pointer_width="32" +//@ [target_vendor_]compile-flags: --cfg target_vendor +//@ [target_has_atomic_]compile-flags: --cfg target_has_atomic="32" +//@ [target_has_atomic_equal_alignment_]compile-flags: --cfg target_has_atomic_equal_alignment="32" +//@ [target_has_atomic_load_store_]compile-flags: --cfg target_has_atomic_load_store="32" +//@ [target_thread_local_]compile-flags: --cfg target_thread_local +//@ [relocation_model_]compile-flags: --cfg relocation_model="a" + +fn main() {} diff --git a/tests/ui/cfg/disallowed-cli-cfgs.sanitize_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.sanitize_.stderr new file mode 100644 index 00000000000..b1f3b19dd86 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.sanitize_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg sanitize="cfi"` flag + | + = note: config `sanitize` is only supposed to be controlled by `-Z sanitizer` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_generalize_pointers_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_generalize_pointers_.stderr new file mode 100644 index 00000000000..49106f99b6b --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_generalize_pointers_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg sanitizer_cfi_generalize_pointers` flag + | + = note: config `sanitizer_cfi_generalize_pointers` is only supposed to be controlled by `-Z sanitizer=cfi` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_normalize_integers_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_normalize_integers_.stderr new file mode 100644 index 00000000000..f9bb9e76297 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_normalize_integers_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg sanitizer_cfi_normalize_integers` flag + | + = note: config `sanitizer_cfi_normalize_integers` is only supposed to be controlled by `-Z sanitizer=cfi` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_abi_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_abi_.stderr new file mode 100644 index 00000000000..f5c09110ce8 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_abi_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_abi="gnu"` flag + | + = note: config `target_abi` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_arch_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_arch_.stderr new file mode 100644 index 00000000000..7f616e75d4e --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_arch_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_arch="arm"` flag + | + = note: config `target_arch` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_endian_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_endian_.stderr new file mode 100644 index 00000000000..755f3708d67 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_endian_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_endian="little"` flag + | + = note: config `target_endian` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_env_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_env_.stderr new file mode 100644 index 00000000000..ccd027dbebe --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_env_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_env` flag + | + = note: config `target_env` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_family_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_family_.stderr new file mode 100644 index 00000000000..e376e4e16a1 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_family_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_family="unix"` flag + | + = note: config `target_family` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_feature_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_feature_.stderr new file mode 100644 index 00000000000..457cca6b885 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_feature_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_feature="sse3"` flag + | + = note: config `target_feature` is only supposed to be controlled by `-C target-feature` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_.stderr new file mode 100644 index 00000000000..160741198ad --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_atomic="32"` flag + | + = note: config `target_has_atomic` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_equal_alignment_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_equal_alignment_.stderr new file mode 100644 index 00000000000..096490a03f6 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_equal_alignment_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_atomic_equal_alignment="32"` flag + | + = note: config `target_has_atomic_equal_alignment` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_load_store_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_load_store_.stderr new file mode 100644 index 00000000000..5253987cb12 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_load_store_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_atomic_load_store="32"` flag + | + = note: config `target_has_atomic_load_store` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_os_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_os_.stderr new file mode 100644 index 00000000000..ba2c967bfa1 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_os_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_os="linux"` flag + | + = note: config `target_os` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_pointer_width_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_pointer_width_.stderr new file mode 100644 index 00000000000..983313bc517 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_pointer_width_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_pointer_width="32"` flag + | + = note: config `target_pointer_width` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_thread_local_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_thread_local_.stderr new file mode 100644 index 00000000000..4f66ea17ee4 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_thread_local_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_thread_local` flag + | + = note: config `target_thread_local` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_vendor_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_vendor_.stderr new file mode 100644 index 00000000000..e8f32598445 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_vendor_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_vendor` flag + | + = note: config `target_vendor` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr new file mode 100644 index 00000000000..96b5beb0210 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg test` flag + | + = note: config `test` is only supposed to be controlled by `--test` + = note: see <https://github.com/rust-lang/rust/issues/xxxxx> for more information + = note: `#[deny(unexpected_builtin_cfgs)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.ub_checks_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.ub_checks_.stderr new file mode 100644 index 00000000000..e61e5ac69d5 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.ub_checks_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg ub_checks` flag + | + = note: config `ub_checks` is only supposed to be controlled by `-Z ub-checks` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.unix_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.unix_.stderr new file mode 100644 index 00000000000..f3c6bb0ef1b --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.unix_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg unix` flag + | + = note: config `unix` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.windows_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.windows_.stderr new file mode 100644 index 00000000000..171a3f95bbd --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.windows_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg windows` flag + | + = note: config `windows` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs new file mode 100644 index 00000000000..311f507d3c7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs @@ -0,0 +1,28 @@ +// This ensures we don't ICE in situations like rust-lang/rust#126272. + +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(Debug, PartialEq, Eq, ConstParamTy)] +//~^ ERROR the trait `ConstParamTy_` +//~| ERROR the trait `ConstParamTy_` +struct Foo { + nested: &'static Bar<dyn std::fmt::Debug>, + //~^ ERROR the size for values + //~| ERROR the size for values + //~| ERROR binary operation `==` cannot + //~| ERROR the trait bound `dyn Debug: Eq` + //~| ERROR the size for values +} + +#[derive(Debug, PartialEq, Eq, ConstParamTy)] +struct Bar<T>(T); + +struct Test<const F: Foo>; + +fn main() { + let x: Test<{ Foo { nested: &Bar(4) } }> = Test; + //~^ ERROR the size for values +} diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr new file mode 100644 index 00000000000..1c30aa68e85 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr @@ -0,0 +1,160 @@ +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + | +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type + --> $DIR/unsizing-wfcheck-issue-126272.rs:8:32 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | ^^^^^^^^^^^^ +... +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ----------------------------------------- this field does not implement `ConstParamTy_` + | + = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type + --> $DIR/unsizing-wfcheck-issue-126272.rs:8:32 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | ^^^^^^^^^^^^ +... +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ----------------------------------------- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Eq` + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + | +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Sized` + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + | +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): UnsizedConstParamTy` + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + | +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | ----- in this derive macro expansion +... +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)`, which is required by `&&'static Bar<(dyn Debug + 'static)>: Debug` + = help: the trait `Debug` is implemented for `Bar<T>` +note: required for `Bar<(dyn Debug + 'static)>` to implement `Debug` + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:10 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | ^^^^^ +LL | struct Bar<T>(T); + | - unsatisfied trait bound introduced in this `derive` macro + = note: 2 redundant requirements hidden + = note: required for `&&'static Bar<(dyn Debug + 'static)>` to implement `Debug` + = note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug` + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0369]: binary operation `==` cannot be applied to type `&Bar<dyn Debug>` + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | --------- in this derive macro expansion +... +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `dyn Debug: Eq` is not satisfied + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | -- in this derive macro expansion +... +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `dyn Debug`, which is required by `&'static Bar<dyn Debug>: Eq` + | + = help: the trait `Eq` is implemented for `Bar<T>` +note: required for `Bar<dyn Debug>` to implement `Eq` + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:28 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | ^^ unsatisfied trait bound introduced in this `derive` macro + = note: 1 redundant requirement hidden + = note: required for `&'static Bar<dyn Debug>` to implement `Eq` +note: required by a bound in `AssertParamIsEq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time + --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + | +LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] + | -- in this derive macro expansion +... +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Debug` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/unsizing-wfcheck-issue-126272.rs:26:33 + | +LL | let x: Test<{ Foo { nested: &Bar(4) } }> = Test; + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0204, E0277, E0369. +For more information about an error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/generic_const_exprs/no_where_clause.rs b/tests/ui/const-generics/generic_const_exprs/no_where_clause.rs index 77cce66e4ec..53230469491 100644 --- a/tests/ui/const-generics/generic_const_exprs/no_where_clause.rs +++ b/tests/ui/const-generics/generic_const_exprs/no_where_clause.rs @@ -17,6 +17,7 @@ impl<const N: usize> Example<N> { a: [0.; N], b: [0.; complex_maths(N)], //~^ ERROR: unconstrained generic constant + //~| ERROR: unconstrained generic constant } } } diff --git a/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr b/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr index a2680eac039..88fc68a4d1b 100644 --- a/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr +++ b/tests/ui/const-generics/generic_const_exprs/no_where_clause.stderr @@ -10,6 +10,17 @@ LL | pub struct Example<const N: usize> where [(); complex_maths(N)]: { | +++++++++++++++++++++++++++++ error: unconstrained generic constant + --> $DIR/no_where_clause.rs:18:10 + | +LL | b: [0.; complex_maths(N)], + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try adding a `where` bound + | +LL | pub fn new() -> Self where [(); complex_maths(N)]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant --> $DIR/no_where_clause.rs:18:15 | LL | b: [0.; complex_maths(N)], @@ -20,5 +31,5 @@ help: try adding a `where` bound LL | pub fn new() -> Self where [(); complex_maths(N)]: { | +++++++++++++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/error-codes/E0283.stderr b/tests/ui/error-codes/E0283.stderr index fc08395a2b0..381eca5f2a4 100644 --- a/tests/ui/error-codes/E0283.stderr +++ b/tests/ui/error-codes/E0283.stderr @@ -7,10 +7,12 @@ LL | fn create() -> u32; LL | let cont: u32 = Coroutine::create(); | ^^^^^^^^^^^^^^^^^^^ cannot call associated function of trait | -help: use a fully-qualified path to a specific available implementation +help: use a fully-qualified path to one of the available implementations | -LL | let cont: u32 = </* self type */ as Coroutine>::create(); - | +++++++++++++++++++ + +LL | let cont: u32 = <Impl as Coroutine>::create(); + | ++++++++ + +LL | let cont: u32 = <AnotherImpl as Coroutine>::create(); + | +++++++++++++++ + error[E0283]: type annotations needed --> $DIR/E0283.rs:35:24 diff --git a/tests/ui/error-codes/E0790.stderr b/tests/ui/error-codes/E0790.stderr index 6338a10b6af..106554b2425 100644 --- a/tests/ui/error-codes/E0790.stderr +++ b/tests/ui/error-codes/E0790.stderr @@ -63,10 +63,12 @@ LL | fn my_fn(); LL | MyTrait2::my_fn(); | ^^^^^^^^^^^^^^^^^ cannot call associated function of trait | -help: use a fully-qualified path to a specific available implementation +help: use a fully-qualified path to one of the available implementations | -LL | </* self type */ as MyTrait2>::my_fn(); - | +++++++++++++++++++ + +LL | <Impl1 as MyTrait2>::my_fn(); + | +++++++++ + +LL | <Impl2 as MyTrait2>::my_fn(); + | +++++++++ + error: aborting due to 5 previous errors diff --git a/tests/ui/generic-associated-types/issue-71176.rs b/tests/ui/generic-associated-types/issue-71176.rs index e58b6f6091e..b33fda8e154 100644 --- a/tests/ui/generic-associated-types/issue-71176.rs +++ b/tests/ui/generic-associated-types/issue-71176.rs @@ -16,6 +16,6 @@ struct Holder<B> { fn main() { Holder { - inner: Box::new(()), + inner: Box::new(()), //~ ERROR: the trait `Provider` cannot be made into an object }; } diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index a1913bb618b..9f83c162c02 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -64,7 +64,23 @@ LL | type A<'a>; = help: consider moving `A` to another trait = help: only type `()` implements the trait, consider using it directly instead -error: aborting due to 4 previous errors +error[E0038]: the trait `Provider` cannot be made into an object + --> $DIR/issue-71176.rs:19:16 + | +LL | inner: Box::new(()), + | ^^^^^^^^^^^^ `Provider` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/issue-71176.rs:2:10 + | +LL | trait Provider { + | -------- this trait cannot be made into an object... +LL | type A<'a>; + | ^ ...because it contains the generic associated type `A` + = help: consider moving `A` to another trait + = help: only type `()` implements the trait, consider using it directly instead + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/impl-trait/in-trait/cannot-capture-intersection.rs b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.rs new file mode 100644 index 00000000000..d7b62436d2d --- /dev/null +++ b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.rs @@ -0,0 +1,29 @@ +#![feature(precise_capturing)] + +use std::future::Future; +use std::pin::Pin; + +trait MyTrait { + fn foo<'a, 'b>(&'a self, x: &'b i32) -> impl Future<Output = i32>; +} + +trait ErasedMyTrait { + fn foo<'life0, 'life1, 'dynosaur>(&'life0 self, x: &'life1 i32) + -> Pin<Box<dyn Future<Output = i32> + 'dynosaur>> + where + 'life0: 'dynosaur, + 'life1: 'dynosaur; +} + +struct DynMyTrait<T: ErasedMyTrait> { + ptr: T, +} + +impl<T: ErasedMyTrait> MyTrait for DynMyTrait<T> { + fn foo<'a, 'b>(&'a self, x: &'b i32) -> impl Future<Output = i32> { + self.ptr.foo(x) + //~^ ERROR hidden type for `impl Future<Output = i32>` captures lifetime that does not appear in bounds + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/cannot-capture-intersection.stderr b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.stderr new file mode 100644 index 00000000000..92ef66c5504 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.stderr @@ -0,0 +1,13 @@ +error[E0700]: hidden type for `impl Future<Output = i32>` captures lifetime that does not appear in bounds + --> $DIR/cannot-capture-intersection.rs:24:9 + | +LL | fn foo<'a, 'b>(&'a self, x: &'b i32) -> impl Future<Output = i32> { + | ------------------------- opaque type defined here +LL | self.ptr.foo(x) + | ^^^^^^^^^^^^^^^ + | + = note: hidden type `Pin<Box<dyn Future<Output = i32>>>` captures lifetime `'_` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index 4d4ba58c974..b7cee7d0b1f 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -2,17 +2,12 @@ error[E0700]: hidden type for `impl Trait<'d, 'e>` captures lifetime that does n --> $DIR/ordinary-bounds-unrelated.rs:28:33 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | -- ------------------ opaque type defined here - | | - | hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here + | ------------------ opaque type defined here ... LL | if condition() { a } else { b } | ^ | -help: to declare that `impl Trait<'d, 'e>` captures `'b`, you can add an explicit `'b` lifetime bound - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + 'b - | ++++ + = note: hidden type `Ordinary<'_>` captures lifetime `'_` error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index 060eaa7e64a..d1190da6c9f 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -2,17 +2,12 @@ error[E0700]: hidden type for `impl Trait<'a, 'b>` captures lifetime that does n --> $DIR/ordinary-bounds-unsuited.rs:31:33 | LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> - | -- ------------------ opaque type defined here - | | - | hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here + | ------------------ opaque type defined here ... LL | if condition() { a } else { b } | ^ | -help: to declare that `impl Trait<'a, 'b>` captures `'b`, you can add an explicit `'b` lifetime bound - | -LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + 'b - | ++++ + = note: hidden type `Ordinary<'_>` captures lifetime `'_` error: aborting due to 1 previous error diff --git a/tests/ui/inline-const/using-late-bound-from-closure.rs b/tests/ui/inline-const/using-late-bound-from-closure.rs new file mode 100644 index 00000000000..2b12b2e26a2 --- /dev/null +++ b/tests/ui/inline-const/using-late-bound-from-closure.rs @@ -0,0 +1,16 @@ +// Test for ICE: cannot convert ReLateParam to a region vid +// https://github.com/rust-lang/rust/issues/125873 + +#![feature(closure_lifetime_binder)] +fn foo() { + let a = for<'a> |b: &'a ()| -> &'a () { + const { + let awd = (); + let _: &'a () = &awd; + //~^ `awd` does not live long enough + }; + b + }; +} + +fn main() {} diff --git a/tests/ui/inline-const/using-late-bound-from-closure.stderr b/tests/ui/inline-const/using-late-bound-from-closure.stderr new file mode 100644 index 00000000000..d6e1579aa34 --- /dev/null +++ b/tests/ui/inline-const/using-late-bound-from-closure.stderr @@ -0,0 +1,19 @@ +error[E0597]: `awd` does not live long enough + --> $DIR/using-late-bound-from-closure.rs:9:29 + | +LL | let a = for<'a> |b: &'a ()| -> &'a () { + | -- lifetime `'a` defined here +LL | const { +LL | let awd = (); + | --- binding `awd` declared here +LL | let _: &'a () = &awd; + | ------ ^^^^ borrowed value does not live long enough + | | + | type annotation requires that `awd` is borrowed for `'a` +LL | +LL | }; + | - `awd` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/issues/issue-19380.rs b/tests/ui/issues/issue-19380.rs index fce737cba18..8b3fe4d2b09 100644 --- a/tests/ui/issues/issue-19380.rs +++ b/tests/ui/issues/issue-19380.rs @@ -15,5 +15,6 @@ struct Bar { const FOO : Foo = Foo; const BAR : Bar = Bar { foos: &[&FOO]}; //~^ ERROR E0038 +//~| ERROR E0038 fn main() { } diff --git a/tests/ui/issues/issue-19380.stderr b/tests/ui/issues/issue-19380.stderr index f6244d9d44f..1d7aa6bd459 100644 --- a/tests/ui/issues/issue-19380.stderr +++ b/tests/ui/issues/issue-19380.stderr @@ -45,6 +45,29 @@ help: alternatively, consider constraining `qiz` so it does not apply to trait o LL | fn qiz() where Self: Sized; | +++++++++++++++++ -error: aborting due to 2 previous errors +error[E0038]: the trait `Qiz` cannot be made into an object + --> $DIR/issue-19380.rs:16:31 + | +LL | const BAR : Bar = Bar { foos: &[&FOO]}; + | ^^^^^^^ `Qiz` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/issue-19380.rs:2:6 + | +LL | trait Qiz { + | --- this trait cannot be made into an object... +LL | fn qiz(); + | ^^^ ...because associated function `qiz` has no `self` parameter + = help: only type `Foo` implements the trait, consider using it directly instead +help: consider turning `qiz` into a method by giving it a `&self` argument + | +LL | fn qiz(&self); + | +++++ +help: alternatively, consider constraining `qiz` so it does not apply to trait objects + | +LL | fn qiz() where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/lint/command-line-lint-group-forbid.stderr b/tests/ui/lint/command-line-lint-group-forbid.stderr index 7b527e7b8b4..ec9b6ca9664 100644 --- a/tests/ui/lint/command-line-lint-group-forbid.stderr +++ b/tests/ui/lint/command-line-lint-group-forbid.stderr @@ -5,7 +5,6 @@ LL | let _InappropriateCamelCasing = true; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `_inappropriate_camel_casing` | = note: `-F non-snake-case` implied by `-F bad-style` - = help: to override `-F bad-style` add `#[allow(non_snake_case)]` error: aborting due to 1 previous error diff --git a/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr b/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr index 01c2ed84c63..d1b764b3414 100644 --- a/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr @@ -7,7 +7,6 @@ LL | 0...100 => true, = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> = note: `--force-warn ellipsis-inclusive-range-patterns` implied by `--force-warn rust-2021-compatibility` - = help: to override `--force-warn rust-2021-compatibility` add `#[allow(ellipsis_inclusive_range_patterns)]` warning: 1 warning emitted diff --git a/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr b/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr index e925a195fb1..dc7b1b7b98d 100644 --- a/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr +++ b/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr @@ -5,7 +5,6 @@ LL | pub fn FUNCTION() {} | ^^^^^^^^ help: convert the identifier to snake case: `function` | = note: `--force-warn non-snake-case` implied by `--force-warn nonstandard-style` - = help: to override `--force-warn nonstandard-style` add `#[allow(non_snake_case)]` warning: 1 warning emitted diff --git a/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr b/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr index a74cda2239f..dc85e8cf961 100644 --- a/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr @@ -7,7 +7,6 @@ LL | pub fn function(_x: Box<SomeTrait>) {} = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` - = help: to override `--force-warn rust-2018-idioms` add `#[allow(bare_trait_objects)]` help: if this is an object-safe trait, use `dyn` | LL | pub fn function(_x: Box<dyn SomeTrait>) {} diff --git a/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr b/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr index c9472a3b9b9..55cfad838f8 100644 --- a/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr +++ b/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr @@ -7,7 +7,6 @@ LL | pub fn function(_x: Box<SomeTrait>) {} = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` - = help: to override `--force-warn rust-2018-idioms` add `#[allow(bare_trait_objects)]` help: if this is an object-safe trait, use `dyn` | LL | pub fn function(_x: Box<dyn SomeTrait>) {} diff --git a/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr b/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr index 558d5cbb531..b7bf0c4ee21 100644 --- a/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr @@ -7,7 +7,6 @@ LL | pub fn function(_x: Box<SomeTrait>) {} = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` - = help: to override `--force-warn rust-2018-idioms` add `#[allow(bare_trait_objects)]` help: if this is an object-safe trait, use `dyn` | LL | pub fn function(_x: Box<dyn SomeTrait>) {} diff --git a/tests/ui/lint/group-forbid-always-trumps-cli.stderr b/tests/ui/lint/group-forbid-always-trumps-cli.stderr index 21674ebae9c..ed1242eacfc 100644 --- a/tests/ui/lint/group-forbid-always-trumps-cli.stderr +++ b/tests/ui/lint/group-forbid-always-trumps-cli.stderr @@ -5,7 +5,6 @@ LL | let x = 1; | ^ help: if this is intentional, prefix it with an underscore: `_x` | = note: `-F unused-variables` implied by `-F unused` - = help: to override `-F unused` add `#[allow(unused_variables)]` error: aborting due to 1 previous error diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index 760897c5143..089aa1b7ab7 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -1,6 +1,7 @@ //@ run-rustfix #![deny(unused_parens)] +#![feature(raw_ref_op)] #![allow(while_true)] // for rustfix #[derive(Eq, PartialEq)] @@ -125,4 +126,11 @@ fn main() { // FIXME: false positive. This parenthesis is required. unit! {} - One //~ ERROR unnecessary parentheses around block return value }; + + // Do *not* lint around `&raw` (but do lint when `&` creates a reference). + let mut x = 0; + let _r = &x; //~ ERROR unnecessary parentheses + let _r = &mut x; //~ ERROR unnecessary parentheses + let _r = (&raw const x); + let _r = (&raw mut x); } diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index 7cbaac8ae54..dc77ee00352 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -1,6 +1,7 @@ //@ run-rustfix #![deny(unused_parens)] +#![feature(raw_ref_op)] #![allow(while_true)] // for rustfix #[derive(Eq, PartialEq)] @@ -125,4 +126,11 @@ fn main() { // FIXME: false positive. This parenthesis is required. (unit! {} - One) //~ ERROR unnecessary parentheses around block return value }; + + // Do *not* lint around `&raw` (but do lint when `&` creates a reference). + let mut x = 0; + let _r = (&x); //~ ERROR unnecessary parentheses + let _r = (&mut x); //~ ERROR unnecessary parentheses + let _r = (&raw const x); + let _r = (&raw mut x); } diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index 755dd5fc309..c9422437a9f 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -1,5 +1,5 @@ error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:13:12 + --> $DIR/lint-unnecessary-parens.rs:14:12 | LL | return (1); | ^ ^ @@ -16,7 +16,7 @@ LL + return 1; | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:16:12 + --> $DIR/lint-unnecessary-parens.rs:17:12 | LL | return (X { y }); | ^ ^ @@ -28,7 +28,7 @@ LL + return X { y }; | error: unnecessary parentheses around type - --> $DIR/lint-unnecessary-parens.rs:19:46 + --> $DIR/lint-unnecessary-parens.rs:20:46 | LL | pub fn unused_parens_around_return_type() -> (u32) { | ^ ^ @@ -40,7 +40,7 @@ LL + pub fn unused_parens_around_return_type() -> u32 { | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:25:9 + --> $DIR/lint-unnecessary-parens.rs:26:9 | LL | (5) | ^ ^ @@ -52,7 +52,7 @@ LL + 5 | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:27:5 + --> $DIR/lint-unnecessary-parens.rs:28:5 | LL | (5) | ^ ^ @@ -64,7 +64,7 @@ LL + 5 | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:39:7 + --> $DIR/lint-unnecessary-parens.rs:40:7 | LL | if(true) {} | ^ ^ @@ -76,7 +76,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:40:10 + --> $DIR/lint-unnecessary-parens.rs:41:10 | LL | while(true) {} | ^ ^ @@ -88,7 +88,7 @@ LL + while true {} | error: unnecessary parentheses around `for` iterator expression - --> $DIR/lint-unnecessary-parens.rs:41:13 + --> $DIR/lint-unnecessary-parens.rs:42:13 | LL | for _ in(e) {} | ^ ^ @@ -100,7 +100,7 @@ LL + for _ in e {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:42:10 + --> $DIR/lint-unnecessary-parens.rs:43:10 | LL | match(1) { _ => ()} | ^ ^ @@ -112,7 +112,7 @@ LL + match 1 { _ => ()} | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:43:11 + --> $DIR/lint-unnecessary-parens.rs:44:11 | LL | return(1); | ^ ^ @@ -124,7 +124,7 @@ LL + return 1; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:74:31 + --> $DIR/lint-unnecessary-parens.rs:75:31 | LL | pub const CONST_ITEM: usize = (10); | ^ ^ @@ -136,7 +136,7 @@ LL + pub const CONST_ITEM: usize = 10; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:75:33 + --> $DIR/lint-unnecessary-parens.rs:76:33 | LL | pub static STATIC_ITEM: usize = (10); | ^ ^ @@ -148,7 +148,7 @@ LL + pub static STATIC_ITEM: usize = 10; | error: unnecessary parentheses around function argument - --> $DIR/lint-unnecessary-parens.rs:79:9 + --> $DIR/lint-unnecessary-parens.rs:80:9 | LL | bar((true)); | ^ ^ @@ -160,7 +160,7 @@ LL + bar(true); | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:81:8 + --> $DIR/lint-unnecessary-parens.rs:82:8 | LL | if (true) {} | ^ ^ @@ -172,7 +172,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:82:11 + --> $DIR/lint-unnecessary-parens.rs:83:11 | LL | while (true) {} | ^ ^ @@ -184,7 +184,7 @@ LL + while true {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:83:11 + --> $DIR/lint-unnecessary-parens.rs:84:11 | LL | match (true) { | ^ ^ @@ -196,7 +196,7 @@ LL + match true { | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:86:16 + --> $DIR/lint-unnecessary-parens.rs:87:16 | LL | if let 1 = (1) {} | ^ ^ @@ -208,7 +208,7 @@ LL + if let 1 = 1 {} | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:87:19 + --> $DIR/lint-unnecessary-parens.rs:88:19 | LL | while let 1 = (2) {} | ^ ^ @@ -220,7 +220,7 @@ LL + while let 1 = 2 {} | error: unnecessary parentheses around method argument - --> $DIR/lint-unnecessary-parens.rs:103:24 + --> $DIR/lint-unnecessary-parens.rs:104:24 | LL | X { y: false }.foo((true)); | ^ ^ @@ -232,7 +232,7 @@ LL + X { y: false }.foo(true); | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:105:18 + --> $DIR/lint-unnecessary-parens.rs:106:18 | LL | let mut _a = (0); | ^ ^ @@ -244,7 +244,7 @@ LL + let mut _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:106:10 + --> $DIR/lint-unnecessary-parens.rs:107:10 | LL | _a = (0); | ^ ^ @@ -256,7 +256,7 @@ LL + _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:107:11 + --> $DIR/lint-unnecessary-parens.rs:108:11 | LL | _a += (1); | ^ ^ @@ -268,7 +268,7 @@ LL + _a += 1; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:109:8 + --> $DIR/lint-unnecessary-parens.rs:110:8 | LL | let(mut _a) = 3; | ^ ^ @@ -280,7 +280,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:110:9 + --> $DIR/lint-unnecessary-parens.rs:111:9 | LL | let (mut _a) = 3; | ^ ^ @@ -292,7 +292,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:111:8 + --> $DIR/lint-unnecessary-parens.rs:112:8 | LL | let( mut _a) = 3; | ^^ ^ @@ -304,7 +304,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:113:8 + --> $DIR/lint-unnecessary-parens.rs:114:8 | LL | let(_a) = 3; | ^ ^ @@ -316,7 +316,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:114:9 + --> $DIR/lint-unnecessary-parens.rs:115:9 | LL | let (_a) = 3; | ^ ^ @@ -328,7 +328,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:115:8 + --> $DIR/lint-unnecessary-parens.rs:116:8 | LL | let( _a) = 3; | ^^ ^ @@ -340,7 +340,7 @@ LL + let _a = 3; | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:121:9 + --> $DIR/lint-unnecessary-parens.rs:122:9 | LL | (unit!() - One) | ^ ^ @@ -352,7 +352,7 @@ LL + unit!() - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:123:9 + --> $DIR/lint-unnecessary-parens.rs:124:9 | LL | (unit![] - One) | ^ ^ @@ -364,7 +364,7 @@ LL + unit![] - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:126:9 + --> $DIR/lint-unnecessary-parens.rs:127:9 | LL | (unit! {} - One) | ^ ^ @@ -375,5 +375,29 @@ LL - (unit! {} - One) LL + unit! {} - One | -error: aborting due to 31 previous errors +error: unnecessary parentheses around assigned value + --> $DIR/lint-unnecessary-parens.rs:132:14 + | +LL | let _r = (&x); + | ^ ^ + | +help: remove these parentheses + | +LL - let _r = (&x); +LL + let _r = &x; + | + +error: unnecessary parentheses around assigned value + --> $DIR/lint-unnecessary-parens.rs:133:14 + | +LL | let _r = (&mut x); + | ^ ^ + | +help: remove these parentheses + | +LL - let _r = (&mut x); +LL + let _r = &mut x; + | + +error: aborting due to 33 previous errors diff --git a/tests/ui/lint/unaligned_references.stderr b/tests/ui/lint/unaligned_references.current.stderr index 328cafbd986..0f980c5301f 100644 --- a/tests/ui/lint/unaligned_references.stderr +++ b/tests/ui/lint/unaligned_references.current.stderr @@ -1,5 +1,5 @@ error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:28:13 + --> $DIR/unaligned_references.rs:32:13 | LL | &self.x; | ^^^^^^^ @@ -9,7 +9,7 @@ LL | &self.x; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:40:24 + --> $DIR/unaligned_references.rs:44:24 | LL | println!("{:?}", &*foo.0); | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", &*foo.0); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:42:24 + --> $DIR/unaligned_references.rs:46:24 | LL | println!("{:?}", &*foo.0); | ^^^^^ @@ -29,7 +29,7 @@ LL | println!("{:?}", &*foo.0); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:47:24 + --> $DIR/unaligned_references.rs:51:24 | LL | println!("{:?}", &*foo.0); | ^^^^^ @@ -39,7 +39,7 @@ LL | println!("{:?}", &*foo.0); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:57:17 + --> $DIR/unaligned_references.rs:81:17 | LL | let _ = &good.ptr; | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = &good.ptr; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:58:17 + --> $DIR/unaligned_references.rs:82:17 | LL | let _ = &good.data; | ^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | let _ = &good.data; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:60:17 + --> $DIR/unaligned_references.rs:84:17 | LL | let _ = &good.data as *const _; | ^^^^^^^^^^ @@ -69,7 +69,7 @@ LL | let _ = &good.data as *const _; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:61:27 + --> $DIR/unaligned_references.rs:85:27 | LL | let _: *const _ = &good.data; | ^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | let _: *const _ = &good.data; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:63:17 + --> $DIR/unaligned_references.rs:87:17 | LL | let _ = good.data.clone(); | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = good.data.clone(); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:65:17 + --> $DIR/unaligned_references.rs:89:17 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | let _ = &good.data2[0]; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:74:17 + --> $DIR/unaligned_references.rs:98:17 | LL | let _ = &packed2.x; | ^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | let _ = &packed2.x; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:113:20 + --> $DIR/unaligned_references.rs:137:20 | LL | let _ref = &m1.1.a; | ^^^^^^^ @@ -119,7 +119,7 @@ LL | let _ref = &m1.1.a; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:116:20 + --> $DIR/unaligned_references.rs:140:20 | LL | let _ref = &m2.1.a; | ^^^^^^^ diff --git a/tests/ui/lint/unaligned_references.next.stderr b/tests/ui/lint/unaligned_references.next.stderr new file mode 100644 index 00000000000..0f980c5301f --- /dev/null +++ b/tests/ui/lint/unaligned_references.next.stderr @@ -0,0 +1,133 @@ +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:32:13 + | +LL | &self.x; + | ^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:44:24 + | +LL | println!("{:?}", &*foo.0); + | ^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:46:24 + | +LL | println!("{:?}", &*foo.0); + | ^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:51:24 + | +LL | println!("{:?}", &*foo.0); + | ^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:81:17 + | +LL | let _ = &good.ptr; + | ^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:82:17 + | +LL | let _ = &good.data; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:84:17 + | +LL | let _ = &good.data as *const _; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:85:27 + | +LL | let _: *const _ = &good.data; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:87:17 + | +LL | let _ = good.data.clone(); + | ^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:89:17 + | +LL | let _ = &good.data2[0]; + | ^^^^^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:98:17 + | +LL | let _ = &packed2.x; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:137:20 + | +LL | let _ref = &m1.1.a; + | ^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:140:20 + | +LL | let _ref = &m2.1.a; + | ^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0793`. diff --git a/tests/ui/lint/unaligned_references.rs b/tests/ui/lint/unaligned_references.rs index 3f6dab35475..321e3ed135c 100644 --- a/tests/ui/lint/unaligned_references.rs +++ b/tests/ui/lint/unaligned_references.rs @@ -1,5 +1,9 @@ -use std::mem::ManuallyDrop; +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + use std::fmt::Debug; +use std::mem::ManuallyDrop; #[repr(packed)] pub struct Good { @@ -50,6 +54,26 @@ fn packed_dyn() { println!("{:?}", &*foo.0); // no error! } +// Test for #115396 +fn packed_slice_behind_alias() { + trait Mirror { + type Assoc: ?Sized; + } + impl<T: ?Sized> Mirror for T { + type Assoc = T; + } + + struct W<T: ?Sized>(<T as Mirror>::Assoc); + + #[repr(packed)] + struct Unaligned<T: ?Sized>(ManuallyDrop<W<T>>); + + // Even if the actual alignment is 1, we cannot know that when looking at `dyn Debug.` + let ref local: Unaligned<[_; 3]> = Unaligned(ManuallyDrop::new(W([3, 5, 8u8]))); + let foo: &Unaligned<[u8]> = local; + let x = &foo.0; // Fine, since the tail of `foo` is `[_]` +} + fn main() { unsafe { let good = Good { data: 0, ptr: &0, data2: [0, 0], aligned: [0; 32] }; diff --git a/tests/ui/offset-of/offset-of-slice-normalized.rs b/tests/ui/offset-of/offset-of-slice-normalized.rs new file mode 100644 index 00000000000..9d1fd9dd2ee --- /dev/null +++ b/tests/ui/offset-of/offset-of-slice-normalized.rs @@ -0,0 +1,37 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ run-pass + +#![feature(offset_of_slice)] + +use std::mem::offset_of; + +trait Mirror { + type Assoc: ?Sized; +} +impl<T: ?Sized> Mirror for T { + type Assoc = T; +} + +#[repr(C)] +struct S { + a: u8, + b: (u8, u8), + c: <[i32] as Mirror>::Assoc, +} + +#[repr(C)] +struct T { + x: i8, + y: S, +} + +type Tup = (i16, <[i32] as Mirror>::Assoc); + +fn main() { + assert_eq!(offset_of!(S, c), 4); + assert_eq!(offset_of!(T, y), 4); + assert_eq!(offset_of!(T, y.c), 8); + assert_eq!(offset_of!(Tup, 1), 4); +} diff --git a/tests/ui/parser/suggest-remove-compount-assign-let-ice.rs b/tests/ui/parser/suggest-remove-compount-assign-let-ice.rs new file mode 100644 index 00000000000..1affee5678e --- /dev/null +++ b/tests/ui/parser/suggest-remove-compount-assign-let-ice.rs @@ -0,0 +1,16 @@ +//! Previously we would try to issue a suggestion for `let x <op>= 1`, i.e. a compound assignment +//! within a `let` binding, to remove the `<op>`. The suggestion code unfortunately incorrectly +//! assumed that the `<op>` is an exactly-1-byte ASCII character, but this assumption is incorrect +//! because we also recover Unicode-confusables like `➖=` as `-=`. In this example, the suggestion +//! code used a `+ BytePos(1)` to calculate the span of the `<op>` codepoint that looks like `-` but +//! the mult-byte Unicode look-alike would cause the suggested removal span to be inside a +//! multi-byte codepoint boundary, triggering a codepoint boundary assertion. +//! +//! issue: rust-lang/rust#128845 + +fn main() { + // Adapted from #128845 but with irrelevant components removed and simplified. + let x ➖= 1; + //~^ ERROR unknown start of token: \u{2796} + //~| ERROR: can't reassign to an uninitialized variable +} diff --git a/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr b/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr new file mode 100644 index 00000000000..59716d69b50 --- /dev/null +++ b/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr @@ -0,0 +1,26 @@ +error: unknown start of token: \u{2796} + --> $DIR/suggest-remove-compount-assign-let-ice.rs:13:11 + | +LL | let x ➖= 1; + | ^^ + | +help: Unicode character '➖' (Heavy Minus Sign) looks like '-' (Minus/Hyphen), but it is not + | +LL | let x -= 1; + | ~ + +error: can't reassign to an uninitialized variable + --> $DIR/suggest-remove-compount-assign-let-ice.rs:13:11 + | +LL | let x ➖= 1; + | ^^^ + | + = help: if you meant to overwrite, remove the `let` binding +help: initialize the variable + | +LL - let x ➖= 1; +LL + let x = 1; + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/polymorphization/inline-tainted-body.rs b/tests/ui/polymorphization/inline-tainted-body.rs new file mode 100644 index 00000000000..13aec97e22b --- /dev/null +++ b/tests/ui/polymorphization/inline-tainted-body.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes + +#![feature(unboxed_closures)] + +use std::sync::Arc; + +pub struct WeakOnce<T>(); +//~^ ERROR type parameter `T` is never used + +impl<T> WeakOnce<T> { + extern "rust-call" fn try_get(&self) -> Option<Arc<T>> {} + //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + //~| ERROR mismatched types + + pub fn get(&self) -> Arc<T> { + self.try_get() + .unwrap_or_else(|| panic!("Singleton {} not available", std::any::type_name::<T>())) + } +} + +fn main() {} diff --git a/tests/ui/polymorphization/inline-tainted-body.stderr b/tests/ui/polymorphization/inline-tainted-body.stderr new file mode 100644 index 00000000000..5c3bd70adae --- /dev/null +++ b/tests/ui/polymorphization/inline-tainted-body.stderr @@ -0,0 +1,30 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/inline-tainted-body.rs:7:21 + | +LL | pub struct WeakOnce<T>(); + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/inline-tainted-body.rs:11:35 + | +LL | extern "rust-call" fn try_get(&self) -> Option<Arc<T>> {} + | ^^^^^ + +error[E0308]: mismatched types + --> $DIR/inline-tainted-body.rs:11:45 + | +LL | extern "rust-call" fn try_get(&self) -> Option<Arc<T>> {} + | ------- ^^^^^^^^^^^^^^ expected `Option<Arc<T>>`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + | + = note: expected enum `Option<Arc<T>>` + found unit type `()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0392. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs index de002ef71d7..fec4e75290f 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs @@ -34,6 +34,7 @@ fn foo() { #[target_feature(enable = "sse2")] fn bar() { + sse2(); avx_bmi2(); //~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe Quux.avx_bmi2(); @@ -43,7 +44,6 @@ fn bar() { #[target_feature(enable = "avx")] fn baz() { sse2(); - //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe avx_bmi2(); //~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe Quux.avx_bmi2(); @@ -54,7 +54,8 @@ fn baz() { #[target_feature(enable = "bmi2")] fn qux() { sse2(); - //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe + avx_bmi2(); + Quux.avx_bmi2(); } const _: () = sse2(); @@ -64,8 +65,6 @@ const _: () = sse2_and_fxsr(); //~^ ERROR call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe #[deny(unsafe_op_in_unsafe_fn)] -#[target_feature(enable = "avx")] -#[target_feature(enable = "bmi2")] unsafe fn needs_unsafe_block() { sse2(); //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index 537819ab859..1ddf05b40a6 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -24,7 +24,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:37:5 + --> $DIR/safe-calls.rs:38:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -32,22 +32,13 @@ LL | avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:39:5 + --> $DIR/safe-calls.rs:40:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:45:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:47:5 | @@ -65,16 +56,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:56:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:60:15 + --> $DIR/safe-calls.rs:61:15 | LL | const _: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -83,7 +65,7 @@ LL | const _: () = sse2(); = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:63:15 + --> $DIR/safe-calls.rs:64:15 | LL | const _: () = sse2_and_fxsr(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -92,7 +74,7 @@ LL | const _: () = sse2_and_fxsr(); = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block - --> $DIR/safe-calls.rs:70:5 + --> $DIR/safe-calls.rs:69:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -101,16 +83,16 @@ LL | sse2(); = help: in order for the call to be safe, the context requires the following additional target feature: sse2 = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:69:1 + --> $DIR/safe-calls.rs:68:1 | LL | unsafe fn needs_unsafe_block() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/safe-calls.rs:66:8 + --> $DIR/safe-calls.rs:67:8 | LL | #[deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.rs b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs new file mode 100644 index 00000000000..cd15be54ec7 --- /dev/null +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs @@ -0,0 +1,17 @@ +// This ensures we don't ICE in situations like rust-lang/rust#127299. + +trait Qux { + fn bar() -> i32; +} + +pub struct Lint { + pub desc: &'static dyn Qux, + //~^ ERROR cannot be made into an object +} + +static FOO: &Lint = &Lint { desc: "desc" }; +//~^ ERROR cannot be shared between threads safely +//~| ERROR cannot be made into an object +//~| ERROR cannot be made into an object + +fn main() {} diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr new file mode 100644 index 00000000000..49e8d87f354 --- /dev/null +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr @@ -0,0 +1,87 @@ +error[E0038]: the trait `Qux` cannot be made into an object + --> $DIR/unsizing-wfcheck-issue-127299.rs:8:24 + | +LL | pub desc: &'static dyn Qux, + | ^^^^^^^ `Qux` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 + | +LL | trait Qux { + | --- this trait cannot be made into an object... +LL | fn bar() -> i32; + | ^^^ ...because associated function `bar` has no `self` parameter +help: consider turning `bar` into a method by giving it a `&self` argument + | +LL | fn bar(&self) -> i32; + | +++++ +help: alternatively, consider constraining `bar` so it does not apply to trait objects + | +LL | fn bar() -> i32 where Self: Sized; + | +++++++++++++++++ + +error[E0277]: `(dyn Qux + 'static)` cannot be shared between threads safely + --> $DIR/unsizing-wfcheck-issue-127299.rs:12:13 + | +LL | static FOO: &Lint = &Lint { desc: "desc" }; + | ^^^^^ `(dyn Qux + 'static)` cannot be shared between threads safely + | + = help: within `&'static Lint`, the trait `Sync` is not implemented for `(dyn Qux + 'static)`, which is required by `&'static Lint: Sync` + = note: required because it appears within the type `&'static (dyn Qux + 'static)` +note: required because it appears within the type `Lint` + --> $DIR/unsizing-wfcheck-issue-127299.rs:7:12 + | +LL | pub struct Lint { + | ^^^^ + = note: required because it appears within the type `&'static Lint` + = note: shared static variables must have a type that implements `Sync` + +error[E0038]: the trait `Qux` cannot be made into an object + --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 + | +LL | static FOO: &Lint = &Lint { desc: "desc" }; + | ^^^^^^ `Qux` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 + | +LL | trait Qux { + | --- this trait cannot be made into an object... +LL | fn bar() -> i32; + | ^^^ ...because associated function `bar` has no `self` parameter + = note: required for the cast from `&'static str` to `&'static (dyn Qux + 'static)` +help: consider turning `bar` into a method by giving it a `&self` argument + | +LL | fn bar(&self) -> i32; + | +++++ +help: alternatively, consider constraining `bar` so it does not apply to trait objects + | +LL | fn bar() -> i32 where Self: Sized; + | +++++++++++++++++ + +error[E0038]: the trait `Qux` cannot be made into an object + --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 + | +LL | static FOO: &Lint = &Lint { desc: "desc" }; + | ^^^^^^ `Qux` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 + | +LL | trait Qux { + | --- this trait cannot be made into an object... +LL | fn bar() -> i32; + | ^^^ ...because associated function `bar` has no `self` parameter +help: consider turning `bar` into a method by giving it a `&self` argument + | +LL | fn bar(&self) -> i32; + | +++++ +help: alternatively, consider constraining `bar` so it does not apply to trait objects + | +LL | fn bar() -> i32 where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/structs/field-implied-unsizing-wfcheck.rs b/tests/ui/structs/field-implied-unsizing-wfcheck.rs new file mode 100644 index 00000000000..a46c9b5a68b --- /dev/null +++ b/tests/ui/structs/field-implied-unsizing-wfcheck.rs @@ -0,0 +1,32 @@ +struct FooStruct { + nested: &'static Bar<dyn std::fmt::Debug>, + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} + +struct FooTuple(&'static Bar<dyn std::fmt::Debug>); +//~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + +enum FooEnum1 { + Struct { nested: &'static Bar<dyn std::fmt::Debug> }, + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} + +enum FooEnum2 { + Tuple(&'static Bar<dyn std::fmt::Debug>), + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} + +struct Bar<T>(T); + +fn main() { + // Ensure there's an error at the construction site, for error tainting purposes. + + FooStruct { nested: &Bar(4) }; + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + FooTuple(&Bar(4)); + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + FooEnum1::Struct { nested: &Bar(4) }; + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + FooEnum2::Tuple(&Bar(4)); + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} diff --git a/tests/ui/structs/field-implied-unsizing-wfcheck.stderr b/tests/ui/structs/field-implied-unsizing-wfcheck.stderr new file mode 100644 index 00000000000..47d486ffa33 --- /dev/null +++ b/tests/ui/structs/field-implied-unsizing-wfcheck.stderr @@ -0,0 +1,163 @@ +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:2:13 + | +LL | nested: &'static Bar<dyn std::fmt::Debug>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:6:17 + | +LL | struct FooTuple(&'static Bar<dyn std::fmt::Debug>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:10:22 + | +LL | Struct { nested: &'static Bar<dyn std::fmt::Debug> }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:15:11 + | +LL | Tuple(&'static Bar<dyn std::fmt::Debug>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:24:25 + | +LL | FooStruct { nested: &Bar(4) }; + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:26:14 + | +LL | FooTuple(&Bar(4)); + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:28:32 + | +LL | FooEnum1::Struct { nested: &Bar(4) }; + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:30:21 + | +LL | FooEnum2::Tuple(&Bar(4)); + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar<T>(T); + | ^ - ...if indirection were used here: `Box<T>` + | | + | this could be changed to `T: ?Sized`... + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr index aeeec3aca34..3a1f685f16b 100644 --- a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr +++ b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr @@ -2,18 +2,13 @@ error[E0700]: hidden type for `impl Iterator<Item = i32>` captures lifetime that --> $DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:7:5 | LL | fn bar(src: &crate::Foo) -> impl Iterator<Item = i32> { - | ---------- ------------------------- opaque type defined here - | | - | hidden type `FilterMap<std::slice::Iter<'static, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures the anonymous lifetime defined here + | ------------------------- opaque type defined here LL | / [0].into_iter() LL | | LL | | .filter_map(|_| foo(src)) | |_________________________________^ | -help: to declare that `impl Iterator<Item = i32>` captures `'_`, you can introduce a named lifetime parameter `'a` - | -LL | fn bar<'a>(src: &'a crate::Foo<'a>) -> impl Iterator<Item = i32> + 'a { - | ++++ ++ ++++ ++++ + = note: hidden type `FilterMap<std::slice::Iter<'static, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures lifetime `'_` error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr index a2a78ddc705..db16fff826f 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr @@ -44,15 +44,13 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct AABB<K: Debug> { LL | pub loc: Vector2<K>, - | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K`, which is required by `Vector2<K>: Clone` + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K` | -note: required for `Vector2<K>` to implement `Clone` - --> $DIR/missing-bound-in-derive-copy-impl-2.rs:4:23 +note: required by a bound in `Vector2` + --> $DIR/missing-bound-in-derive-copy-impl-2.rs:5:31 | -LL | #[derive(Debug, Copy, Clone)] - | ^^^^^ LL | pub struct Vector2<T: Debug + Copy + Clone> { - | ---- unsatisfied trait bound introduced in this `derive` macro + | ^^^^ required by this bound in `Vector2` = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr index 2ae0871b815..cf383b5c8ff 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr @@ -95,15 +95,13 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct AABB<K> { LL | pub loc: Vector2<K>, - | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K`, which is required by `Vector2<K>: Clone` + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K` | -note: required for `Vector2<K>` to implement `Clone` - --> $DIR/missing-bound-in-derive-copy-impl.rs:3:23 +note: required by a bound in `Vector2` + --> $DIR/missing-bound-in-derive-copy-impl.rs:4:31 | -LL | #[derive(Debug, Copy, Clone)] - | ^^^^^ LL | pub struct Vector2<T: Debug + Copy + Clone> { - | ---- unsatisfied trait bound introduced in this `derive` macro + | ^^^^ required by this bound in `Vector2` = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` | diff --git a/tests/ui/target-feature/asm-implied-features-issue-128125.rs b/tests/ui/target-feature/asm-implied-features-issue-128125.rs new file mode 100644 index 00000000000..2b4f1d7df85 --- /dev/null +++ b/tests/ui/target-feature/asm-implied-features-issue-128125.rs @@ -0,0 +1,10 @@ +//@ only-x86_64 +//@ build-pass +#![allow(dead_code)] + +#[target_feature(enable = "avx2")] +unsafe fn demo(v: std::arch::x86_64::__m256i) { + std::arch::asm!("/* {v} */", v = in(ymm_reg) v); +} + +fn main() {} diff --git a/tests/ui/target-feature/implied-features.rs b/tests/ui/target-feature/implied-features.rs new file mode 100644 index 00000000000..4fdd843e6c2 --- /dev/null +++ b/tests/ui/target-feature/implied-features.rs @@ -0,0 +1,24 @@ +//@ only-x86_64 +//@ build-pass +#![feature(target_feature_11)] +#![allow(dead_code)] + +#[target_feature(enable = "ssse3")] +fn call_ssse3() {} + +#[target_feature(enable = "avx")] +fn call_avx() {} + +#[target_feature(enable = "avx2")] +fn test_avx2() { + call_ssse3(); + call_avx(); +} + +#[target_feature(enable = "fma")] +fn test_fma() { + call_ssse3(); + call_avx(); +} + +fn main() {} diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr index 6f74b89d224..03e91b52a2a 100644 --- a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr @@ -4,6 +4,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type NonNullU32 = pattern_type!(u32 is 1..); | ^^^^^^^^^^^^ | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -13,6 +14,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Percent = pattern_type!(u32 is 0..=100); | ^^^^^^^^^^^^ | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -22,6 +24,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Negative = pattern_type!(i32 is ..=0); | ^^^^^^^^^^^^ | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -31,6 +34,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Positive = pattern_type!(i32 is 0..); | ^^^^^^^^^^^^ | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -40,6 +44,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Always = pattern_type!(Option<u32> is Some(_)); | ^^^^^^^^^^^^ | + = note: see issue #123646 <https://github.com/rust-lang/rust/issues/123646> for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.rs b/tests/ui/typeck/suggest-arg-comma-delete-ice.rs new file mode 100644 index 00000000000..48d02e13eca --- /dev/null +++ b/tests/ui/typeck/suggest-arg-comma-delete-ice.rs @@ -0,0 +1,19 @@ +//! Previously, we tried to remove extra arg commas when providing extra arg removal suggestions. +//! One of the edge cases is having to account for an arg that has a closing delimiter `)` +//! following it. However, the previous suggestion code assumed that the delimiter is in fact +//! exactly the 1-byte `)` character. This assumption was proven incorrect, because we recover +//! from Unicode-confusable delimiters in the parser, which means that the ending delimiter could be +//! a multi-byte codepoint that looks *like* a `)`. Subtracing 1 byte could land us in the middle of +//! a codepoint, triggering a codepoint boundary assertion. +//! +//! issue: rust-lang/rust#128717 + +fn main() { + // The following example has been modified from #128717 to remove irrelevant Unicode as they do + // not otherwise partake in the right delimiter calculation causing the codepoint boundary + // assertion. + main(rahh); + //~^ ERROR unknown start of token + //~| ERROR this function takes 0 arguments but 1 argument was supplied + //~| ERROR cannot find value `rahh` in this scope +} diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr b/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr new file mode 100644 index 00000000000..53608391f3c --- /dev/null +++ b/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr @@ -0,0 +1,38 @@ +error: unknown start of token: \u{ff09} + --> $DIR/suggest-arg-comma-delete-ice.rs:15:14 + | +LL | main(rahh); + | ^^ + | +help: Unicode character ')' (Fullwidth Right Parenthesis) looks like ')' (Right Parenthesis), but it is not + | +LL | main(rahh); + | ~ + +error[E0425]: cannot find value `rahh` in this scope + --> $DIR/suggest-arg-comma-delete-ice.rs:15:10 + | +LL | main(rahh); + | ^^^^ not found in this scope + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/suggest-arg-comma-delete-ice.rs:15:5 + | +LL | main(rahh); + | ^^^^ ---- unexpected argument + | +note: function defined here + --> $DIR/suggest-arg-comma-delete-ice.rs:11:4 + | +LL | fn main() { + | ^^^^ +help: remove the extra argument + | +LL - main(rahh); +LL + main(); + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0061, E0425. +For more information about an error, try `rustc --explain E0061`. diff --git a/triagebot.toml b/triagebot.toml index 2795f937284..33108f743cb 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -778,6 +778,14 @@ If this was unintentional then you should revert the changes before this PR is m Otherwise, you can ignore this comment. """ +[mentions."library/Cargo.lock"] +message = """ +These commits modify the `library/Cargo.lock` file. Unintentional changes to `library/Cargo.lock` can be introduced when switching branches and rebasing PRs. + +If this was unintentional then you should revert the changes before this PR is merged. +Otherwise, you can ignore this comment. +""" + [mentions."src/tools/x"] message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version." |
