diff options
1290 files changed, 34896 insertions, 17689 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfaa70bb9db..eeff563d8ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ standard library in the [Standard library developers Guide][std-dev-guide], comm ## About the [rustc-dev-guide] The [rustc-dev-guide] is meant to help document how rustc –the Rust compiler– works, -as well as to help new contributors get involved in rustc development. It is recommend +as well as to help new contributors get involved in rustc development. It is recommended to read and understand the [rustc-dev-guide] before making a contribution. This guide talks about the different bots in the Rust ecosystem, the Rust development tools, bootstrapping, the compiler architecture, source code representation, and more. diff --git a/Cargo.lock b/Cargo.lock index d3ecadf6e17..ad9e4fcb2d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,12 +194,6 @@ dependencies = [ ] [[package]] -name = "array_tool" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" - -[[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -238,17 +232,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" [[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -356,7 +339,7 @@ version = "0.1.0" dependencies = [ "anyhow", "curl", - "indexmap 2.0.0", + "indexmap", "serde", "serde_json", "toml 0.5.11", @@ -553,7 +536,7 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "clippy" -version = "0.1.75" +version = "0.1.76" dependencies = [ "anstream", "clippy_config", @@ -581,7 +564,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.75" +version = "0.1.76" dependencies = [ "rustc-semver", "serde", @@ -604,14 +587,13 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.75" +version = "0.1.76" dependencies = [ "arrayvec", "cargo_metadata 0.15.4", "clippy_config", "clippy_utils", "declare_clippy_lint", - "if_chain", "itertools", "quine-mc_cluskey", "regex", @@ -630,11 +612,10 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.75" +version = "0.1.76" dependencies = [ "arrayvec", "clippy_config", - "if_chain", "itertools", "rustc-semver", ] @@ -741,7 +722,7 @@ dependencies = [ "getopts", "glob", "home", - "indexmap 2.0.0", + "indexmap", "lazycell", "libc", "miow", @@ -1016,7 +997,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.75" +version = "0.1.76" dependencies = [ "itertools", "quote", @@ -1249,24 +1230,11 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "env_logger" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "humantime 2.1.0", + "humantime", "is-terminal", "log", "regex", @@ -1380,7 +1348,7 @@ dependencies = [ "intl-memoizer", "intl_pluralrules", "rustc-hash", - "self_cell", + "self_cell 0.10.3", "smallvec", "unic-langid", ] @@ -1610,7 +1578,7 @@ checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" dependencies = [ "compiler_builtins", "fallible-iterator", - "indexmap 2.0.0", + "indexmap", "rustc-std-workspace-alloc", "rustc-std-workspace-core", "stable_deref_trait", @@ -1646,9 +1614,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -1656,7 +1624,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap", "slab", "tokio", "tokio-util", @@ -1679,12 +1647,6 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" @@ -1704,15 +1666,6 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" @@ -1804,15 +1757,6 @@ dependencies = [ [[package]] name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" @@ -2018,22 +1962,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown", "rustc-rayon", "serde", ] @@ -2119,7 +2053,7 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "rustix", "windows-sys 0.48.0", ] @@ -2195,12 +2129,10 @@ dependencies = [ [[package]] name = "jsonpath_lib" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61352ec23883402b7d30b3313c16cbabefb8907361c4eb669d990cbb87ceee5a" +checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" dependencies = [ - "array_tool", - "env_logger 0.7.1", "log", "serde", "serde_json", @@ -2418,7 +2350,7 @@ dependencies = [ "clap", "clap_complete", "elasticlunr-rs", - "env_logger 0.10.0", + "env_logger", "handlebars", "log", "memchr", @@ -2543,7 +2475,7 @@ dependencies = [ "aes", "colored", "ctrlc", - "env_logger 0.10.0", + "env_logger", "getrandom", "lazy_static", "libc", @@ -2647,7 +2579,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "libc", ] @@ -2666,8 +2598,8 @@ dependencies = [ "compiler_builtins", "crc32fast", "flate2", - "hashbrown 0.14.2", - "indexmap 2.0.0", + "hashbrown", + "indexmap", "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -2752,11 +2684,11 @@ dependencies = [ "camino", "clap", "derive_builder", - "env_logger 0.10.0", + "env_logger", "fs_extra", "glob", "humansize", - "humantime 2.1.0", + "humantime", "log", "reqwest", "serde", @@ -3107,12 +3039,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe" [[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] name = "quine-mc_cluskey" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3359,7 +3285,7 @@ name = "rustbook" version = "0.1.0" dependencies = [ "clap", - "env_logger 0.10.0", + "env_logger", "mdbook", ] @@ -3743,7 +3669,7 @@ dependencies = [ "bitflags 1.3.2", "elsa", "ena", - "indexmap 2.0.0", + "indexmap", "itertools", "jobserver", "libc", @@ -3817,6 +3743,7 @@ dependencies = [ "rustc_query_system", "rustc_resolve", "rustc_session", + "rustc_smir", "rustc_span", "rustc_symbol_mangling", "rustc_target", @@ -4504,7 +4431,7 @@ dependencies = [ name = "rustc_serialize" version = "0.0.0" dependencies = [ - "indexmap 2.0.0", + "indexmap", "rustc_macros", "smallvec", "tempfile", @@ -4554,7 +4481,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ - "indexmap 2.0.0", + "indexmap", "md-5", "rustc_arena", "rustc_data_structures", @@ -4713,7 +4640,7 @@ dependencies = [ "arrayvec", "askama", "expect-test", - "indexmap 2.0.0", + "indexmap", "itertools", "minifier", "once_cell", @@ -4903,9 +4830,18 @@ dependencies = [ [[package]] name = "self_cell" -version = "0.10.2" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.2", +] + +[[package]] +name = "self_cell" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" +checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" [[package]] name = "semver" @@ -4942,7 +4878,7 @@ version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ - "indexmap 2.0.0", + "indexmap", "itoa", "ryu", "serde", @@ -5122,8 +5058,8 @@ dependencies = [ "core", "dlmalloc", "fortanix-sgx-abi", - "hashbrown 0.14.2", - "hermit-abi 0.3.2", + "hashbrown", + "hermit-abi", "libc", "miniz_oxide", "object", @@ -5430,7 +5366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ "gimli", - "hashbrown 0.14.2", + "hashbrown", "object", "tracing", ] @@ -5602,7 +5538,7 @@ version = "0.19.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ - "indexmap 2.0.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", diff --git a/RELEASES.md b/RELEASES.md index 3b764fd773b..5bc302305c7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -7,11 +7,12 @@ Language -------- - [Codify that `std::mem::Discriminant<T>` does not depend on any lifetimes in T](https://github.com/rust-lang/rust/pull/104299/) -- [Replace `private_in_public` lint with `private_interfaces` and `private_bounds` per RFC 2145](https://github.com/rust-lang/rust/pull/113126/) +- [Replace `private_in_public` lint with `private_interfaces` and `private_bounds` per RFC 2145.](https://github.com/rust-lang/rust/pull/113126/) Read more in [RFC 2145](https://rust-lang.github.io/rfcs/2145-type-privacy.html). - [Allow explicit `#[repr(Rust)]`](https://github.com/rust-lang/rust/pull/114201/) - [closure field capturing: don't depend on alignment of packed fields](https://github.com/rust-lang/rust/pull/115315/) - [Enable MIR-based drop-tracking for `async` blocks](https://github.com/rust-lang/rust/pull/107421/) +- [Stabilize `impl_trait_projections`](https://github.com/rust-lang/rust/pull/115659) <a id="1.74.0-Compiler"></a> @@ -46,8 +47,8 @@ Stabilized APIs - [`core::num::Saturating`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html) - [`impl From<io::Stdout> for std::process::Stdio`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CStdout%3E-for-Stdio) - [`impl From<io::Stderr> for std::process::Stdio`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CStderr%3E-for-Stdio) -- [`impl From<OwnedHandle> for std::process::Child{Stdin, Stdout, Stderr}`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CStderr%3E-for-Stdio) -- [`impl From<OwnedFd> for std::process::Child{Stdin, Stdout, Stderr}`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CStderr%3E-for-Stdio) +- [`impl From<OwnedHandle> for std::process::Child{Stdin, Stdout, Stderr}`](https://doc.rust-lang.org/stable/std/process/struct.ChildStderr.html#impl-From%3COwnedHandle%3E-for-ChildStderr) +- [`impl From<OwnedFd> for std::process::Child{Stdin, Stdout, Stderr}`](https://doc.rust-lang.org/stable/std/process/struct.ChildStderr.html#impl-From%3COwnedFd%3E-for-ChildStderr) - [`std::ffi::OsString::from_encoded_bytes_unchecked`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.from_encoded_bytes_unchecked) - [`std::ffi::OsString::into_encoded_bytes`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.into_encoded_bytes) - [`std::ffi::OsStr::from_encoded_bytes_unchecked`](https://doc.rust-lang.org/stable/std/ffi/struct.OsStr.html#method.from_encoded_bytes_unchecked) @@ -70,17 +71,17 @@ These APIs are now stable in const contexts: Cargo ----- -- [fix: Set MSRV for internal packages](https://github.com/rust-lang/cargo/pull/12381/) -- [config: merge lists in precedence order](https://github.com/rust-lang/cargo/pull/12515/) -- [fix(update): Clarify meaning of --aggressive as --recursive](https://github.com/rust-lang/cargo/pull/12544/) -- [fix(update): Make `-p` more convenient by being positional](https://github.com/rust-lang/cargo/pull/12545/) -- [feat(help): Add styling to help output ](https://github.com/rust-lang/cargo/pull/12578/) -- [feat(pkgid): Allow incomplete versions when unambigious](https://github.com/rust-lang/cargo/pull/12614/) -- [feat: stabilize credential-process and registry-auth](https://github.com/rust-lang/cargo/pull/12649/) -- [feat(cli): Add '-n' to dry-run](https://github.com/rust-lang/cargo/pull/12660/) +- [In `Cargo.toml`, stabilize `[lints]`](https://github.com/rust-lang/cargo/pull/12648/) +- [Stabilize credential-process and registry-auth](https://github.com/rust-lang/cargo/pull/12649/) +- [Stabilize `--keep-going` build flag](https://github.com/rust-lang/cargo/pull/12568/) +- [Add styling to `--help` output](https://github.com/rust-lang/cargo/pull/12578/) +- [For `cargo clean`, add `--dry-run` flag and summary line at the end](https://github.com/rust-lang/cargo/pull/12638) +- [For `cargo update`, make `--package` more convenient by being positional](https://github.com/rust-lang/cargo/pull/12545/) +- [For `cargo update`, clarify meaning of --aggressive as --recursive](https://github.com/rust-lang/cargo/pull/12544/) +- [Add '-n' as an alias for `--dry-run`](https://github.com/rust-lang/cargo/pull/12660/) +- [Allow version-prefixes in pkgid's (e.g. `--package` flags) to resolve ambiguities](https://github.com/rust-lang/cargo/pull/12614/) +- [In `.cargo/config.toml`, merge lists in precedence order](https://github.com/rust-lang/cargo/pull/12515/) - [Add support for `target.'cfg(..)'.linker`](https://github.com/rust-lang/cargo/pull/12535/) -- [Stabilize `--keep-going`](https://github.com/rust-lang/cargo/pull/12568/) -- [feat: Stabilize lints](https://github.com/rust-lang/cargo/pull/12648/) <a id="1.74.0-Rustdoc"></a> @@ -101,6 +102,7 @@ Compatibility Notes - [make Cell::swap panic if the Cells partially overlap](https://github.com/rust-lang/rust/pull/114795/) - [Reject invalid crate names in `--extern`](https://github.com/rust-lang/rust/pull/116001/) - [Don't resolve generic impls that may be shadowed by dyn built-in impls](https://github.com/rust-lang/rust/pull/114941/) +- [The new `impl From<{&,&mut} [T; N]> for Vec<T>` is known to cause some inference failures with overly-generic code.](https://github.com/rust-lang/rust/issues/117054) In those examples using the `tui` crate, the combination of `AsRef<_>` and `Into<Vec>` leaves the middle type ambiguous, and the new `impl` adds another possibility, so it now requires an explicit type annotation. <a id="1.74.0-Internal-Changes"></a> @@ -198,7 +200,6 @@ These APIs are now stable in const contexts: Cargo ----- -- [Encode URL params correctly for `SourceId` in `Cargo.lock`.](https://github.com/rust-lang/cargo/pull/12280/) - [Bail out an error when using `cargo::` in custom build script.](https://github.com/rust-lang/cargo/pull/12332/) <a id="1.73.0-Misc"></a> diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 09a87cf8e2f..eb42803f93e 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "nightly", feature(step_trait))] #![cfg_attr(feature = "nightly", allow(internal_features))] -#![cfg_attr(all(not(bootstrap), feature = "nightly"), doc(rust_logo))] -#![cfg_attr(all(not(bootstrap), feature = "nightly"), feature(rustdoc_internals))] +#![cfg_attr(feature = "nightly", doc(rust_logo))] +#![cfg_attr(feature = "nightly", feature(rustdoc_internals))] use std::fmt; use std::num::{NonZeroUsize, ParseIntError}; diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index a53fd4ae95a..bd35364509a 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -11,8 +11,8 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(no_crate_inject, attr(deny(warnings))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(new_uninit)] diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index be7d1b207bc..152ef4bcbfd 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -342,7 +342,7 @@ impl MetaItem { let span = span.with_hi(segments.last().unwrap().ident.span.hi()); Path { span, segments, tokens: None } } - Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt { + Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &nt.0 { token::Nonterminal::NtMeta(item) => return item.meta(item.path.span), token::Nonterminal::NtPath(path) => (**path).clone(), _ => return None, diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index c1f6ad6a27d..34303cbbc9a 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -8,9 +8,9 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(deny(warnings))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![allow(internal_features)] +#![feature(rustdoc_internals)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(const_trait_impl)] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7c0a78253a2..541b9872922 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -764,7 +764,10 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) { return; // Avoid visiting the span for the second time. } token::Interpolated(nt) => { - visit_nonterminal(Lrc::make_mut(nt), vis); + let nt = Lrc::make_mut(nt); + let (nt, sp) = (&mut nt.0, &mut nt.1); + vis.visit_span(sp); + visit_nonterminal(nt, vis); } _ => {} } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index a6ee93e8a6b..3189fcf7ff9 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -110,7 +110,7 @@ impl Lit { Ident(name, false) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)), Literal(token_lit) => Some(token_lit), Interpolated(ref nt) - if let NtExpr(expr) | NtLiteral(expr) = &**nt + if let NtExpr(expr) | NtLiteral(expr) = &nt.0 && let ast::ExprKind::Lit(token_lit) = expr.kind => { Some(token_lit) @@ -314,7 +314,7 @@ pub enum TokenKind { /// - It prevents `Token` from implementing `Copy`. /// It adds complexity and likely slows things down. Please don't add new /// occurrences of this token kind! - Interpolated(Lrc<Nonterminal>), + Interpolated(Lrc<(Nonterminal, Span)>), /// A doc comment token. /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc) @@ -388,7 +388,8 @@ impl TokenKind { match *self { Comma => Some(vec![Dot, Lt, Semi]), Semi => Some(vec![Colon, Comma]), - FatArrow => Some(vec![Eq, RArrow]), + Colon => Some(vec![Semi]), + FatArrow => Some(vec![Eq, RArrow, Ge, Gt]), _ => None, } } @@ -421,7 +422,7 @@ impl Token { /// if they keep spans or perform edition checks. pub fn uninterpolated_span(&self) -> Span { match &self.kind { - Interpolated(nt) => nt.span(), + Interpolated(nt) => nt.0.use_span(), _ => self.span, } } @@ -464,7 +465,7 @@ impl Token { ModSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes - Interpolated(ref nt) => matches!(**nt, NtLiteral(..) | + Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) | NtExpr(..) | NtBlock(..) | NtPath(..)), @@ -488,7 +489,7 @@ impl Token { | DotDot | DotDotDot | DotDotEq // ranges | Lt | BinOp(Shl) // associated path | ModSep => true, // global path - Interpolated(ref nt) => matches!(**nt, NtLiteral(..) | + Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) | NtPat(..) | NtBlock(..) | NtPath(..)), @@ -511,7 +512,7 @@ impl Token { Lifetime(..) | // lifetime bound in trait object Lt | BinOp(Shl) | // associated path ModSep => true, // global path - Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)), + Interpolated(ref nt) => matches!(&nt.0, NtTy(..) | NtPath(..)), // For anonymous structs or unions, which only appear in specific positions // (type of struct fields or union fields), we don't consider them as regular types _ => false, @@ -522,7 +523,7 @@ impl Token { pub fn can_begin_const_arg(&self) -> bool { match self.kind { OpenDelim(Delimiter::Brace) => true, - Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), + Interpolated(ref nt) => matches!(&nt.0, NtExpr(..) | NtBlock(..) | NtLiteral(..)), _ => self.can_begin_literal_maybe_minus(), } } @@ -576,7 +577,7 @@ impl Token { match self.uninterpolate().kind { Literal(..) | BinOp(Minus) => true, Ident(name, false) if name.is_bool_lit() => true, - Interpolated(ref nt) => match &**nt { + Interpolated(ref nt) => match &nt.0 { NtLiteral(_) => true, NtExpr(e) => match &e.kind { ast::ExprKind::Lit(_) => true, @@ -597,9 +598,9 @@ impl Token { /// otherwise returns the original token. pub fn uninterpolate(&self) -> Cow<'_, Token> { match &self.kind { - Interpolated(nt) => match **nt { + Interpolated(nt) => match &nt.0 { NtIdent(ident, is_raw) => { - Cow::Owned(Token::new(Ident(ident.name, is_raw), ident.span)) + Cow::Owned(Token::new(Ident(ident.name, *is_raw), ident.span)) } NtLifetime(ident) => Cow::Owned(Token::new(Lifetime(ident.name), ident.span)), _ => Cow::Borrowed(self), @@ -614,8 +615,8 @@ impl Token { // We avoid using `Token::uninterpolate` here because it's slow. match &self.kind { &Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)), - Interpolated(nt) => match **nt { - NtIdent(ident, is_raw) => Some((ident, is_raw)), + Interpolated(nt) => match &nt.0 { + NtIdent(ident, is_raw) => Some((*ident, *is_raw)), _ => None, }, _ => None, @@ -628,8 +629,8 @@ impl Token { // We avoid using `Token::uninterpolate` here because it's slow. match &self.kind { &Lifetime(name) => Some(Ident::new(name, self.span)), - Interpolated(nt) => match **nt { - NtLifetime(ident) => Some(ident), + Interpolated(nt) => match &nt.0 { + NtLifetime(ident) => Some(*ident), _ => None, }, _ => None, @@ -655,7 +656,7 @@ impl Token { /// Returns `true` if the token is an interpolated path. fn is_path(&self) -> bool { if let Interpolated(nt) = &self.kind - && let NtPath(..) = **nt + && let NtPath(..) = &nt.0 { return true; } @@ -668,7 +669,7 @@ impl Token { /// (which happens while parsing the result of macro expansion)? pub fn is_whole_expr(&self) -> bool { if let Interpolated(nt) = &self.kind - && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt + && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = &nt.0 { return true; } @@ -679,7 +680,7 @@ impl Token { /// Is the token an interpolated block (`$b:block`)? pub fn is_whole_block(&self) -> bool { if let Interpolated(nt) = &self.kind - && let NtBlock(..) = **nt + && let NtBlock(..) = &nt.0 { return true; } @@ -927,7 +928,7 @@ impl fmt::Display for NonterminalKind { } impl Nonterminal { - pub fn span(&self) -> Span { + pub fn use_span(&self) -> Span { match self { NtItem(item) => item.span, NtBlock(block) => block.span, @@ -941,6 +942,23 @@ impl Nonterminal { NtVis(vis) => vis.span, } } + + pub fn descr(&self) -> &'static str { + match self { + NtItem(..) => "item", + NtBlock(..) => "block", + NtStmt(..) => "statement", + NtPat(..) => "pattern", + NtExpr(..) => "expression", + NtLiteral(..) => "literal", + NtTy(..) => "type", + NtIdent(..) => "identifier", + NtLifetime(..) => "lifetime", + NtMeta(..) => "attribute", + NtPath(..) => "path", + NtVis(..) => "visibility", + } + } } impl PartialEq for Nonterminal { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 23b8f9c12d8..48854bbae24 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -477,13 +477,13 @@ impl TokenStream { fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree { match &token.kind { - token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = **nt => { + token::Interpolated(nt) if let token::NtIdent(ident, is_raw) = nt.0 => { TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing) } token::Interpolated(nt) => TokenTree::Delimited( DelimSpan::from_single(token.span), Delimiter::Invisible, - TokenStream::from_nonterminal_ast(nt).flattened(), + TokenStream::from_nonterminal_ast(&nt.0).flattened(), ), _ => TokenTree::Token(token.clone(), spacing), } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0fff9a6be92..2c13e1523f1 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -42,8 +42,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Merge attributes into the inner expression. if !e.attrs.is_empty() { - let old_attrs = - self.attrs.get(&ex.hir_id.local_id).map(|la| *la).unwrap_or(&[]); + let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); self.attrs.insert( ex.hir_id.local_id, &*self.arena.alloc_from_iter( @@ -792,8 +791,11 @@ impl<'hir> LoweringContext<'_, 'hir> { // debuggers and debugger extensions expect it to be called `__awaitee`. They use // this name to identify what is being awaited by a suspended async functions. let awaitee_ident = Ident::with_dummy_span(sym::__awaitee); - let (awaitee_pat, awaitee_pat_hid) = - self.pat_ident_binding_mode(span, awaitee_ident, hir::BindingAnnotation::MUT); + let (awaitee_pat, awaitee_pat_hid) = self.pat_ident_binding_mode( + gen_future_span, + awaitee_ident, + hir::BindingAnnotation::MUT, + ); let task_context_ident = Ident::with_dummy_span(sym::_task_context); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a88493acf98..3db9d0b31e0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -30,9 +30,9 @@ //! get confused if the spans from leaf AST nodes occur in multiple places //! in the HIR, especially for multiple identifiers. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![feature(box_patterns)] #![feature(let_chains)] #![feature(never_type)] @@ -443,11 +443,6 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { drop(ast_index); sess.time("drop_ast", || drop(krate)); - // Discard hygiene data, which isn't required after lowering to HIR. - if !sess.opts.unstable_opts.keep_hygiene_data { - rustc_span::hygiene::clear_syntax_context_map(); - } - // Don't hash unless necessary, because it's expensive. let opt_hir_hash = if tcx.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None }; @@ -504,7 +499,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name /// resolver (if any). fn orig_opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> { - self.resolver.node_id_to_def_id.get(&node).map(|local_def_id| *local_def_id) + self.resolver.node_id_to_def_id.get(&node).copied() } /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name @@ -547,7 +542,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.generics_def_id_map .iter() .rev() - .find_map(|map| map.get(&local_def_id).map(|local_def_id| *local_def_id)) + .find_map(|map| map.get(&local_def_id).copied()) .unwrap_or(local_def_id) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ce37f6677cf..f73d791db49 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1213,8 +1213,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => { errors::TildeConstReason::Function { ident: ident.span } } - &DisallowTildeConstContext::Trait(span) => errors::TildeConstReason::Trait { span }, - &DisallowTildeConstContext::Impl(span) => errors::TildeConstReason::Impl { span }, + &DisallowTildeConstContext::Trait(span) => { + errors::TildeConstReason::Trait { span } + } + &DisallowTildeConstContext::Impl(span) => { + errors::TildeConstReason::Impl { span } + } DisallowTildeConstContext::TraitObject => { errors::TildeConstReason::TraitObject } @@ -1446,9 +1450,7 @@ fn deny_equality_constraints( id: rustc_ast::node_id::DUMMY_NODE_ID, ident: *ident, gen_args, - kind: AssocConstraintKind::Equality { - term: predicate.rhs_ty.clone().into(), - }, + kind: AssocConstraintKind::Equality { term: predicate.rhs_ty.clone().into() }, span: ident.span, }); // Add `<Bar = RhsTy>` to `Foo`. @@ -1461,11 +1463,7 @@ fn deny_equality_constraints( }, empty_args => { *empty_args = Some( - AngleBracketedArgs { - span: ident.span, - args: thin_vec![arg], - } - .into(), + AngleBracketedArgs { span: ident.span, args: thin_vec![arg] }.into(), ); } } diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 5147e672f5f..6187258a950 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,9 +4,9 @@ //! //! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![allow(internal_features)] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index 475bdb02378..9e4ffd8dd6a 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -1,6 +1,6 @@ -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![feature(associated_type_bounds)] diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 48421ff7140..0962cb1b8d4 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -825,7 +825,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } token::Eof => "<eof>".into(), - token::Interpolated(ref nt) => self.nonterminal_to_string(nt).into(), + token::Interpolated(ref nt) => self.nonterminal_to_string(&nt.0).into(), } } diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index 868c0412255..5da5a3cb342 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,9 +4,9 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![feature(let_chains)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_baked_icu_data/src/lib.rs b/compiler/rustc_baked_icu_data/src/lib.rs index ae8c062d25d..ffcb290685a 100644 --- a/compiler/rustc_baked_icu_data/src/lib.rs +++ b/compiler/rustc_baked_icu_data/src/lib.rs @@ -20,9 +20,9 @@ //! --cldr-tag latest --icuexport-tag latest -o src/data //! ``` -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![allow(elided_lifetimes_in_paths)] mod data { diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index cfcf31fce32..03172ef34b9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -10,6 +10,8 @@ use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::RePlaceholder; +use rustc_middle::ty::Region; use rustc_middle::ty::RegionVid; use rustc_middle::ty::UniverseIndex; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -205,6 +207,8 @@ trait TypeOpInfo<'tcx> { let span = cause.span; let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region); + debug!(?nice_error); + if let Some(nice_error) = nice_error { mbcx.buffer_error(nice_error); } else { @@ -404,19 +408,41 @@ fn try_extract_error_from_region_constraints<'tcx>( mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin, mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - let (sub_region, cause) = - region_constraints.constraints.iter().find_map(|(constraint, cause)| { - match *constraint { - Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => { - Some((sub, cause.clone())) - } - // FIXME: Should this check the universe of the var? - Constraint::VarSubReg(vid, sup) if sup == placeholder_region => { - Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) - } - _ => None, + let matches = + |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { + (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, + _ => a_region == b_region, + }; + let check = |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| { + match *constraint { + Constraint::RegSubReg(sub, sup) + if ((exact && sup == placeholder_region) + || (!exact && matches(sup, placeholder_region))) + && sup != sub => + { + Some((sub, cause.clone())) + } + // FIXME: Should this check the universe of the var? + Constraint::VarSubReg(vid, sup) + if ((exact && sup == placeholder_region) + || (!exact && matches(sup, placeholder_region))) => + { + Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) } - })?; + _ => None, + } + }; + let mut info = region_constraints + .constraints + .iter() + .find_map(|(constraint, cause)| check(constraint, cause, true)); + if info.is_none() { + info = region_constraints + .constraints + .iter() + .find_map(|(constraint, cause)| check(constraint, cause, false)); + } + let (sub_region, cause) = info?; debug!(?sub_region, "cause = {:#?}", cause); let error = match (error_region, *sub_region) { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a8f1c97edf..875a71145fa 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1578,8 +1578,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return; }; let sig = args.as_closure().sig(); - let tupled_params = - tcx.erase_late_bound_regions(sig.inputs().iter().next().unwrap().map_bound(|&b| b)); + let tupled_params = tcx.instantiate_bound_regions_with_erased( + sig.inputs().iter().next().unwrap().map_bound(|&b| b), + ); let ty::Tuple(params) = tupled_params.kind() else { return }; // Find the first argument with a matching type, get its name diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 41d6b98d7cf..aec1cd504c1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -607,7 +607,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(adt) = local_ty.ty_adt_def() && adt.repr().packed() - && let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind + && let ExpnKind::Macro(MacroKind::Derive, name) = + self.body.span.ctxt().outer_expn_data().kind { err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour")); } diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index b6eb9ae980e..94981c45582 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -50,8 +50,8 @@ impl OutlivesSuggestionBuilder { // naming the `'self` lifetime in methods, etc. fn region_name_is_suggestable(name: &RegionName) -> bool { match name.source { - RegionNameSource::NamedEarlyBoundRegion(..) - | RegionNameSource::NamedFreeRegion(..) + RegionNameSource::NamedEarlyParamRegion(..) + | RegionNameSource::NamedLateParamRegion(..) | RegionNameSource::Static => true, // Don't give suggestions for upvars, closure return types, or other unnameable diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a0a809123c0..76c5a06c80d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -95,6 +95,12 @@ impl<'tcx> RegionErrors<'tcx> { } } +impl std::fmt::Debug for RegionErrors<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("RegionErrors").field(&self.0).finish() + } +} + #[derive(Clone, Debug)] pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). @@ -181,8 +187,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Returns `true` if a closure is inferred to be an `FnMut` closure. fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { - if let Some(ty::ReFree(free_region)) = self.to_error_region(fr).as_deref() - && let ty::BoundRegionKind::BrEnv = free_region.bound_region + if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref() + && let ty::BoundRegionKind::BrEnv = late_param.bound_region && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; @@ -995,7 +1001,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .infcx .tcx .is_suitable_region(sub) - .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) + .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.bound_region)) else { return; }; @@ -1004,7 +1010,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .infcx .tcx .is_suitable_region(sup) - .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) + .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.bound_region)) else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 9d6b0a0e91b..fee35485cd7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -23,14 +23,14 @@ pub(crate) struct RegionName { } /// Denotes the source of a region that is named by a `RegionName`. For example, a free region that -/// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`. +/// was named by the user would get `NamedLateParamRegion` and `'static` lifetime would get `Static`. /// This helps to print the right kinds of diagnostics. #[derive(Debug, Clone)] pub(crate) enum RegionNameSource { /// A bound (not free) region that was instantiated at the def site (not an HRTB). - NamedEarlyBoundRegion(Span), + NamedEarlyParamRegion(Span), /// A free region that the user has a name (`'a`) for. - NamedFreeRegion(Span), + NamedLateParamRegion(Span), /// The `'static` region. Static, /// The free region corresponding to the environment of a closure. @@ -69,8 +69,8 @@ pub(crate) enum RegionNameHighlight { impl RegionName { pub(crate) fn was_named(&self) -> bool { match self.source { - RegionNameSource::NamedEarlyBoundRegion(..) - | RegionNameSource::NamedFreeRegion(..) + RegionNameSource::NamedEarlyParamRegion(..) + | RegionNameSource::NamedLateParamRegion(..) | RegionNameSource::Static => true, RegionNameSource::SynthesizedFreeEnvRegion(..) | RegionNameSource::AnonRegionFromArgument(..) @@ -85,8 +85,8 @@ impl RegionName { pub(crate) fn span(&self) -> Option<Span> { match self.source { RegionNameSource::Static => None, - RegionNameSource::NamedEarlyBoundRegion(span) - | RegionNameSource::NamedFreeRegion(span) + RegionNameSource::NamedEarlyParamRegion(span) + | RegionNameSource::NamedLateParamRegion(span) | RegionNameSource::SynthesizedFreeEnvRegion(span, _) | RegionNameSource::AnonRegionFromUpvar(span, _) | RegionNameSource::AnonRegionFromYieldTy(span, _) @@ -104,8 +104,8 @@ impl RegionName { pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) { match &self.source { - RegionNameSource::NamedFreeRegion(span) - | RegionNameSource::NamedEarlyBoundRegion(span) => { + RegionNameSource::NamedLateParamRegion(span) + | RegionNameSource::NamedEarlyParamRegion(span) => { diag.span_label(*span, format!("lifetime `{self}` defined here")); } RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { @@ -280,28 +280,31 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { debug!("give_region_a_name: error_region = {:?}", error_region); match *error_region { - ty::ReEarlyBound(ebr) => ebr.has_name().then(|| { + ty::ReEarlyParam(ebr) => ebr.has_name().then(|| { let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP); - RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyBoundRegion(span) } + RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) } }), ty::ReStatic => { Some(RegionName { name: kw::StaticLifetime, source: RegionNameSource::Static }) } - ty::ReFree(free_region) => match free_region.bound_region { + ty::ReLateParam(late_param) => match late_param.bound_region { ty::BoundRegionKind::BrNamed(region_def_id, name) => { // Get the span to point to, even if we don't use the name. let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP); debug!( "bound region named: {:?}, is_named: {:?}", name, - free_region.bound_region.is_named() + late_param.bound_region.is_named() ); - if free_region.bound_region.is_named() { + if late_param.bound_region.is_named() { // A named region that is actually named. - Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) }) + Some(RegionName { + name, + source: RegionNameSource::NamedLateParamRegion(span), + }) } else if tcx.asyncness(self.mir_hir_id().owner).is_async() { // If we spuriously thought that the region is named, we should let the // system generate a true name for error messages. Currently this can @@ -847,7 +850,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, fr: RegionVid, ) -> Option<RegionName> { - let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else { return None; }; if region.has_name() { @@ -862,7 +865,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let found = tcx .any_free_region_meets(&tcx.type_of(region_parent).instantiate_identity(), |r| { - *r == ty::ReEarlyBound(region) + *r == ty::ReEarlyParam(region) }); Some(RegionName { @@ -881,7 +884,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, fr: RegionVid, ) -> Option<RegionName> { - let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else { return None; }; if region.has_name() { @@ -943,7 +946,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, clauses: &[ty::Clause<'tcx>], ty: Ty<'tcx>, - region: ty::EarlyBoundRegion, + region: ty::EarlyParamRegion, ) -> bool { let tcx = self.infcx.tcx; ty.walk().any(|arg| { @@ -956,7 +959,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {} _ => return false, } - tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyBound(region)) + tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyParam(region)) }) } else { false diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5787ea13e79..353e4cf0829 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1,8 +1,8 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. #![allow(internal_features)] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(let_chains)] diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b1f91a05628..9e35433bb7e 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -151,6 +151,7 @@ pub(crate) struct AppliedMemberConstraint { pub(crate) member_constraint_index: NllMemberConstraintIndex, } +#[derive(Debug)] pub(crate) struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential /// variable? etc. (See the `NllRegionVariableOrigin` for more @@ -687,6 +688,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { &mut errors_buffer, ); + debug!(?errors_buffer); + debug!(?outlives_requirements); + // In Polonius mode, the errors about missing universal region relations are in the output // and need to be emitted or propagated. Otherwise, we need to check whether the // constraints were too strong, and if so, emit or propagate those errors. @@ -700,10 +704,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer); } + debug!(?errors_buffer); + if errors_buffer.is_empty() { self.check_member_constraints(infcx, &mut errors_buffer); } + debug!(?errors_buffer); + let outlives_requirements = outlives_requirements.unwrap_or_default(); if outlives_requirements.is_empty() { @@ -1457,6 +1465,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer: &mut RegionErrors<'tcx>, ) { for (fr, fr_definition) in self.definitions.iter_enumerated() { + debug!(?fr, ?fr_definition); match fr_definition.origin { NllRegionVariableOrigin::FreeRegion => { // Go through each of the universal regions `fr` and check that diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index fb0e5811c26..c93cfa78832 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -36,7 +36,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// call `infer_opaque_definition_from_instantiation` to get the inferred /// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation` /// compares lifetimes directly, so we need to map the inference variables - /// back to concrete lifetimes: `'static`, `ReEarlyBound` or `ReFree`. + /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`. /// /// First we map all the lifetimes in the concrete type to an equal /// universal region that occurs in the concrete type's args, in this case @@ -386,7 +386,7 @@ fn check_opaque_type_parameter_valid( let arg_is_param = match arg.unpack() { GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(lt) if is_ty_alias => { - matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_)) } // FIXME(#113916): we can't currently check for unique lifetime params, // see that issue for more. We will also have to ignore unused lifetime diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index c84256f8ff3..478eedc26d3 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -7,7 +7,7 @@ use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; -use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; @@ -321,6 +321,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); self.add_outlives_bounds(bounds); constraints } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index bd01210544f..5d9fd2647f2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1202,7 +1202,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.infcx.tcx } - #[instrument(skip(self, body, location), level = "debug")] + #[instrument(skip(self, body), level = "debug")] fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { let tcx = self.tcx(); debug!("stmt kind: {:?}", stmt.kind); @@ -1387,7 +1387,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } }; - let (sig, map) = tcx.replace_late_bound_regions(sig, |br| { + let (sig, map) = tcx.instantiate_bound_regions(sig, |br| { use crate::renumber::RegionCtxt; let region_ctxt_fn = || { diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index ae7f9c5244c..abeb021adc8 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -462,7 +462,6 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { // "Liberate" the late-bound regions. These correspond to // "local" free regions. - let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars( @@ -739,13 +738,13 @@ trait InferCtxtExt<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>; - fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope( + fn instantiate_bound_regions_with_nll_infer_vars_in_recursive_scope( &self, mir_def_id: LocalDefId, indices: &mut UniversalRegionIndices<'tcx>, ); - fn replace_late_bound_regions_with_nll_infer_vars_in_item( + fn instantiate_bound_regions_with_nll_infer_vars_in_item( &self, mir_def_id: LocalDefId, indices: &mut UniversalRegionIndices<'tcx>, @@ -781,10 +780,10 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| { + let (value, _map) = self.tcx.instantiate_bound_regions(value, |br| { debug!(?br); let liberated_region = - ty::Region::new_free(self.tcx, all_outlive_scope.to_def_id(), br.kind); + ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), br.kind); let region_vid = { let name = match br.kind.get_name() { Some(name) => name, @@ -811,7 +810,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { /// set of late-bound regions and checks for any that we have not yet seen, adding them to the /// inputs vector. #[instrument(skip(self, indices))] - fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope( + fn instantiate_bound_regions_with_nll_infer_vars_in_recursive_scope( &self, mir_def_id: LocalDefId, indices: &mut UniversalRegionIndices<'tcx>, @@ -831,7 +830,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { } #[instrument(skip(self, indices))] - fn replace_late_bound_regions_with_nll_infer_vars_in_item( + fn instantiate_bound_regions_with_nll_infer_vars_in_item( &self, mir_def_id: LocalDefId, indices: &mut UniversalRegionIndices<'tcx>, @@ -854,7 +853,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { /// Initially, the `UniversalRegionIndices` map contains only the /// early-bound regions in scope. Once that is all setup, we come /// in later and instantiate the late-bound regions, and then we - /// insert the `ReFree` version of those into the map as + /// insert the `ReLateParam` version of those into the map as /// well. These are used for error reporting. fn insert_late_bound_region(&mut self, r: ty::Region<'tcx>, vid: ty::RegionVid) { debug!("insert_late_bound_region({:?}, {:?})", r, vid); @@ -933,7 +932,8 @@ fn for_each_late_bound_region_in_item<'tcx>( let ty::BoundVariableKind::Region(bound_region) = bound_var else { continue; }; - let liberated_region = ty::Region::new_free(tcx, mir_def_id.to_def_id(), bound_region); + let liberated_region = + ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), bound_region); f(liberated_region); } } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 214fed8e2d8..8a3e15ee24a 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -617,10 +617,17 @@ fn report_missing_placeholders( let placeholders = pieces .iter() .filter_map(|piece| { - if let parse::Piece::NextArgument(argument) = piece && let ArgumentNamed(binding) = argument.position { - let span = fmt_span.from_inner(InnerSpan::new(argument.position_span.start, argument.position_span.end)); + if let parse::Piece::NextArgument(argument) = piece + && let ArgumentNamed(binding) = argument.position + { + let span = fmt_span.from_inner(InnerSpan::new( + argument.position_span.start, + argument.position_span.end, + )); Some((span, binding)) - } else { None } + } else { + None + } }) .collect::<Vec<_>>(); diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index d84742c9b82..ae097dec8f1 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -1,9 +1,9 @@ //! This crate contains implementations of built-in macros and other code generating facilities //! injecting code into the crate before it is lowered to HIR. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] #![feature(box_patterns)] diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index 12aa69d3c79..bd3b051185b 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -35,6 +35,10 @@ jobs: steps: - uses: actions/checkout@v3 + - name: CPU features + if: matrix.os == 'ubuntu-latest' + run: cat /proc/cpuinfo + - name: Cache cargo target dir uses: actions/cache@v3 with: diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 47d9a3b93f7..05dc28d0745 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -66,6 +66,10 @@ jobs: steps: - uses: actions/checkout@v3 + - name: CPU features + if: matrix.os == 'ubuntu-latest' + run: cat /proc/cpuinfo + - name: Cache cargo target dir uses: actions/cache@v3 with: @@ -136,6 +140,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: CPU features + run: cat /proc/cpuinfo + - name: Prepare dependencies run: ./y.sh prepare @@ -159,6 +166,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: CPU features + run: cat /proc/cpuinfo + - name: Cache cargo target dir uses: actions/cache@v3 with: diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml index b49dc3aff7a..cb5dd51fee3 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -11,6 +11,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: CPU features + run: cat /proc/cpuinfo + - name: Cache cargo target dir uses: actions/cache@v3 with: @@ -31,6 +34,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: CPU features + run: cat /proc/cpuinfo + - name: Cache cargo target dir uses: actions/cache@v3 with: diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 1a2b2bbc588..ca6ecdf1d0e 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -5,8 +5,48 @@ This has the potential to improve compilation times in debug mode. If your project doesn't use any of the things listed under "Not yet supported", it should work fine. If not please open an issue. +## Download using Rustup + +The Cranelift codegen backend is distributed in nightly builds on Linux and x86_64 macOS. If you want to +install it using Rustup, you can do that by running: + +```bash +$ 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] + codegen-backend = true + + [profile.dev] + codegen-backend = "cranelift" + ``` +- Add the following to `Cargo.toml`: + ```toml + # This line needs to come before anything else in Cargo.toml + cargo-features = ["codegen-backend"] + + [profile.dev] + codegen-backend = "cranelift" + ``` + +## Precompiled builds + +You can also download a pre-built version from the [releases] page. +Extract the `dist` directory in the archive anywhere you want. +If you want to use `cargo clif build` instead of having to specify the full path to the `cargo-clif` executable, you can add the `bin` subdirectory of the extracted `dist` directory to your `PATH`. +(tutorial [for Windows](https://stackoverflow.com/a/44272417), and [for Linux/MacOS](https://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path/26059#26059)). + +[releases]: https://github.com/rust-lang/rustc_codegen_cranelift/releases/tag/dev + ## Building and testing +If you want to build the backend manually, you can download it from GitHub and build it yourself: + ```bash $ git clone https://github.com/rust-lang/rustc_codegen_cranelift $ cd rustc_codegen_cranelift @@ -22,15 +62,6 @@ $ ./test.sh For more docs on how to build and test see [build_system/usage.txt](build_system/usage.txt) or the help message of `./y.sh`. -## Precompiled builds - -Alternatively you can download a pre built version from the [releases] page. -Extract the `dist` directory in the archive anywhere you want. -If you want to use `cargo clif build` instead of having to specify the full path to the `cargo-clif` executable, you can add the `bin` subdirectory of the extracted `dist` directory to your `PATH`. -(tutorial [for Windows](https://stackoverflow.com/a/44272417), and [for Linux/MacOS](https://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path/26059#26059)). - -[releases]: https://github.com/rust-lang/rustc_codegen_cranelift/releases/tag/dev - ## Usage rustc_codegen_cranelift can be used as a near-drop-in replacement for `cargo build` or `cargo run` for existing projects. diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 10736ff9a55..ff71a567ed3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -457,6 +457,8 @@ impl<'a> TestRunner<'a> { cmd.arg("--target"); cmd.arg(&self.target_compiler.triple); cmd.arg("-Cpanic=abort"); + cmd.arg("--check-cfg=cfg(no_unstable_features)"); + cmd.arg("--check-cfg=cfg(jit)"); cmd.args(args); cmd } diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index b832b06e0ff..80ef1e49f23 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-11-10" +channel = "nightly-2023-11-16" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index e62788f2e50..355282911c2 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -46,7 +46,7 @@ case $1 in git pull origin master branch=sync_cg_clif-$(date +%Y-%m-%d) git checkout -b "$branch" - "$cg_clif/git-fixed-subtree.sh" pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/bjorn3/rustc_codegen_cranelift.git master + "$cg_clif/git-fixed-subtree.sh" pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/rust-lang/rustc_codegen_cranelift.git master git push -u my "$branch" # immediately merge the merge commit into cg_clif to prevent merge conflicts when syncing diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index bbb8a010d96..731828caae2 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -1,15 +1,17 @@ #!/usr/bin/env bash set -e +# CG_CLIF_FORCE_GNU_AS will force usage of as instead of the LLVM backend of rustc as we +# the LLVM backend isn't compiled in here. +export CG_CLIF_FORCE_GNU_AS=1 + # Compiletest expects all standard library paths to start with /rustc/FAKE_PREFIX. # CG_CLIF_STDLIB_REMAP_PATH_PREFIX will cause cg_clif's build system to pass # --remap-path-prefix to handle this. -# CG_CLIF_FORCE_GNU_AS will force usage of as instead of the LLVM backend of rustc as we -# the LLVM backend isn't compiled in here. -CG_CLIF_FORCE_GNU_AS=1 CG_CLIF_STDLIB_REMAP_PATH_PREFIX=/rustc/FAKE_PREFIX ./y.sh build +CG_CLIF_STDLIB_REMAP_PATH_PREFIX=/rustc/FAKE_PREFIX ./y.sh build echo "[SETUP] Rust fork" -git clone https://github.com/rust-lang/rust.git || true +git clone https://github.com/rust-lang/rust.git --filter=tree:0 || true pushd rust git fetch git checkout -- . diff --git a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh index a8f6d7a2024..791d457993d 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh @@ -11,7 +11,5 @@ rm -r compiler/rustc_codegen_cranelift/{Cargo.*,src} cp ../Cargo.* compiler/rustc_codegen_cranelift/ cp -r ../src compiler/rustc_codegen_cranelift/src -# CG_CLIF_FORCE_GNU_AS will force usage of as instead of the LLVM backend of rustc as we -# the LLVM backend isn't compiled in here. -CG_CLIF_FORCE_GNU_AS=1 ./x.py build --stage 1 library/std +./x.py build --stage 1 library/std popd diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index c4572e03525..0ff1473da43 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -383,6 +383,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( args, ret_place, target, + source_info.span, ); return; } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 91b1547cb6e..71557d49ef2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -456,7 +456,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } - crate::inline_asm::codegen_inline_asm( + crate::inline_asm::codegen_inline_asm_terminator( fx, source_info.span, template, diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index b0853d30e03..cf68a3857c5 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -1,10 +1,13 @@ //! Handling of `static`s, `const`s and promoted allocations +use std::cmp::Ordering; + use cranelift_module::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{read_target_uint, AllocId, GlobalAlloc, Scalar}; use rustc_middle::mir::ConstValue; +use rustc_middle::ty::ScalarInt; use crate::prelude::*; @@ -430,9 +433,9 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant pub(crate) fn mir_operand_get_const_val<'tcx>( fx: &FunctionCx<'_, '_, 'tcx>, operand: &Operand<'tcx>, -) -> Option<ConstValue<'tcx>> { +) -> Option<ScalarInt> { match operand { - Operand::Constant(const_) => Some(eval_mir_constant(fx, const_).0), + Operand::Constant(const_) => eval_mir_constant(fx, const_).0.try_to_scalar_int(), // FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored // inside a temporary before being passed to the intrinsic requiring the const argument. // This code tries to find a single constant defining definition of the referenced local. @@ -440,7 +443,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( if !place.projection.is_empty() { return None; } - let mut computed_const_val = None; + let mut computed_scalar_int = None; for bb_data in fx.mir.basic_blocks.iter() { for stmt in &bb_data.statements { match &stmt.kind { @@ -456,22 +459,38 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( operand, ty, ) => { - if computed_const_val.is_some() { + if computed_scalar_int.is_some() { return None; // local assigned twice } if !matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) { return None; } - let const_val = mir_operand_get_const_val(fx, operand)?; - if fx.layout_of(*ty).size - != const_val.try_to_scalar_int()?.size() + let scalar_int = mir_operand_get_const_val(fx, operand)?; + let scalar_int = match fx + .layout_of(*ty) + .size + .cmp(&scalar_int.size()) { - return None; - } - computed_const_val = Some(const_val); + Ordering::Equal => scalar_int, + Ordering::Less => match ty.kind() { + ty::Uint(_) => ScalarInt::try_from_uint( + scalar_int.try_to_uint(scalar_int.size()).unwrap(), + fx.layout_of(*ty).size, + ) + .unwrap(), + ty::Int(_) => ScalarInt::try_from_int( + scalar_int.try_to_int(scalar_int.size()).unwrap(), + fx.layout_of(*ty).size, + ) + .unwrap(), + _ => unreachable!(), + }, + Ordering::Greater => return None, + }; + computed_scalar_int = Some(scalar_int); } Rvalue::Use(operand) => { - computed_const_val = mir_operand_get_const_val(fx, operand) + computed_scalar_int = mir_operand_get_const_val(fx, operand) } _ => return None, } @@ -522,7 +541,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( TerminatorKind::Call { .. } => {} } } - computed_const_val + computed_scalar_int } } } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index ce0eecca8a8..25d14319f57 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -10,7 +10,7 @@ use target_lexicon::BinaryFormat; use crate::prelude::*; -enum CInlineAsmOperand<'tcx> { +pub(crate) enum CInlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, value: Value, @@ -34,7 +34,7 @@ enum CInlineAsmOperand<'tcx> { }, } -pub(crate) fn codegen_inline_asm<'tcx>( +pub(crate) fn codegen_inline_asm_terminator<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span, template: &[InlineAsmTemplatePiece], @@ -42,8 +42,6 @@ pub(crate) fn codegen_inline_asm<'tcx>( options: InlineAsmOptions, destination: Option<mir::BasicBlock>, ) { - // FIXME add .eh_frame unwind info directives - // Used by panic_abort on Windows, but uses a syntax which only happens to work with // asm!() by accident and breaks with the GNU assembler as well as global_asm!() for // the LLVM backend. @@ -135,15 +133,33 @@ pub(crate) fn codegen_inline_asm<'tcx>( }) .collect::<Vec<_>>(); - let mut inputs = Vec::new(); - let mut outputs = Vec::new(); + codegen_inline_asm_inner(fx, template, &operands, options); + + match destination { + Some(destination) => { + let destination_block = fx.get_block(destination); + fx.bcx.ins().jump(destination_block, &[]); + } + None => { + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + } + } +} + +pub(crate) fn codegen_inline_asm_inner<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + template: &[InlineAsmTemplatePiece], + operands: &[CInlineAsmOperand<'tcx>], + options: InlineAsmOptions, +) { + // FIXME add .eh_frame unwind info directives let mut asm_gen = InlineAssemblyGenerator { tcx: fx.tcx, arch: fx.tcx.sess.asm_arch.unwrap(), enclosing_def_id: fx.instance.def_id(), template, - operands: &operands, + operands, options, registers: Vec::new(), stack_slots_clobber: Vec::new(), @@ -165,6 +181,8 @@ pub(crate) fn codegen_inline_asm<'tcx>( let generated_asm = asm_gen.generate_asm_wrapper(&asm_name); fx.cx.global_asm.push_str(&generated_asm); + let mut inputs = Vec::new(); + let mut outputs = Vec::new(); for (i, operand) in operands.iter().enumerate() { match operand { CInlineAsmOperand::In { reg: _, value } => { @@ -186,16 +204,6 @@ pub(crate) fn codegen_inline_asm<'tcx>( } call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs); - - match destination { - Some(destination) => { - let destination_block = fx.get_block(destination); - fx.bcx.ins().jump(destination_block, &[]); - } - None => { - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); - } - } } struct InlineAssemblyGenerator<'a, 'tcx> { @@ -637,8 +645,21 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { ) { match arch { InlineAsmArch::X86_64 => { - write!(generated_asm, " mov [rbx+0x{:x}], ", offset.bytes()).unwrap(); - reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap(); + match reg { + InlineAsmReg::X86(reg) + if reg as u32 >= X86InlineAsmReg::xmm0 as u32 + && reg as u32 <= X86InlineAsmReg::xmm15 as u32 => + { + // rustc emits x0 rather than xmm0 + write!(generated_asm, " movups [rbx+0x{:x}], ", offset.bytes()).unwrap(); + write!(generated_asm, "xmm{}", reg as u32 - X86InlineAsmReg::xmm0 as u32) + .unwrap(); + } + _ => { + write!(generated_asm, " mov [rbx+0x{:x}], ", offset.bytes()).unwrap(); + reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap(); + } + } generated_asm.push('\n'); } InlineAsmArch::AArch64 => { @@ -663,8 +684,24 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { ) { match arch { InlineAsmArch::X86_64 => { - generated_asm.push_str(" mov "); - reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap(); + match reg { + InlineAsmReg::X86(reg) + if reg as u32 >= X86InlineAsmReg::xmm0 as u32 + && reg as u32 <= X86InlineAsmReg::xmm15 as u32 => + { + // rustc emits x0 rather than xmm0 + write!( + generated_asm, + " movups xmm{}", + reg as u32 - X86InlineAsmReg::xmm0 as u32 + ) + .unwrap(); + } + _ => { + generated_asm.push_str(" mov "); + reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap() + } + } writeln!(generated_asm, ", [rbx+0x{:x}]", offset.bytes()).unwrap(); } InlineAsmArch::AArch64 => { @@ -720,7 +757,12 @@ fn call_inline_asm<'tcx>( fx.bcx.ins().call(inline_asm_func, &[stack_slot_addr]); for (offset, place) in outputs { - let ty = fx.clif_type(place.layout().ty).unwrap(); + let ty = if place.layout().ty.is_simd() { + let (lane_count, lane_type) = place.layout().ty.simd_size_and_type(fx.tcx); + fx.clif_type(lane_type).unwrap().by(lane_count.try_into().unwrap()).unwrap() + } else { + fx.clif_type(place.layout().ty).unwrap() + }; let value = stack_slot.offset(fx, i32::try_from(offset.bytes()).unwrap().into()).load( fx, ty, @@ -729,83 +771,3 @@ fn call_inline_asm<'tcx>( place.write_cvalue(fx, CValue::by_val(value, place.layout())); } } - -pub(crate) fn codegen_xgetbv<'tcx>( - fx: &mut FunctionCx<'_, '_, 'tcx>, - xcr_no: Value, - ret: CPlace<'tcx>, -) { - // FIXME add .eh_frame unwind info directives - - let operands = vec![ - CInlineAsmOperand::In { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)), - value: xcr_no, - }, - CInlineAsmOperand::Out { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)), - late: true, - place: Some(ret), - }, - CInlineAsmOperand::Out { - reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)), - late: true, - place: None, - }, - ]; - let options = InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM; - - let mut inputs = Vec::new(); - let mut outputs = Vec::new(); - - let mut asm_gen = InlineAssemblyGenerator { - tcx: fx.tcx, - arch: fx.tcx.sess.asm_arch.unwrap(), - enclosing_def_id: fx.instance.def_id(), - template: &[InlineAsmTemplatePiece::String( - " - xgetbv - // out = rdx << 32 | rax - shl rdx, 32 - or rax, rdx - " - .to_string(), - )], - operands: &operands, - options, - registers: Vec::new(), - stack_slots_clobber: Vec::new(), - stack_slots_input: Vec::new(), - stack_slots_output: Vec::new(), - stack_slot_size: Size::from_bytes(0), - }; - asm_gen.allocate_registers(); - asm_gen.allocate_stack_slots(); - - let inline_asm_index = fx.cx.inline_asm_index.get(); - fx.cx.inline_asm_index.set(inline_asm_index + 1); - let asm_name = format!( - "__inline_asm_{}_n{}", - fx.cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - inline_asm_index - ); - - let generated_asm = asm_gen.generate_asm_wrapper(&asm_name); - fx.cx.global_asm.push_str(&generated_asm); - - for (i, operand) in operands.iter().enumerate() { - match operand { - CInlineAsmOperand::In { reg: _, value } => { - inputs.push((asm_gen.stack_slots_input[i].unwrap(), *value)); - } - CInlineAsmOperand::Out { reg: _, late: _, place } => { - if let Some(place) = place { - outputs.push((asm_gen.stack_slots_output[i].unwrap(), *place)); - } - } - _ => unreachable!(), - } - } - - call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs); -} diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index e9b7daf1492..659e6c133ef 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -12,6 +12,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( args: &[mir::Operand<'tcx>], ret: CPlace<'tcx>, target: Option<BasicBlock>, + span: Span, ) { if intrinsic.starts_with("llvm.aarch64") { return llvm_aarch64::codegen_aarch64_llvm_intrinsic_call( @@ -31,6 +32,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( args, ret, target, + span, ); } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 4c536048626..8dd2b6ed014 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -1,7 +1,10 @@ //! Emulate x86 LLVM intrinsics +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::ty::GenericArgsRef; +use rustc_target::asm::*; +use crate::inline_asm::{codegen_inline_asm_inner, CInlineAsmOperand}; use crate::intrinsics::*; use crate::prelude::*; @@ -12,6 +15,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( args: &[mir::Operand<'tcx>], ret: CPlace<'tcx>, target: Option<BasicBlock>, + span: Span, ) { match intrinsic { "llvm.x86.sse2.pause" | "llvm.aarch64.isb" => { @@ -24,7 +28,35 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let xcr_no = xcr_no.load_scalar(fx); - crate::inline_asm::codegen_xgetbv(fx, xcr_no, ret); + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + " + xgetbv + // out = rdx << 32 | rax + shl rdx, 32 + or rax, rdx + " + .to_string(), + )], + &[ + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)), + value: xcr_no, + }, + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)), + late: true, + place: Some(ret), + }, + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)), + late: true, + place: None, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); } "llvm.x86.sse3.ldu.dq" | "llvm.x86.avx.ldu.dq.256" => { @@ -688,64 +720,278 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( "llvm.x86.pclmulqdq" => { // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128&ig_expand=772 - intrinsic_args!(fx, args => (a, b, imm8); intrinsic); + intrinsic_args!(fx, args => (a, b, _imm8); intrinsic); - assert_eq!(a.layout(), b.layout()); - let layout = a.layout(); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); - 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_eq!(lane_ty, fx.tcx.types.i64); - assert_eq!(ret_lane_ty, fx.tcx.types.i64); - assert_eq!(lane_count, 2); - assert_eq!(ret_lane_count, 2); + let imm8 = if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2]) + { + imm8 + } else { + fx.tcx.sess.span_fatal( + span, + "Index argument for `_mm_clmulepi64_si128` is not a constant", + ); + }; - let imm8 = imm8.load_scalar(fx); + let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); - let control0 = fx.bcx.ins().band_imm(imm8, 0b0000_0001); - let a_lane0 = a.value_lane(fx, 0).load_scalar(fx); - let a_lane1 = a.value_lane(fx, 1).load_scalar(fx); - let temp1 = fx.bcx.ins().select(control0, a_lane1, a_lane0); + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(format!("pclmulqdq xmm0, xmm1, {imm8}"))], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } - let control4 = fx.bcx.ins().band_imm(imm8, 0b0001_0000); - let b_lane0 = b.value_lane(fx, 0).load_scalar(fx); - let b_lane1 = b.value_lane(fx, 1).load_scalar(fx); - let temp2 = fx.bcx.ins().select(control4, b_lane1, b_lane0); + "llvm.x86.aesni.aeskeygenassist" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aeskeygenassist_si128&ig_expand=261 + intrinsic_args!(fx, args => (a, _imm8); intrinsic); - fn extract_bit(fx: &mut FunctionCx<'_, '_, '_>, val: Value, bit: i64) -> Value { - let tmp = fx.bcx.ins().ushr_imm(val, bit); - fx.bcx.ins().band_imm(tmp, 1) - } + let a = a.load_scalar(fx); - let mut res1 = fx.bcx.ins().iconst(types::I64, 0); - for i in 0..=63 { - let x = extract_bit(fx, temp1, 0); - let y = extract_bit(fx, temp2, i); - let mut temp = fx.bcx.ins().band(x, y); - for j in 1..=i { - let x = extract_bit(fx, temp1, j); - let y = extract_bit(fx, temp2, i - j); - let z = fx.bcx.ins().band(x, y); - temp = fx.bcx.ins().bxor(temp, z); - } - let temp = fx.bcx.ins().ishl_imm(temp, i); - res1 = fx.bcx.ins().bor(res1, temp); - } - ret.place_lane(fx, 0).to_ptr().store(fx, res1, MemFlags::trusted()); - - let mut res2 = fx.bcx.ins().iconst(types::I64, 0); - for i in 64..=127 { - let mut temp = fx.bcx.ins().iconst(types::I64, 0); - for j in i - 63..=63 { - let x = extract_bit(fx, temp1, j); - let y = extract_bit(fx, temp2, i - j); - let z = fx.bcx.ins().band(x, y); - temp = fx.bcx.ins().bxor(temp, z); - } - let temp = fx.bcx.ins().ishl_imm(temp, i); - res2 = fx.bcx.ins().bor(res2, temp); - } - ret.place_lane(fx, 1).to_ptr().store(fx, res2, MemFlags::trusted()); + let imm8 = if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[1]) + { + imm8 + } else { + fx.tcx.sess.span_fatal( + span, + "Index argument for `_mm_aeskeygenassist_si128` is not a constant", + ); + }; + + let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(format!("aeskeygenassist xmm0, xmm0, {imm8}"))], + &[CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.aesni.aesimc" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesimc_si128&ig_expand=260 + intrinsic_args!(fx, args => (a); intrinsic); + + let a = a.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("aesimc xmm0, xmm0".to_string())], + &[CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.aesni.aesenc" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128&ig_expand=252 + intrinsic_args!(fx, args => (a, round_key); intrinsic); + + let a = a.load_scalar(fx); + let round_key = round_key.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("aesenc xmm0, xmm1".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + value: round_key, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.aesni.aesenclast" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128&ig_expand=257 + intrinsic_args!(fx, args => (a, round_key); intrinsic); + + let a = a.load_scalar(fx); + let round_key = round_key.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("aesenclast xmm0, xmm1".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + value: round_key, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.aesni.aesdec" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128&ig_expand=242 + intrinsic_args!(fx, args => (a, round_key); intrinsic); + + let a = a.load_scalar(fx); + let round_key = round_key.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("aesdec xmm0, xmm1".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + value: round_key, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.aesni.aesdeclast" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128&ig_expand=247 + intrinsic_args!(fx, args => (a, round_key); intrinsic); + + let a = a.load_scalar(fx); + let round_key = round_key.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("aesdeclast xmm0, xmm1".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + value: round_key, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.sha256rnds2" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sha256rnds2_epu32&ig_expand=5977 + intrinsic_args!(fx, args => (a, b, k); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let k = k.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256rnds2 xmm1, xmm2".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm2)), + value: b, + }, + // Implicit argument to the sha256rnds2 instruction + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + value: k, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.sha256msg1" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sha256msg1_epu32&ig_expand=5975 + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256msg1 xmm1, xmm2".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm2)), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + + "llvm.x86.sha256msg2" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sha256msg2_epu32&ig_expand=5976 + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("sha256msg2 xmm1, xmm2".to_string())], + &[ + CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm1)), + _late: true, + in_value: a, + out_place: Some(ret), + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm2)), + value: b, + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); } "llvm.x86.avx.ptestz.256" => { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index ea137c4ca1e..0bd211fd614 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -282,11 +282,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.tcx.sess.span_fatal(span, "Index argument for `simd_insert` is not a constant"); }; - let idx = idx_const - .try_to_bits(Size::from_bytes(4 /* u32*/)) - .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx: u32 = idx_const + .try_to_u32() + .unwrap_or_else(|_| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); - if idx >= lane_count.into() { + if u64::from(idx) >= lane_count { fx.tcx.sess.span_fatal( fx.mir.span, format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count), @@ -331,10 +331,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; let idx = idx_const - .try_to_bits(Size::from_bytes(4 /* u32*/)) - .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + .try_to_u32() + .unwrap_or_else(|_| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); - if idx >= lane_count.into() { + if u64::from(idx) >= lane_count { fx.tcx.sess.span_fatal( fx.mir.span, format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count), diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a530fc994a2..fd4af984bc0 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -12,9 +12,9 @@ * TODO(antoyo): remove the patches. */ -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![allow(internal_features)] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature( rustc_private, decl_macro, @@ -384,7 +384,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool, target_info: &Locke .iter() .filter_map( |&(feature, gate)| { - if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None } + if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { None } }, ) .filter(|_feature| { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 9d5204034de..8d335ff1718 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -990,11 +990,7 @@ unsafe fn embed_bitcode( // reason (see issue #90326 for historical background). let is_aix = target_is_aix(cgcx); let is_apple = target_is_apple(cgcx); - if is_apple - || is_aix - || cgcx.opts.target_triple.triple().starts_with("wasm") - || cgcx.opts.target_triple.triple().starts_with("asmjs") - { + if is_apple || is_aix || cgcx.opts.target_triple.triple().starts_with("wasm") { // We don't need custom section flags, create LLVM globals. let llconst = common::bytes_in_context(llcx, bitcode); let llglobal = llvm::LLVMAddGlobal( diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index d5778757caa..0c9f7f19551 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -59,7 +59,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> // To avoid this, we set the Storage Class to "DllImport" so that // LLVM will prefix the name with `__imp_`. Ideally, we'd like the // existing logic below to set the Storage Class, but it has an - // exemption for MinGW for backwards compatability. + // exemption for MinGW for backwards compatibility. let llfn = cx.declare_fn( &common::i686_decorated_name( &dllimport, diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 0b0816c27b6..d1b643f4967 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -203,6 +203,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMSetInitializer(g, sc); llvm::LLVMSetGlobalConstant(g, True); + llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); } (s.to_owned(), g) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 4dd6372b5e0..242c6aed906 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -368,6 +368,24 @@ pub unsafe fn create_module<'ll>( llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), ); + // Add module flags specified via -Z llvm_module_flag + for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag { + let key = format!("{key}\0"); + let behavior = match behavior.as_str() { + "error" => llvm::LLVMModFlagBehavior::Error, + "warning" => llvm::LLVMModFlagBehavior::Warning, + "require" => llvm::LLVMModFlagBehavior::Require, + "override" => llvm::LLVMModFlagBehavior::Override, + "append" => llvm::LLVMModFlagBehavior::Append, + "appendunique" => llvm::LLVMModFlagBehavior::AppendUnique, + "max" => llvm::LLVMModFlagBehavior::Max, + "min" => llvm::LLVMModFlagBehavior::Min, + // We already checked this during option parsing + _ => unreachable!(), + }; + llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value) + } + llmod } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 8a6a5f79b3b..3242e78ab7e 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -4,9 +4,9 @@ //! //! This API is completely unstable and subject to change. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(exact_size_is_empty)] #![feature(extern_types)] diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index cc4ccaf19c2..c86bf81fc13 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -293,7 +293,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { supported_target_features(sess) .iter() .filter_map(|&(feature, gate)| { - if sess.is_nightly_build() || allow_unstable || gate.is_none() { + if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { None @@ -554,7 +554,8 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } }; sess.emit_warning(unknown_feature); - } else if feature_state.is_some_and(|(_name, feature_gate)| feature_gate.is_some()) + } else if feature_state + .is_some_and(|(_name, feature_gate)| !feature_gate.is_stable()) { // An unstable feature. Warn about using it. sess.emit_warning(UnstableCTargetFeature { feature }); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index dd9d277fb77..33e8f352cd8 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2243,9 +2243,9 @@ fn linker_with_args<'a>( // ------------ Late order-dependent options ------------ // Doesn't really make sense. - // FIXME: In practice built-in target specs use this for arbitrary order-independent options, - // introduce a target spec option for order-independent linker options, migrate built-in specs - // to it and remove the option. + // FIXME: In practice built-in target specs use this for arbitrary order-independent options. + // Introduce a target spec option for order-independent linker options, migrate built-in specs + // to it and remove the option. Currently the last holdout is wasm32-unknown-emscripten. add_post_link_args(cmd, sess, flavor); Ok(cmd.take_cmd()) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 1a85eb8dd79..3c7e8873b4d 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -249,7 +249,7 @@ fn push_debuginfo_type_name<'tcx>( .projection_bounds() .map(|bound| { let ExistentialProjection { def_id: item_def_id, term, .. } = - tcx.erase_late_bound_regions(bound); + tcx.instantiate_bound_regions_with_erased(bound); // FIXME(associated_const_equality): allow for consts here (item_def_id, term.ty().unwrap()) }) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 156c2904f40..8a82a37df9c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,7 +1,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(if_let_guard)] diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 2285e7f4e01..ed859cb20de 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -36,7 +36,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Arguments get assigned to by means of the function being called for arg in mir.args_iter() { - analyzer.assign(arg, DefLocation::Argument); + analyzer.define(arg, DefLocation::Argument); } // If there exists a local definition that dominates all uses of that local, @@ -74,7 +74,7 @@ struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { } impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { - fn assign(&mut self, local: mir::Local, location: DefLocation) { + fn define(&mut self, local: mir::Local, location: DefLocation) { let kind = &mut self.locals[local]; match *kind { LocalKind::ZST => {} @@ -162,7 +162,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue); if let Some(local) = place.as_local() { - self.assign(local, DefLocation::Body(location)); + self.define(local, DefLocation::Assignment(location)); if self.locals[local] != LocalKind::Memory { let decl_span = self.fx.mir.local_decls[local].source_info.span; if !self.fx.rvalue_creates_operand(rvalue, decl_span) { @@ -183,9 +183,14 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> fn visit_local(&mut self, local: mir::Local, context: PlaceContext, location: Location) { match context { - PlaceContext::MutatingUse(MutatingUseContext::Call) - | PlaceContext::MutatingUse(MutatingUseContext::Yield) => { - self.assign(local, DefLocation::Body(location)); + PlaceContext::MutatingUse(MutatingUseContext::Call) => { + let call = location.block; + let TerminatorKind::Call { target, .. } = + self.fx.mir.basic_blocks[call].terminator().kind + else { + bug!() + }; + self.define(local, DefLocation::CallReturn { call, target }); } PlaceContext::NonUse(_) @@ -237,6 +242,8 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> } } } + + PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(), } } } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 2936f1de3cb..d802816bb75 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -19,10 +19,44 @@ use rustc_span::Span; /// Features that control behaviour of rustc, rather than the codegen. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; +/// Stability information for target features. +#[derive(Debug, Clone, Copy)] +pub enum Stability { + /// This target feature is stable, it can be used in `#[target_feature]` and + /// `#[cfg(target_feature)]`. + Stable, + /// This target feature is unstable; using it in `#[target_feature]` or `#[cfg(target_feature)]` + /// requires enabling the given nightly feature. + Unstable(Symbol), +} +use Stability::*; + +impl Stability { + fn as_feature_name(self) -> Option<Symbol> { + match self { + Stable => None, + Unstable(s) => Some(s), + } + } + + pub fn is_stable(self) -> bool { + matches!(self, Stable) + } +} + +// Here we list target features that rustc "understands": they can be used in `#[target_feature]` +// and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with +// `-Ctarget-feature`. +// // When adding features to the below lists // check whether they're named already elsewhere in rust // e.g. in stdarch and whether the given name matches LLVM's -// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted +// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted. +// +// Also note that all target features listed here must be purely additive: for target_feature 1.1 to +// be sound, we can never allow features like `+soft-float` (on x86) to be controlled on a +// per-function level, since we would then allow safe calls from functions with `+soft-float` to +// functions without that feature! // // When adding a new feature, be particularly mindful of features that affect function ABIs. Those // need to be treated very carefully to avoid introducing unsoundness! This often affects features @@ -30,139 +64,138 @@ pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; // example of this going wrong), but features enabling new SIMD registers are also a concern (see // https://github.com/rust-lang/rust/issues/116558 for an example of this going wrong). // -// Stabilizing a target feature (setting the 2nd component of the pair to `None`) requires t-lang -// approval. +// Stabilizing a target feature requires t-lang approval. -const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const ARM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("aclass", Some(sym::arm_target_feature)), - ("aes", Some(sym::arm_target_feature)), - ("crc", Some(sym::arm_target_feature)), - ("d32", Some(sym::arm_target_feature)), - ("dotprod", Some(sym::arm_target_feature)), - ("dsp", Some(sym::arm_target_feature)), - ("fp-armv8", Some(sym::arm_target_feature)), - ("i8mm", Some(sym::arm_target_feature)), - ("mclass", Some(sym::arm_target_feature)), - ("neon", Some(sym::arm_target_feature)), - ("rclass", Some(sym::arm_target_feature)), - ("sha2", Some(sym::arm_target_feature)), + ("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)), // 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", Some(sym::arm_target_feature)), - ("thumb2", Some(sym::arm_target_feature)), - ("trustzone", Some(sym::arm_target_feature)), - ("v5te", Some(sym::arm_target_feature)), - ("v6", Some(sym::arm_target_feature)), - ("v6k", Some(sym::arm_target_feature)), - ("v6t2", Some(sym::arm_target_feature)), - ("v7", Some(sym::arm_target_feature)), - ("v8", Some(sym::arm_target_feature)), - ("vfp2", Some(sym::arm_target_feature)), - ("vfp3", Some(sym::arm_target_feature)), - ("vfp4", Some(sym::arm_target_feature)), - ("virtualization", Some(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)), + ("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)), // tidy-alphabetical-end ]; -const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const AARCH64_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start // FEAT_AES - ("aes", None), + ("aes", Stable), // FEAT_BF16 - ("bf16", None), + ("bf16", Stable), // FEAT_BTI - ("bti", None), + ("bti", Stable), // FEAT_CRC - ("crc", None), + ("crc", Stable), // FEAT_DIT - ("dit", None), + ("dit", Stable), // FEAT_DotProd - ("dotprod", None), + ("dotprod", Stable), // FEAT_DPB - ("dpb", None), + ("dpb", Stable), // FEAT_DPB2 - ("dpb2", None), + ("dpb2", Stable), // FEAT_F32MM - ("f32mm", None), + ("f32mm", Stable), // FEAT_F64MM - ("f64mm", None), + ("f64mm", Stable), // FEAT_FCMA - ("fcma", None), + ("fcma", Stable), // FEAT_FHM - ("fhm", None), + ("fhm", Stable), // FEAT_FLAGM - ("flagm", None), + ("flagm", Stable), // FEAT_FP16 - ("fp16", None), + ("fp16", Stable), // FEAT_FRINTTS - ("frintts", None), + ("frintts", Stable), // FEAT_I8MM - ("i8mm", None), + ("i8mm", Stable), // FEAT_JSCVT - ("jsconv", None), + ("jsconv", Stable), // FEAT_LOR - ("lor", None), + ("lor", Stable), // FEAT_LSE - ("lse", None), + ("lse", Stable), // FEAT_MTE - ("mte", None), + ("mte", Stable), // FEAT_AdvSimd & FEAT_FP - ("neon", None), + ("neon", Stable), // FEAT_PAUTH (address authentication) - ("paca", None), + ("paca", Stable), // FEAT_PAUTH (generic authentication) - ("pacg", None), + ("pacg", Stable), // FEAT_PAN - ("pan", None), + ("pan", Stable), // FEAT_PMUv3 - ("pmuv3", None), + ("pmuv3", Stable), // FEAT_RAND - ("rand", None), + ("rand", Stable), // FEAT_RAS - ("ras", None), + ("ras", Stable), // FEAT_RCPC - ("rcpc", None), + ("rcpc", Stable), // FEAT_RCPC2 - ("rcpc2", None), + ("rcpc2", Stable), // FEAT_RDM - ("rdm", None), + ("rdm", Stable), // FEAT_SB - ("sb", None), + ("sb", Stable), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", None), + ("sha2", Stable), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", None), + ("sha3", Stable), // FEAT_SM3 & FEAT_SM4 - ("sm4", None), + ("sm4", Stable), // FEAT_SPE - ("spe", None), + ("spe", Stable), // FEAT_SSBS - ("ssbs", None), + ("ssbs", Stable), // FEAT_SVE - ("sve", None), + ("sve", Stable), // FEAT_SVE2 - ("sve2", None), + ("sve2", Stable), // FEAT_SVE2_AES - ("sve2-aes", None), + ("sve2-aes", Stable), // FEAT_SVE2_BitPerm - ("sve2-bitperm", None), + ("sve2-bitperm", Stable), // FEAT_SVE2_SHA3 - ("sve2-sha3", None), + ("sve2-sha3", Stable), // FEAT_SVE2_SM4 - ("sve2-sm4", None), + ("sve2-sm4", Stable), // FEAT_TME - ("tme", None), - ("v8.1a", Some(sym::aarch64_ver_target_feature)), - ("v8.2a", Some(sym::aarch64_ver_target_feature)), - ("v8.3a", Some(sym::aarch64_ver_target_feature)), - ("v8.4a", Some(sym::aarch64_ver_target_feature)), - ("v8.5a", Some(sym::aarch64_ver_target_feature)), - ("v8.6a", Some(sym::aarch64_ver_target_feature)), - ("v8.7a", Some(sym::aarch64_ver_target_feature)), + ("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)), // FEAT_VHE - ("vh", None), + ("vh", Stable), // tidy-alphabetical-end ]; @@ -170,197 +203,197 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("adx", None), - ("aes", None), - ("avx", None), - ("avx2", None), - ("avx512bf16", Some(sym::avx512_target_feature)), - ("avx512bitalg", Some(sym::avx512_target_feature)), - ("avx512bw", Some(sym::avx512_target_feature)), - ("avx512cd", Some(sym::avx512_target_feature)), - ("avx512dq", Some(sym::avx512_target_feature)), - ("avx512er", Some(sym::avx512_target_feature)), - ("avx512f", Some(sym::avx512_target_feature)), - ("avx512ifma", Some(sym::avx512_target_feature)), - ("avx512pf", Some(sym::avx512_target_feature)), - ("avx512vbmi", Some(sym::avx512_target_feature)), - ("avx512vbmi2", Some(sym::avx512_target_feature)), - ("avx512vl", Some(sym::avx512_target_feature)), - ("avx512vnni", Some(sym::avx512_target_feature)), - ("avx512vp2intersect", Some(sym::avx512_target_feature)), - ("avx512vpopcntdq", Some(sym::avx512_target_feature)), - ("bmi1", None), - ("bmi2", None), - ("cmpxchg16b", None), - ("ermsb", Some(sym::ermsb_target_feature)), - ("f16c", None), - ("fma", None), - ("fxsr", None), - ("gfni", Some(sym::avx512_target_feature)), - ("lzcnt", None), - ("movbe", None), - ("pclmulqdq", None), - ("popcnt", None), - ("rdrand", None), - ("rdseed", None), - ("rtm", Some(sym::rtm_target_feature)), - ("sha", None), - ("sse", None), - ("sse2", None), - ("sse3", None), - ("sse4.1", None), - ("sse4.2", None), - ("sse4a", Some(sym::sse4a_target_feature)), - ("ssse3", None), - ("tbm", Some(sym::tbm_target_feature)), - ("vaes", Some(sym::avx512_target_feature)), - ("vpclmulqdq", Some(sym::avx512_target_feature)), - ("xsave", None), - ("xsavec", None), - ("xsaveopt", None), - ("xsaves", None), + ("adx", Stable), + ("aes", Stable), + ("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)), + ("avx512er", Unstable(sym::avx512_target_feature)), + ("avx512f", Unstable(sym::avx512_target_feature)), + ("avx512ifma", Unstable(sym::avx512_target_feature)), + ("avx512pf", 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)), + ("bmi1", Stable), + ("bmi2", Stable), + ("cmpxchg16b", Stable), + ("ermsb", Unstable(sym::ermsb_target_feature)), + ("f16c", Stable), + ("fma", Stable), + ("fxsr", Stable), + ("gfni", Unstable(sym::avx512_target_feature)), + ("lzcnt", Stable), + ("movbe", Stable), + ("pclmulqdq", Stable), + ("popcnt", Stable), + ("rdrand", Stable), + ("rdseed", Stable), + ("rtm", Unstable(sym::rtm_target_feature)), + ("sha", Stable), + ("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)), + ("xsave", Stable), + ("xsavec", Stable), + ("xsaveopt", Stable), + ("xsaves", Stable), // tidy-alphabetical-end ]; -const HEXAGON_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("hvx", Some(sym::hexagon_target_feature)), - ("hvx-length128b", Some(sym::hexagon_target_feature)), + ("hvx", Unstable(sym::hexagon_target_feature)), + ("hvx-length128b", Unstable(sym::hexagon_target_feature)), // tidy-alphabetical-end ]; -const POWERPC_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const POWERPC_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("altivec", Some(sym::powerpc_target_feature)), - ("power10-vector", Some(sym::powerpc_target_feature)), - ("power8-altivec", Some(sym::powerpc_target_feature)), - ("power8-vector", Some(sym::powerpc_target_feature)), - ("power9-altivec", Some(sym::powerpc_target_feature)), - ("power9-vector", Some(sym::powerpc_target_feature)), - ("vsx", Some(sym::powerpc_target_feature)), + ("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)), // tidy-alphabetical-end ]; -const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const MIPS_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("fp64", Some(sym::mips_target_feature)), - ("msa", Some(sym::mips_target_feature)), - ("virt", Some(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, Option<Symbol>)] = &[ +const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("a", None), - ("c", None), - ("d", Some(sym::riscv_target_feature)), - ("e", Some(sym::riscv_target_feature)), - ("f", Some(sym::riscv_target_feature)), - ("m", None), - ("relax", Some(sym::riscv_target_feature)), - ("unaligned-scalar-mem", Some(sym::riscv_target_feature)), - ("v", Some(sym::riscv_target_feature)), - ("zba", None), - ("zbb", None), - ("zbc", None), - ("zbkb", None), - ("zbkc", None), - ("zbkx", None), - ("zbs", None), - ("zdinx", Some(sym::riscv_target_feature)), - ("zfh", Some(sym::riscv_target_feature)), - ("zfhmin", Some(sym::riscv_target_feature)), - ("zfinx", Some(sym::riscv_target_feature)), - ("zhinx", Some(sym::riscv_target_feature)), - ("zhinxmin", Some(sym::riscv_target_feature)), - ("zk", None), - ("zkn", None), - ("zknd", None), - ("zkne", None), - ("zknh", None), - ("zkr", None), - ("zks", None), - ("zksed", None), - ("zksh", None), - ("zkt", None), + ("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), // tidy-alphabetical-end ]; -const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("atomics", Some(sym::wasm_target_feature)), - ("bulk-memory", Some(sym::wasm_target_feature)), - ("exception-handling", Some(sym::wasm_target_feature)), - ("multivalue", Some(sym::wasm_target_feature)), - ("mutable-globals", Some(sym::wasm_target_feature)), - ("nontrapping-fptoint", Some(sym::wasm_target_feature)), - ("reference-types", Some(sym::wasm_target_feature)), - ("relaxed-simd", Some(sym::wasm_target_feature)), - ("sign-ext", Some(sym::wasm_target_feature)), - ("simd128", None), + ("atomics", Unstable(sym::wasm_target_feature)), + ("bulk-memory", Unstable(sym::wasm_target_feature)), + ("exception-handling", Unstable(sym::wasm_target_feature)), + ("multivalue", Unstable(sym::wasm_target_feature)), + ("mutable-globals", Unstable(sym::wasm_target_feature)), + ("nontrapping-fptoint", Unstable(sym::wasm_target_feature)), + ("reference-types", Unstable(sym::wasm_target_feature)), + ("relaxed-simd", Unstable(sym::wasm_target_feature)), + ("sign-ext", Unstable(sym::wasm_target_feature)), + ("simd128", Stable), // tidy-alphabetical-end ]; -const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))]; +const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; -const CSKY_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ +const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("10e60", Some(sym::csky_target_feature)), - ("2e3", Some(sym::csky_target_feature)), - ("3e3r1", Some(sym::csky_target_feature)), - ("3e3r2", Some(sym::csky_target_feature)), - ("3e3r3", Some(sym::csky_target_feature)), - ("3e7", Some(sym::csky_target_feature)), - ("7e10", Some(sym::csky_target_feature)), - ("cache", Some(sym::csky_target_feature)), - ("doloop", Some(sym::csky_target_feature)), - ("dsp1e2", Some(sym::csky_target_feature)), - ("dspe60", Some(sym::csky_target_feature)), - ("e1", Some(sym::csky_target_feature)), - ("e2", Some(sym::csky_target_feature)), - ("edsp", Some(sym::csky_target_feature)), - ("elrw", Some(sym::csky_target_feature)), - ("float1e2", Some(sym::csky_target_feature)), - ("float1e3", Some(sym::csky_target_feature)), - ("float3e4", Some(sym::csky_target_feature)), - ("float7e60", Some(sym::csky_target_feature)), - ("floate1", Some(sym::csky_target_feature)), - ("hard-tp", Some(sym::csky_target_feature)), - ("high-registers", Some(sym::csky_target_feature)), - ("hwdiv", Some(sym::csky_target_feature)), - ("mp", Some(sym::csky_target_feature)), - ("mp1e2", Some(sym::csky_target_feature)), - ("nvic", Some(sym::csky_target_feature)), - ("trust", Some(sym::csky_target_feature)), - ("vdsp2e60f", Some(sym::csky_target_feature)), - ("vdspv1", Some(sym::csky_target_feature)), - ("vdspv2", Some(sym::csky_target_feature)), + ("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)), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", Some(sym::csky_target_feature)), - ("fpuv2_df", Some(sym::csky_target_feature)), - ("fpuv2_sf", Some(sym::csky_target_feature)), - ("fpuv3_df", Some(sym::csky_target_feature)), - ("fpuv3_hf", Some(sym::csky_target_feature)), - ("fpuv3_hi", Some(sym::csky_target_feature)), - ("fpuv3_sf", Some(sym::csky_target_feature)), - ("hard-float", Some(sym::csky_target_feature)), - ("hard-float-abi", Some(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, Option<Symbol>)] = &[ +const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start - ("d", Some(sym::loongarch_target_feature)), - ("f", Some(sym::loongarch_target_feature)), - ("lasx", Some(sym::loongarch_target_feature)), - ("lbt", Some(sym::loongarch_target_feature)), - ("lsx", Some(sym::loongarch_target_feature)), - ("lvz", Some(sym::loongarch_target_feature)), - ("ual", Some(sym::loongarch_target_feature)), + ("d", Unstable(sym::loongarch_target_feature)), + ("f", 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)), + ("ual", Unstable(sym::loongarch_target_feature)), // tidy-alphabetical-end ]; @@ -368,7 +401,7 @@ const LOONGARCH_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ /// primitives may be documented. /// /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_known_features() -> impl Iterator<Item = (&'static str, Option<Symbol>)> { +pub fn all_known_features() -> impl Iterator<Item = (&'static str, Stability)> { std::iter::empty() .chain(ARM_ALLOWED_FEATURES.iter()) .chain(AARCH64_ALLOWED_FEATURES.iter()) @@ -384,7 +417,7 @@ pub fn all_known_features() -> impl Iterator<Item = (&'static str, Option<Symbol .cloned() } -pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option<Symbol>)] { +pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Stability)] { match &*sess.target.arch { "arm" => ARM_ALLOWED_FEATURES, "aarch64" => AARCH64_ALLOWED_FEATURES, @@ -529,11 +562,11 @@ pub(crate) fn provide(providers: &mut Providers) { if tcx.sess.opts.actually_rustdoc { // rustdoc needs to be able to document functions that use all the features, so // whitelist them all - all_known_features().map(|(a, b)| (a.to_string(), b)).collect() + all_known_features().map(|(a, b)| (a.to_string(), b.as_feature_name())).collect() } else { supported_target_features(tcx.sess) .iter() - .map(|&(a, b)| (a.to_string(), b)) + .map(|&(a, b)| (a.to_string(), b.as_feature_name())) .collect() } }, diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index fd173670374..d9f583c1d1f 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -119,7 +119,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { throw_ub!(UninhabitedEnumVariantRead(index)) } - // For consisteny with `write_discriminant`, and to make sure that + // For consistency with `write_discriminant`, and to make sure that // `project_downcast` cannot fail due to strange layouts, we declare immediate UB // for uninhabited variants. if op.layout().for_variant(self, index).abi.is_uninhabited() { @@ -236,7 +236,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { variant } }; - // For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants. + // For consistency with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants. if op.layout().for_variant(self, index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantRead(index)) } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index a3ba9530f9d..128b548f43d 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -114,9 +114,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_middle::mir::BinOp::*; // Performs appropriate non-deterministic adjustments of NaN results. - let adjust_nan = |f: F| -> F { - if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } - }; + let adjust_nan = + |f: F| -> F { if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } }; let val = match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1e21c494070..26ff757ca59 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -4,9 +4,9 @@ Rust MIR: a lowered representation of Rust. */ -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![deny(rustc::untranslatable_diagnostic)] #![feature(assert_matches)] #![feature(box_patterns)] diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 32af537e271..b3d6b891b11 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -864,6 +864,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { }; // Use the underlying local for this (necessarily interior) borrow. + debug_assert!(region.is_erased()); let ty = local_decls[place.local].ty; let span = statement.source_info.span; @@ -873,8 +874,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() }, ); - *region = tcx.lifetimes.re_erased; - let mut projection = vec![PlaceElem::Deref]; projection.extend(place.projection); place.projection = tcx.mk_place_elems(&projection); diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index d09c026c4b4..3ef87684fa5 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,12 +10,11 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![deny(rustc::diagnostic_outside_of_impl)] #![deny(rustc::untranslatable_diagnostic)] #![deny(unsafe_op_in_unsafe_fn)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(allocator_api)] #![feature(array_windows)] #![feature(auto_traits)] @@ -34,6 +33,7 @@ #![feature(never_type)] #![feature(ptr_alignment_type)] #![feature(rustc_attrs)] +#![feature(rustdoc_internals)] #![feature(strict_provenance)] #![feature(test)] #![feature(thread_id_value)] diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index 29516fffd6a..639f05c9e52 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -79,7 +79,7 @@ impl<T> Sharded<T> { pub fn lock_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> LockGuard<'_, T> { match self { Self::Single(single) => { - // Syncronization is disabled so use the `lock_assume_no_sync` method optimized + // Synchronization is disabled so use the `lock_assume_no_sync` method optimized // for that case. // SAFETY: We know `is_dyn_thread_safe` was false when creating the lock thus @@ -102,7 +102,7 @@ impl<T> Sharded<T> { pub fn lock_shard_by_index(&self, _i: usize) -> LockGuard<'_, T> { match self { Self::Single(single) => { - // Syncronization is disabled so use the `lock_assume_no_sync` method optimized + // Synchronization is disabled so use the `lock_assume_no_sync` method optimized // for that case. // SAFETY: We know `is_dyn_thread_safe` was false when creating the lock thus @@ -111,7 +111,7 @@ impl<T> Sharded<T> { } #[cfg(parallel_compiler)] Self::Shards(shards) => { - // Syncronization is enabled so use the `lock_assume_sync` method optimized + // Synchronization is enabled so use the `lock_assume_sync` method optimized // for that case. // SAFETY (get_unchecked): The index gets ANDed with the shard mask, ensuring it is diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs index 339aebbf81a..040a8aa6b63 100644 --- a/compiler/rustc_data_structures/src/sync/lock.rs +++ b/compiler/rustc_data_structures/src/sync/lock.rs @@ -38,7 +38,7 @@ mod maybe_sync { lock: &'a Lock<T>, marker: PhantomData<&'a mut T>, - /// The syncronization mode of the lock. This is explicitly passed to let LLVM relate it + /// The synchronization mode of the lock. This is explicitly passed to let LLVM relate it /// to the original lock operation. mode: Mode, } @@ -142,7 +142,7 @@ mod maybe_sync { .then(|| LockGuard { lock: self, marker: PhantomData, mode }) } - /// This acquires the lock assuming syncronization is in a specific mode. + /// This acquires the lock assuming synchronization is in a specific mode. /// /// Safety /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 27f08fe7eef..acd93b0b2a6 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1,8 +1,8 @@ // This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in // `rustc_driver_impl` to be compiled in parallel with other crates. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] pub use rustc_driver_impl::*; diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index da7c2440faa..545ff32e598 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -43,6 +43,7 @@ rustc_privacy = { path = "../rustc_privacy" } rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } +rustc_smir ={ path = "../rustc_smir" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 84ae45d6a2b..6ee7213a19d 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -5,9 +5,9 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(decl_macro)] #![feature(lazy_cell)] #![feature(let_chains)] @@ -33,7 +33,7 @@ use rustc_feature::find_gated_cfg; use rustc_fluent_macro::fluent_messages; use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; use rustc_interface::{interface, Queries}; -use rustc_lint::{unerased_lint_store, LintStore}; +use rustc_lint::unerased_lint_store; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType, TrimmedDefPaths}; @@ -293,7 +293,7 @@ fn run_compiler( >, using_internal_features: Arc<std::sync::atomic::AtomicBool>, ) -> interface::Result<()> { - let mut early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default()); + let mut default_handler = EarlyErrorHandler::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. // In case of at_args being empty, as might be the case by @@ -305,14 +305,14 @@ fn run_compiler( // the compiler with @empty_file as argv[0] and no more arguments. let at_args = at_args.get(1..).unwrap_or_default(); - let args = args::arg_expand_all(&early_error_handler, at_args); + let args = args::arg_expand_all(&default_handler, at_args); - let Some(matches) = handle_options(&early_error_handler, &args) else { return Ok(()) }; + let Some(matches) = handle_options(&default_handler, &args) else { return Ok(()) }; - let sopts = config::build_session_options(&mut early_error_handler, &matches); + let sopts = config::build_session_options(&mut default_handler, &matches); if let Some(ref code) = matches.opt_str("explain") { - handle_explain(&early_error_handler, diagnostics_registry(), code, sopts.color); + handle_explain(&default_handler, diagnostics_registry(), code, sopts.color); return Ok(()); } @@ -338,71 +338,56 @@ fn run_compiler( expanded_args: args, }; - match make_input(&early_error_handler, &matches.free) { + let has_input = match make_input(&default_handler, &matches.free) { Err(reported) => return Err(reported), Ok(Some(input)) => { config.input = input; - - callbacks.config(&mut config); + true // has input: normal compilation } Ok(None) => match matches.free.len() { - 0 => { - callbacks.config(&mut config); - - early_error_handler.abort_if_errors(); - - interface::run_compiler(config, |compiler| { - let sopts = &compiler.session().opts; - let handler = EarlyErrorHandler::new(sopts.error_format); - - if sopts.describe_lints { - let mut lint_store = - rustc_lint::new_lint_store(compiler.session().enable_internal_lints()); - let registered_lints = - if let Some(register_lints) = compiler.register_lints() { - register_lints(compiler.session(), &mut lint_store); - true - } else { - false - }; - describe_lints(compiler.session(), &lint_store, registered_lints); - return; - } - let should_stop = print_crate_info( - &handler, - &**compiler.codegen_backend(), - compiler.session(), - false, - ); - - if should_stop == Compilation::Stop { - return; - } - handler.early_error("no input filename given") - }); - return Ok(()); - } + 0 => false, // no input: we will exit early 1 => panic!("make_input should have provided valid inputs"), - _ => early_error_handler.early_error(format!( + _ => default_handler.early_error(format!( "multiple input filenames provided (first two filenames are `{}` and `{}`)", matches.free[0], matches.free[1], )), }, }; - early_error_handler.abort_if_errors(); + callbacks.config(&mut config); + + default_handler.abort_if_errors(); + drop(default_handler); interface::run_compiler(config, |compiler| { let sess = compiler.session(); + let codegen_backend = compiler.codegen_backend(); + + // This implements `-Whelp`. It should be handled very early, like + // `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because + // it must happen after lints are registered, during session creation. + if sess.opts.describe_lints { + describe_lints(sess); + return sess.compile_status(); + } + let handler = EarlyErrorHandler::new(sess.opts.error_format); - let should_stop = print_crate_info(&handler, &**compiler.codegen_backend(), sess, true) - .and_then(|| { - list_metadata(&handler, sess, &*compiler.codegen_backend().metadata_loader()) - }) - .and_then(|| try_process_rlink(sess, compiler)); + if print_crate_info(&handler, codegen_backend, sess, has_input) == Compilation::Stop { + return sess.compile_status(); + } + + if !has_input { + handler.early_error("no input filename given"); // this is fatal + } + + if !sess.opts.unstable_opts.ls.is_empty() { + list_metadata(&handler, sess, &*codegen_backend.metadata_loader()); + return sess.compile_status(); + } - if should_stop == Compilation::Stop { + if sess.opts.unstable_opts.link_only { + process_rlink(sess, compiler); return sess.compile_status(); } @@ -441,13 +426,6 @@ fn run_compiler( return early_exit(); } - if sess.opts.describe_lints { - queries - .global_ctxt()? - .enter(|tcx| describe_lints(sess, unerased_lint_store(tcx), true)); - return early_exit(); - } - // Make sure name resolution and macro expansion is run. queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering(())); @@ -493,11 +471,7 @@ fn run_compiler( if let Some(linker) = linker { let _timer = sess.timer("link"); - linker.link()? - } - - if sess.opts.unstable_opts.perf_stats { - sess.print_perf_stats(); + linker.link(sess, codegen_backend)? } if sess.opts.unstable_opts.print_fuel.is_some() { @@ -566,15 +540,6 @@ pub enum Compilation { Continue, } -impl Compilation { - fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation { - match self { - Compilation::Stop => Compilation::Stop, - Compilation::Continue => next(), - } - } -} - fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str, color: ColorConfig) { let upper_cased_code = code.to_ascii_uppercase(); let normalised = @@ -679,44 +644,34 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { } } -fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { - if sess.opts.unstable_opts.link_only { - if let Input::File(file) = &sess.io.input { - let outputs = compiler.build_output_filenames(sess, &[]); - let rlink_data = fs::read(file).unwrap_or_else(|err| { - sess.emit_fatal(RlinkUnableToRead { err }); - }); - let codegen_results = match CodegenResults::deserialize_rlink(sess, rlink_data) { - Ok(codegen) => codegen, - Err(err) => { - match err { - CodegenErrors::WrongFileType => sess.emit_fatal(RLinkWrongFileType), - CodegenErrors::EmptyVersionNumber => { - sess.emit_fatal(RLinkEmptyVersionNumber) - } - CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => { - sess.emit_fatal(RLinkEncodingVersionMismatch { - version_array, - rlink_version, - }) - } - CodegenErrors::RustcVersionMismatch { rustc_version } => { - sess.emit_fatal(RLinkRustcVersionMismatch { - rustc_version, - current_version: sess.cfg_version, - }) - } - }; - } - }; - let result = compiler.codegen_backend().link(sess, codegen_results, &outputs); - abort_on_err(result, sess); - } else { - sess.emit_fatal(RlinkNotAFile {}) - } - Compilation::Stop +fn process_rlink(sess: &Session, compiler: &interface::Compiler) { + assert!(sess.opts.unstable_opts.link_only); + if let Input::File(file) = &sess.io.input { + let outputs = compiler.build_output_filenames(sess, &[]); + let rlink_data = fs::read(file).unwrap_or_else(|err| { + sess.emit_fatal(RlinkUnableToRead { err }); + }); + let codegen_results = match CodegenResults::deserialize_rlink(sess, rlink_data) { + Ok(codegen) => codegen, + Err(err) => { + match err { + CodegenErrors::WrongFileType => sess.emit_fatal(RLinkWrongFileType), + CodegenErrors::EmptyVersionNumber => sess.emit_fatal(RLinkEmptyVersionNumber), + CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => sess + .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }), + CodegenErrors::RustcVersionMismatch { rustc_version } => { + sess.emit_fatal(RLinkRustcVersionMismatch { + rustc_version, + current_version: sess.cfg_version, + }) + } + }; + } + }; + let result = compiler.codegen_backend().link(sess, codegen_results, &outputs); + abort_on_err(result, sess); } else { - Compilation::Continue + sess.emit_fatal(RlinkNotAFile {}) } } @@ -724,25 +679,25 @@ fn list_metadata( handler: &EarlyErrorHandler, sess: &Session, metadata_loader: &dyn MetadataLoader, -) -> Compilation { - let ls_kinds = &sess.opts.unstable_opts.ls; - if !ls_kinds.is_empty() { - match sess.io.input { - Input::File(ref ifile) => { - let path = &(*ifile); - let mut v = Vec::new(); - locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v, ls_kinds) - .unwrap(); - safe_println!("{}", String::from_utf8(v).unwrap()); - } - Input::Str { .. } => { - handler.early_error("cannot list metadata for stdin"); - } +) { + match sess.io.input { + Input::File(ref ifile) => { + let path = &(*ifile); + let mut v = Vec::new(); + locator::list_file_metadata( + &sess.target, + path, + metadata_loader, + &mut v, + &sess.opts.unstable_opts.ls, + ) + .unwrap(); + safe_println!("{}", String::from_utf8(v).unwrap()); + } + Input::Str { .. } => { + handler.early_error("cannot list metadata for stdin"); } - return Compilation::Stop; } - - Compilation::Continue } fn print_crate_info( @@ -995,7 +950,7 @@ the command line flag directly. } /// Write to stdout lint command options, together with a list of all available lints -pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_lints: bool) { +pub fn describe_lints(sess: &Session) { safe_println!( " Available lint options: @@ -1021,6 +976,7 @@ Available lint options: lints } + let lint_store = unerased_lint_store(sess); let (loaded, builtin): (Vec<_>, _) = lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_loaded); let loaded = sort_lints(sess, loaded); @@ -1098,7 +1054,7 @@ Available lint options: print_lint_groups(builtin_groups, true); - match (loaded_lints, loaded.len(), loaded_groups.len()) { + match (sess.registered_lints, loaded.len(), loaded_groups.len()) { (false, 0, _) | (false, _, 0) => { safe_println!("Lint tools like Clippy can load additional lints and lint groups."); } @@ -1524,14 +1480,14 @@ fn report_ice( /// This allows tools to enable rust logging without having to magically match rustc's /// tracing crate version. pub fn init_rustc_env_logger(handler: &EarlyErrorHandler) { - init_env_logger(handler, "RUSTC_LOG"); + init_logger(handler, rustc_log::LoggerConfig::from_env("RUSTC_LOG")); } /// This allows tools to enable rust logging without having to magically match rustc's -/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var -/// other than `RUSTC_LOG`. -pub fn init_env_logger(handler: &EarlyErrorHandler, env: &str) { - if let Err(error) = rustc_log::init_env_logger(env) { +/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose +/// the values directly rather than having to set an environment variable. +pub fn init_logger(handler: &EarlyErrorHandler, cfg: rustc_log::LoggerConfig) { + if let Err(error) = rustc_log::init_logger(cfg) { handler.early_error(error.to_string()); } } diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index cc533b9941a..7cd63bc6422 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -9,6 +9,7 @@ use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode}; use rustc_session::Session; +use rustc_smir::rustc_internal::pretty::write_smir_pretty; use rustc_span::symbol::Ident; use rustc_span::FileName; @@ -325,6 +326,11 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { write_mir_graphviz(ex.tcx(), None, &mut out).unwrap(); String::from_utf8(out).unwrap() } + StableMir => { + let mut out = Vec::new(); + write_smir_pretty(ex.tcx(), &mut out).unwrap(); + String::from_utf8(out).unwrap() + } ThirTree => { let tcx = ex.tcx(); let mut out = String::new(); diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 81ad661286e..dd281811636 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -1,6 +1,6 @@ -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![deny(rustdoc::invalid_codeblock_attributes)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 6249c1e7961..35bd45e64a2 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -1,5 +1,5 @@ -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(let_chains)] #![feature(lazy_cell)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index dd462cc6486..1cee57843f0 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -3,14 +3,13 @@ //! This module contains the code for creating and emitting diagnostics. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(never_type)] -#![feature(result_option_inspect)] #![feature(rustc_attrs)] #![feature(yeet_expr)] #![feature(try_blocks)] diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index d3a08da6283..67e4963fddf 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -329,7 +329,7 @@ fn parse_with_end_pat<'a>( end_sep: &[u8], ignore_esc: bool, ) -> Option<(&'a [u8], &'a [u8])> { - // Find positions that start with the end seperator + // Find positions that start with the end separator for idx in (0..buf.len()).filter(|idx| buf[*idx..].starts_with(end_sep)) { if !ignore_esc && idx > 0 && buf[idx - 1] == b'\\' { continue; diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index bef48765937..0d76b1b2974 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -74,7 +74,9 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // - E.g. enable `test_2018_feature` if `features_edition` is 2018 or higher let mut edition_enabled_features = FxHashSet::default(); for f in UNSTABLE_FEATURES { - if let Some(edition) = f.feature.edition && edition <= features_edition { + if let Some(edition) = f.feature.edition + && edition <= features_edition + { // FIXME(Manishearth) there is currently no way to set lib features by // edition. edition_enabled_features.insert(f.feature.name); @@ -251,8 +253,7 @@ impl<'a> StripUnconfigured<'a> { let trees: Vec<_> = stream .0 .iter() - .flat_map(|tree| { - match tree.clone() { + .flat_map(|tree| match tree.clone() { AttrTokenTree::Attributes(mut data) => { data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); @@ -277,7 +278,6 @@ impl<'a> StripUnconfigured<'a> { AttrTokenTree::Token(token, spacing) => { Some(AttrTokenTree::Token(token, spacing)).into_iter() } - } }) .collect(); AttrTokenStream::new(trees) diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 5a774164a4b..cb084a85e47 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,5 +1,5 @@ -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(associated_type_bounds)] #![feature(associated_type_defaults)] diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index e060375646c..64cf9ced9c1 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -67,6 +67,12 @@ pub(super) fn failed_to_match_macro<'cx>( && (matches!(expected_token.kind, TokenKind::Interpolated(_)) || matches!(token.kind, TokenKind::Interpolated(_))) { + if let TokenKind::Interpolated(node) = &expected_token.kind { + err.span_label(node.1, ""); + } + if let TokenKind::Interpolated(node) = &token.kind { + err.span_label(node.1, ""); + } 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"); diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 7e85beaadcb..1c78232a0f3 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -397,7 +397,7 @@ pub(crate) enum NamedMatch { MatchedTokenTree(rustc_ast::tokenstream::TokenTree), // A metavar match of any type other than `tt`. - MatchedNonterminal(Lrc<Nonterminal>), + MatchedNonterminal(Lrc<(Nonterminal, rustc_span::Span)>), } /// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison) @@ -692,7 +692,7 @@ impl TtParser { Ok(nt) => nt, }; let m = match nt { - ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new(nt)), + ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new((nt, span))), ParseNtResult::Tt(tt) => MatchedTokenTree(tt), }; mp.push_match(next_metavar, seq_depth, m); diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index c617cd76e3c..39a16259fa6 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -126,7 +126,7 @@ impl MultiItemModifier for DeriveProcMacro { Annotatable::Stmt(stmt) => token::NtStmt(stmt), _ => unreachable!(), }; - TokenStream::token_alone(token::Interpolated(Lrc::new(nt)), DUMMY_SP) + TokenStream::token_alone(token::Interpolated(Lrc::new((nt, span))), DUMMY_SP) } else { item.to_tokens() }; diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index aa4c7a53135..41ec0ed84f7 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -226,18 +226,23 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre })); } - Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => trees - .push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span })), + Interpolated(ref nt) if let NtIdent(ident, is_raw) = &nt.0 => { + trees.push(TokenTree::Ident(Ident { + sym: ident.name, + is_raw: *is_raw, + span: ident.span, + })) + } Interpolated(nt) => { - let stream = TokenStream::from_nonterminal_ast(&nt); + let stream = TokenStream::from_nonterminal_ast(&nt.0); // A hack used to pass AST fragments to attribute and derive // macros as a single nonterminal token instead of a token // stream. Such token needs to be "unwrapped" and not // represented as a delimited group. // FIXME: It needs to be removed, but there are some // compatibility issues (see #73345). - if crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()) { + if crate::base::nt_pretty_printing_compatibility_hack(&nt.0, rustc.sess()) { trees.extend(Self::from_internal((stream, rustc))); } else { trees.push(TokenTree::Group(Group { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index f07022733d4..577657ff794 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -65,7 +65,7 @@ declare_features! ( /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions. (accepted, async_await, "1.39.0", Some(50547), None), /// Allows async functions to be declared, implemented, and used in traits. - (accepted, async_fn_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None), + (accepted, async_fn_in_trait, "1.75.0", Some(91611), None), /// Allows all literals in attribute lists and values of key-value pairs. (accepted, attr_literals, "1.30.0", Some(34981), None), /// Allows overloading augmented assignment operations like `a += b`. @@ -306,7 +306,7 @@ declare_features! ( /// Allows `#[repr(transparent)]` attribute on newtype structs. (accepted, repr_transparent, "1.28.0", Some(43036), None), /// Allows return-position `impl Trait` in traits. - (accepted, return_position_impl_trait_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None), + (accepted, return_position_impl_trait_in_trait, "1.75.0", Some(91611), None), /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None), /// Allows `Self` in type definitions (RFC 2300). diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 070234df94c..73d51d9f80e 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -11,9 +11,9 @@ //! even if it is stabilized or removed, *do not remove it*. Instead, move the //! symbol to the `accepted` or `removed` modules respectively. -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![feature(lazy_cell)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 03f92f69b41..4385e745bac 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -153,7 +153,7 @@ declare_features! ( (removed, panic_implementation, "1.28.0", Some(44489), None, Some("subsumed by `#[panic_handler]`")), /// Allows using `#![plugin(myplugin)]`. - (removed, plugin, "CURRENT_RUSTC_VERSION", Some(29597), None, + (removed, plugin, "1.75.0", Some(29597), None, Some("plugins are no longer supported")), /// Allows using `#[plugin_registrar]` on functions. (removed, plugin_registrar, "1.54.0", Some(29597), None, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 64b5a7d2921..b11b190bded 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -457,7 +457,7 @@ declare_features! ( /// Allows using `#[repr(align(...))]` on function items (unstable, fn_align, "1.53.0", Some(82232), None), /// Allows defining gen blocks and `gen fn`. - (unstable, gen_blocks, "CURRENT_RUSTC_VERSION", Some(117078), None), + (unstable, gen_blocks, "1.75.0", Some(117078), None), /// Infer generic args for both consts and types. (unstable, generic_arg_infer, "1.55.0", Some(85077), None), /// An extension to the `generic_associated_types` feature, allowing incomplete features. @@ -527,7 +527,7 @@ declare_features! ( /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. (unstable, object_safe_for_dispatch, "1.40.0", Some(43561), None), /// Allows using enums in offset_of! - (unstable, offset_of_enum, "CURRENT_RUSTC_VERSION", Some(106655), None), + (unstable, offset_of_enum, "1.75.0", Some(106655), None), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882), None), /// Allows exhaustive integer pattern matching on `usize` and `isize`. diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs index 191fb787f70..b041fad1f13 100644 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ b/compiler/rustc_fluent_macro/src/lib.rs @@ -1,7 +1,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![allow(internal_features)] +#![feature(rustdoc_internals)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] #![deny(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 9cb279e3efd..9abd7aec98d 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -273,9 +273,9 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] +#![allow(internal_features)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index cb9259998e3..f6907019d6e 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -194,8 +194,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { self.add_bounds( param_ty, - ast_bounds.iter().filter(|bound| { - match filter { + ast_bounds.iter().filter(|bound| match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfAndAssociatedTypeBounds => true, @@ -209,7 +208,6 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { false } } - } }), &mut bounds, ty::List::empty(), diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 32be7e0837b..7ced37e5694 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -206,15 +206,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { && let parent = hir.get_parent_item(hir.local_def_id_to_hir_id(def_id)) && let Some(generics) = hir.get_generics(parent.def_id) { - if generics.bounds_for_param(def_id) - .flat_map(|pred| pred.bounds.iter()) - .any(|b| match b { + if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( + |b| match b { hir::GenericBound::Trait(t, ..) => { t.trait_ref.trait_def_id().as_ref() == Some(best_trait) } _ => false, - }) - { + }, + ) { // The type param already has a bound for `trait_name`, we just need to // change the associated type. err.span_suggestion_verbose( @@ -227,15 +226,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Applicability::MaybeIncorrect, ); } else if suggest_constraining_type_param( - self.tcx(), - generics, - &mut err, - &ty_param_name, - &trait_name, - None, - None, - ) - && suggested_name != assoc_name.name + self.tcx(), + generics, + &mut err, + &ty_param_name, + &trait_name, + None, + None, + ) && suggested_name != assoc_name.name { // We suggested constraining a type parameter, but the associated type on it // was also not an exact match, so we also suggest changing it. diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index e7a2131011c..8126b451181 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -258,12 +258,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; - ty::Region::new_early_bound(tcx, ty::EarlyBoundRegion { def_id, index, name }) + ty::Region::new_early_param(tcx, ty::EarlyParamRegion { def_id, index, name }) } Some(rbv::ResolvedArg::Free(scope, id)) => { let name = lifetime_name(id.expect_local()); - ty::Region::new_free(tcx, scope, ty::BrNamed(id, name)) + ty::Region::new_late_param(tcx, scope, ty::BrNamed(id, name)) // (*) -- not late-bound, won't change } @@ -2847,6 +2847,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// provided, if they provided one, and otherwise search the supertypes of trait bounds /// for region bounds. It may be that we can derive no bound at all, in which case /// we return `None`. + #[instrument(level = "debug", skip(self, span), ret)] fn compute_object_lifetime_bound( &self, span: Span, @@ -2855,8 +2856,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { let tcx = self.tcx(); - debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates); - // No explicit region bound specified. Therefore, examine trait // bounds and see if we can derive region bounds from those. let derived_region_bounds = object_region_bounds(tcx, existential_predicates); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index d3a383ff7c5..f026f78cc2b 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -532,8 +532,8 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReFree(fr) = *r { - ty::Region::new_free( + if let ty::ReLateParam(fr) = *r { + ty::Region::new_late_param( self.tcx, fr.scope, self.mapping.get(&fr.bound_region).copied().unwrap_or(fr.bound_region), @@ -1078,20 +1078,20 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> { region: ty::Region<'tcx>, ) -> Result<ty::Region<'tcx>, Self::Error> { match region.kind() { - // Remap all free regions, which correspond to late-bound regions in the function. - ty::ReFree(_) => {} + // Remap late-bound regions from the function. + ty::ReLateParam(_) => {} // Remap early-bound regions as long as they don't come from the `impl` itself, // in which case we don't really need to renumber them. - ty::ReEarlyBound(ebr) if self.tcx.parent(ebr.def_id) != self.impl_def_id => {} + ty::ReEarlyParam(ebr) if self.tcx.parent(ebr.def_id) != self.impl_def_id => {} _ => return Ok(region), } let e = if let Some(region) = self.map.get(®ion) { - if let ty::ReEarlyBound(e) = region.kind() { e } else { bug!() } + if let ty::ReEarlyParam(e) = region.kind() { e } else { bug!() } } else { let guar = match region.kind() { - ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) - | ty::ReFree(ty::FreeRegion { + ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { bound_region: ty::BoundRegionKind::BrNamed(def_id, _), .. }) => { @@ -1119,9 +1119,9 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> { return Err(guar); }; - Ok(ty::Region::new_early_bound( + Ok(ty::Region::new_early_param( self.tcx, - ty::EarlyBoundRegion { + ty::EarlyParamRegion { def_id: e.def_id, name: e.name, index: (e.index as usize - self.num_trait_args + self.num_impl_args) as u32, diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index dda3f742569..0a05c1039b6 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -81,8 +81,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( self_type_did: DefId, adt_to_impl_args: GenericArgsRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_args, CheckRegions::OnlyEarlyBound) - else { + let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_args, CheckRegions::OnlyParam) else { return Ok(()); }; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index eb009b9368f..7ea21b24fc8 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -225,25 +225,6 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ], Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), ), - sym::option_payload_ptr => { - let option_def_id = tcx.require_lang_item(hir::LangItem::Option, None); - let p0 = param(0); - ( - 1, - vec![Ty::new_ptr( - tcx, - ty::TypeAndMut { - ty: Ty::new_adt( - tcx, - tcx.adt_def(option_def_id), - tcx.mk_args_from_iter([ty::GenericArg::from(p0)].into_iter()), - ), - mutbl: hir::Mutability::Not, - }, - )], - Ty::new_ptr(tcx, ty::TypeAndMut { ty: p0, mutbl: hir::Mutability::Not }), - ) - } sym::ptr_mask => ( 1, vec![ diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 729e9f2f1d1..d7b50d127cd 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -595,9 +595,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( // Same for the region. In our example, 'a corresponds // to the 'me parameter. let region_param = gat_generics.param_at(*region_a_idx, tcx); - let region_param = ty::Region::new_early_bound( + let region_param = ty::Region::new_early_param( tcx, - ty::EarlyBoundRegion { + ty::EarlyParamRegion { def_id: region_param.def_id, index: region_param.index, name: region_param.name, @@ -628,9 +628,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( debug!("required clause: {region_a} must outlive {region_b}"); // Translate into the generic parameters of the GAT. let region_a_param = gat_generics.param_at(*region_a_idx, tcx); - let region_a_param = ty::Region::new_early_bound( + let region_a_param = ty::Region::new_early_param( tcx, - ty::EarlyBoundRegion { + ty::EarlyParamRegion { def_id: region_a_param.def_id, index: region_a_param.index, name: region_a_param.name, @@ -638,9 +638,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( ); // Same for the region. let region_b_param = gat_generics.param_at(*region_b_idx, tcx); - let region_b_param = ty::Region::new_early_bound( + let region_b_param = ty::Region::new_early_param( tcx, - ty::EarlyBoundRegion { + ty::EarlyParamRegion { def_id: region_b_param.def_id, index: region_b_param.index, name: region_b_param.name, diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index e5e192e0079..f277badf275 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -550,7 +550,7 @@ fn infringing_fields_error( .entry((ty.clone(), predicate.clone())) .or_default() .push(origin.span()); - if let ty::RegionKind::ReEarlyBound(ebr) = *b + if let ty::RegionKind::ReEarlyParam(ebr) = *b && ebr.has_name() { bounds.push((b.to_string(), a.to_string(), None)); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 9636c614446..7b5b049d254 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -440,10 +440,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { second: format!( "{}::", // Replace the existing lifetimes with a new named lifetime. - self.tcx.replace_late_bound_regions_uncached( + self.tcx.instantiate_bound_regions_uncached( poly_trait_ref, |_| { - ty::Region::new_early_bound(self.tcx, ty::EarlyBoundRegion { + ty::Region::new_early_param(self.tcx, ty::EarlyParamRegion { def_id: item_def_id, index: 0, name: Symbol::intern(<_name), diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 104da581e01..68d040d5846 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -362,10 +362,10 @@ fn compute_bidirectional_outlives_predicates<'tcx>( ) { for param in opaque_own_params { let orig_lifetime = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); - if let ty::ReEarlyBound(..) = *orig_lifetime { - let dup_lifetime = ty::Region::new_early_bound( + if let ty::ReEarlyParam(..) = *orig_lifetime { + let dup_lifetime = ty::Region::new_early_param( tcx, - ty::EarlyBoundRegion { def_id: param.def_id, index: param.index, name: param.name }, + ty::EarlyParamRegion { def_id: param.def_id, index: param.index, name: param.name }, ); let span = tcx.def_span(param.def_id); predicates.push(( diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 6424d1c7931..53efc2c6e82 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1848,8 +1848,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { - debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref); let mut late_depth = 0; let mut scope = self.scope; let lifetime = loop { @@ -2012,7 +2012,7 @@ fn is_late_bound_map( fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<!> { debug!("r={:?}", r.kind()); - if let ty::RegionKind::ReEarlyBound(region) = r.kind() { + if let ty::RegionKind::ReEarlyParam(region) = r.kind() { self.arg_is_constrained[region.index as usize] = true; } diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index ed5e9dd2b5a..65d1ffa40e2 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -13,8 +13,8 @@ impl From<ty::ParamTy> for Parameter { } } -impl From<ty::EarlyBoundRegion> for Parameter { - fn from(param: ty::EarlyBoundRegion) -> Self { +impl From<ty::EarlyParamRegion> for Parameter { + fn from(param: ty::EarlyParamRegion) -> Self { Parameter(param.index) } } @@ -73,7 +73,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector { } fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ReEarlyBound(data) = *r { + if let ty::ReEarlyParam(data) = *r { self.parameters.push(Parameter::from(data)); } ControlFlow::Continue(()) diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index d5fb4340e1c..55c01d2084b 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -5,7 +5,7 @@ use rustc_hir::{ForeignItem, ForeignItemKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Region, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -68,7 +68,13 @@ fn diagnostic_hir_wf_check<'tcx>( let infcx = self.tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(&infcx); - let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx }); + let tcx_ty = self.icx.to_ty(ty); + // This visitor can walk into binders, resulting in the `tcx_ty` to + // potentially reference escaping bound variables. We simply erase + // those here. + let tcx_ty = self.tcx.fold_regions(tcx_ty, |r, _| { + if r.is_bound() { self.tcx.lifetimes.re_erased } else { r } + }); let cause = traits::ObligationCause::new( ty.span, self.def_id, @@ -178,25 +184,3 @@ fn diagnostic_hir_wf_check<'tcx>( } visitor.cause } - -struct EraseAllBoundRegions<'tcx> { - tcx: TyCtxt<'tcx>, -} - -// Higher ranked regions are complicated. -// To make matters worse, the HIR WF check can instantiate them -// outside of a `Binder`, due to the way we (ab)use -// `ItemCtxt::to_ty`. To make things simpler, we just erase all -// of them, regardless of depth. At worse, this will give -// us an inaccurate span for an error message, but cannot -// lead to unsoundness (we call `delay_span_bug` at the start -// of `diagnostic_hir_wf_check`). -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseAllBoundRegions<'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - // FIXME(@lcnr): only erase escaping bound regions! - if r.is_bound() { self.tcx.lifetimes.re_erased } else { r } - } -} diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2b8219c01c7..bcc2051888b 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -57,9 +57,9 @@ This API is completely unstable and subject to change. #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(if_let_guard)] diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 85703c60c30..218f658b061 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -146,11 +146,11 @@ fn is_free_region(region: Region<'_>) -> bool { // These correspond to `T: 'a` relationships: // // struct Foo<'a, T> { - // field: &'a T, // this would generate a ReEarlyBound referencing `'a` + // field: &'a T, // this would generate a ReEarlyParam referencing `'a` // } // // We care about these, so fall through. - ty::ReEarlyBound(_) => true, + ty::ReEarlyParam(_) => true, // These correspond to `T: 'static` relationships which can be // rather surprising. @@ -173,7 +173,7 @@ fn is_free_region(region: Region<'_>) -> bool { ty::ReError(_) => false, // These regions don't appear in types from type declarations: - ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => { + ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReLateParam(..) => { bug!("unexpected region in outlives inference: {:?}", region); } } diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 6c1efb6470e..f09594cbbc6 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -413,7 +413,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { variance: VarianceTermPtr<'a>, ) { match *region { - ty::ReEarlyBound(ref data) => { + ty::ReEarlyParam(ref data) => { self.add_constraint(current, data.index, variance); } @@ -428,7 +428,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { ty::ReError(_) => {} - ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => { + ty::ReLateParam(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. bug!( diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 9fb39a0e93b..410706110c9 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -106,7 +106,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeLifetimeCollector<'tcx> { #[instrument(level = "trace", skip(self), ret)] fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() { + if let ty::RegionKind::ReEarlyParam(ebr) = r.kind() { self.variances[ebr.index as usize] = ty::Invariant; } ControlFlow::Continue(()) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 6b6d1574b2b..173186e6d9f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -625,6 +625,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = callee_expr.kind + && let Res::Local(_) = path.res + && let [segment] = &path.segments[..] + { + for id in self.tcx.hir().items() { + if let Some(node) = self.tcx.hir().get_if_local(id.owner_id.into()) + && let hir::Node::Item(item) = node + && let hir::ItemKind::Fn(..) = item.kind + && item.ident.name == segment.ident.name + { + err.span_label( + self.tcx.def_span(id.owner_id), + "this function of the same name is available here, but it's shadowed by \ + the local binding", + ); + } + } + } + let mut inner_callee_path = None; let def = match callee_expr.kind { hir::ExprKind::Path(ref qpath) => { diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index acaa3e02f09..386e4b4642a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -31,9 +31,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.annotate_alternative_method_deref(err, expr, error); + self.explain_self_literal(err, expr, expected, expr_ty); // Use `||` to give these suggestions a precedence let suggested = self.suggest_missing_parentheses(err, expr) + || self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) @@ -49,8 +51,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected) || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected) - || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty) - || self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty); + || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty); if !suggested { self.note_source_of_type_mismatch_constraint( @@ -1027,6 +1028,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } + fn explain_self_literal( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + match expr.peel_drop_temps().kind { + hir::ExprKind::Struct( + hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::SelfTyAlias { alias_to, .. }, span, .. }, + ), + .., + ) + | hir::ExprKind::Call( + hir::Expr { + kind: + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { + res: hir::def::Res::SelfTyAlias { alias_to, .. }, + span, + .. + }, + )), + .. + }, + .., + ) => { + if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + })) = self.tcx.hir().get_if_local(*alias_to) + { + err.span_label(self_ty.span, "this is the type of the `Self` literal"); + } + if let ty::Adt(e_def, e_args) = expected.kind() + && let ty::Adt(f_def, _f_args) = found.kind() + && e_def == f_def + { + err.span_suggestion_verbose( + *span, + "use the type name directly", + self.tcx.value_path_str_with_args(*alias_to, e_args), + Applicability::MaybeIncorrect, + ); + } + } + _ => {} + } + } + fn note_wrong_return_ty_due_to_generic_arg( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 811d8a4b94e..6c99044d0aa 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -21,7 +21,7 @@ use crate::{ TupleArgumentsFlag::DontTupleArguments, }; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ pluralize, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, @@ -2191,7 +2191,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(field_name) = find_best_match_for_name(&available_field_names, field.ident.name, None) { - err.span_suggestion( + err.span_label(field.ident.span, "unknown field"); + err.span_suggestion_verbose( field.ident.span, "a field with a similar name exists", field_name, @@ -2420,35 +2421,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, ) { let Some(output_ty) = self.get_impl_future_output_ty(ty) else { + err.span_label(field_ident.span, "unknown field"); return; }; - let mut add_label = true; - if let ty::Adt(def, _) = output_ty.kind() { - // no field access on enum type - if !def.is_enum() { - if def - .non_enum_variant() - .fields - .iter() - .any(|field| field.ident(self.tcx) == field_ident) - { - add_label = false; - err.span_label( - field_ident.span, - "field not available in `impl Future`, but it is available in its `Output`", - ); - err.span_suggestion_verbose( - base.span.shrink_to_hi(), - "consider `await`ing on the `Future` and access the field of its `Output`", - ".await", - Applicability::MaybeIncorrect, - ); - } - } + let ty::Adt(def, _) = output_ty.kind() else { + err.span_label(field_ident.span, "unknown field"); + return; + }; + // no field access on enum type + if def.is_enum() { + err.span_label(field_ident.span, "unknown field"); + return; } - if add_label { - err.span_label(field_ident.span, format!("field not found in `{ty}`")); + if !def.non_enum_variant().fields.iter().any(|field| field.ident(self.tcx) == field_ident) { + err.span_label(field_ident.span, "unknown field"); + return; } + err.span_label( + field_ident.span, + "field not available in `impl Future`, but it is available in its `Output`", + ); + err.span_suggestion_verbose( + base.span.shrink_to_hi(), + "consider `await`ing on the `Future` and access the field of its `Output`", + ".await", + Applicability::MaybeIncorrect, + ); } fn ban_nonexisting_field( @@ -2471,16 +2469,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::RawPtr(..) => { self.suggest_first_deref_field(&mut err, expr, base, ident); } - ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, expr, def, ident); - } ty::Param(param_ty) => { + err.span_label(ident.span, "unknown field"); self.point_at_param_definition(&mut err, param_ty); } ty::Alias(ty::Opaque, _) => { self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); } - _ => {} + _ => { + err.span_label(ident.span, "unknown field"); + } } self.suggest_fn_call(&mut err, base, base_ty, |output_ty| { @@ -2633,34 +2631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(param_span, format!("type parameter '{param_name}' declared here")); } - fn suggest_fields_on_recordish( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - def: ty::AdtDef<'tcx>, - field: Ident, - ) { - let available_field_names = self.available_field_names(def.non_enum_variant(), expr, &[]); - if let Some(suggested_field_name) = - find_best_match_for_name(&available_field_names, field.name, None) - { - err.span_suggestion( - field.span, - "a field with a similar name exists", - suggested_field_name, - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(field.span, "unknown field"); - if !available_field_names.is_empty() { - err.note(format!( - "available fields are: {}", - self.name_series_display(available_field_names), - )); - } - } - } - fn maybe_suggest_array_indexing( &self, err: &mut Diagnostic, @@ -2669,6 +2639,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: Ident, len: ty::Const<'tcx>, ) { + err.span_label(field.span, "unknown field"); if let (Some(len), Ok(user_index)) = (len.try_eval_target_usize(self.tcx, self.param_env), field.as_str().parse::<u64>()) && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) @@ -2691,6 +2662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &hir::Expr<'_>, field: Ident, ) { + err.span_label(field.span, "unknown field"); if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { let msg = format!("`{base}` is a raw pointer; try dereferencing it"); let suggestion = format!("(*{base}).{field}"); @@ -2709,7 +2681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = type_error_struct!( self.tcx().sess, - field.span, + span, expr_t, E0609, "no field `{field}` on type `{expr_t}`", @@ -2717,10 +2689,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // try to add a suggestion in case the field is a nested field of a field of the Adt let mod_id = self.tcx.parent_module(id).to_def_id(); - if let Some((fields, args)) = - self.get_field_candidates_considering_privacy(span, expr_t, mod_id) + let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind() + && (self.tcx.is_diagnostic_item(sym::Result, def.did()) + || self.tcx.is_diagnostic_item(sym::Option, def.did())) + && let Some(arg) = args.get(0) + && let Some(ty) = arg.as_type() + { + (ty, "unwrap().") + } else { + (expr_t, "") + }; + for (found_fields, args) in + self.get_field_candidates_considering_privacy(span, ty, mod_id, id) { - let candidate_fields: Vec<_> = fields + let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>(); + let candidate_fields: Vec<_> = found_fields + .into_iter() .filter_map(|candidate_field| { self.check_for_nested_field_satisfying( span, @@ -2729,15 +2713,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, vec![], mod_id, + id, ) }) .map(|mut field_path| { field_path.pop(); field_path .iter() - .map(|id| id.name.to_ident_string()) - .collect::<Vec<String>>() - .join(".") + .map(|id| format!("{}.", id.name.to_ident_string())) + .collect::<String>() }) .collect::<Vec<_>>(); @@ -2750,9 +2734,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if len > 1 { "some" } else { "one" }, if len > 1 { "have" } else { "has" }, ), - candidate_fields.iter().map(|path| format!("{path}.")), + candidate_fields.iter().map(|path| format!("{unwrap}{path}")), Applicability::MaybeIncorrect, ); + } else { + if let Some(field_name) = find_best_match_for_name(&field_names, field.name, None) { + err.span_suggestion_verbose( + field.span, + "a field with a similar name exists", + format!("{unwrap}{}", field_name), + Applicability::MaybeIncorrect, + ); + } else if !field_names.is_empty() { + let is = if field_names.len() == 1 { " is" } else { "s are" }; + err.note(format!( + "available field{is}: {}", + self.name_series_display(field_names), + )); + } } } err @@ -2781,33 +2780,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, base_ty: Ty<'tcx>, mod_id: DefId, - ) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, GenericArgsRef<'tcx>)> { + hir_id: hir::HirId, + ) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> { debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); - for (base_t, _) in self.autoderef(span, base_ty) { - match base_t.kind() { - ty::Adt(base_def, args) if !base_def.is_enum() => { - let tcx = self.tcx; - let fields = &base_def.non_enum_variant().fields; - // Some struct, e.g. some that impl `Deref`, have all private fields - // because you're expected to deref them to access the _real_ fields. - // This, for example, will help us suggest accessing a field through a `Box<T>`. - if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) { - continue; + self.autoderef(span, base_ty) + .filter_map(move |(base_t, _)| { + match base_t.kind() { + ty::Adt(base_def, args) if !base_def.is_enum() => { + let tcx = self.tcx; + let fields = &base_def.non_enum_variant().fields; + // Some struct, e.g. some that impl `Deref`, have all private fields + // because you're expected to deref them to access the _real_ fields. + // This, for example, will help us suggest accessing a field through a `Box<T>`. + if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) { + return None; + } + return Some(( + fields + .iter() + .filter(move |field| { + field.vis.is_accessible_from(mod_id, tcx) + && self.is_field_suggestable(field, hir_id, span) + }) + // For compile-time reasons put a limit on number of fields we search + .take(100) + .collect::<Vec<_>>(), + *args, + )); } - return Some(( - fields - .iter() - .filter(move |field| field.vis.is_accessible_from(mod_id, tcx)) - // For compile-time reasons put a limit on number of fields we search - .take(100), - args, - )); + _ => None, } - _ => {} - } - } - None + }) + .collect() } /// This method is called after we have encountered a missing field error to recursively @@ -2820,6 +2825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subst: GenericArgsRef<'tcx>, mut field_path: Vec<Ident>, mod_id: DefId, + hir_id: HirId, ) -> Option<Vec<Ident>> { debug!( "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}", @@ -2835,20 +2841,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_ty = candidate_field.ty(self.tcx, subst); if matches(candidate_field, field_ty) { return Some(field_path); - } else if let Some((nested_fields, subst)) = - self.get_field_candidates_considering_privacy(span, field_ty, mod_id) - { - // recursively search fields of `candidate_field` if it's a ty::Adt - for field in nested_fields { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - matches, - field, - subst, - field_path.clone(), - mod_id, - ) { - return Some(field_path); + } else { + for (nested_fields, subst) in + self.get_field_candidates_considering_privacy(span, field_ty, mod_id, hir_id) + { + // recursively search fields of `candidate_field` if it's a ty::Adt + for field in nested_fields { + if let Some(field_path) = self.check_for_nested_field_satisfying( + span, + matches, + field, + subst, + field_path.clone(), + mod_id, + hir_id, + ) { + return Some(field_path); + } } } } @@ -2877,7 +2886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // two-phase not needed because index_ty is never mutable self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); self.select_obligations_where_possible(|errors| { - self.point_at_index_if_possible(errors, idx.span) + self.point_at_index(errors, idx.span); }); element_ty } @@ -3036,16 +3045,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .ok() } - fn point_at_index_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - span: Span, - ) { + fn point_at_index(&self, errors: &mut Vec<traits::FulfillmentError<'tcx>>, span: Span) { + let mut seen_preds = FxHashSet::default(); + // We re-sort here so that the outer most root obligations comes first, as we have the + // subsequent weird logic to identify *every* relevant obligation for proper deduplication + // of diagnostics. + errors.sort_by_key(|error| error.root_obligation.recursion_depth); for error in errors { - match error.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) - if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { + match ( + error.root_obligation.predicate.kind().skip_binder(), + error.obligation.predicate.kind().skip_binder(), + ) { + (ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)), _) + if self.tcx.lang_items().index_trait() == Some(predicate.trait_ref.def_id) => + { + seen_preds.insert(error.obligation.predicate.kind().skip_binder()); } + (_, ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate))) + if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => + { + seen_preds.insert(error.obligation.predicate.kind().skip_binder()); + } + (root, pred) if seen_preds.contains(&pred) || seen_preds.contains(&root) => {} _ => continue, } error.obligation.cause.span = span; @@ -3173,19 +3194,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sym::offset_of_enum, ident.span, "using enums in offset_of is experimental", - ).emit(); + ) + .emit(); } - let Some((index, variant)) = container_def.variants() + let Some((index, variant)) = container_def + .variants() .iter_enumerated() - .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) else { + .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident) + else { let mut err = type_error_struct!( self.tcx().sess, ident.span, container, E0599, "no variant named `{ident}` found for enum `{container}`", - ); + ); err.span_label(field.span, "variant not found"); err.emit(); break; @@ -3197,7 +3221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { container, E0795, "`{ident}` is an enum variant; expected field at end of `offset_of`", - ); + ); err.span_label(field.span, "enum variant"); err.emit(); break; @@ -3205,16 +3229,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (subident, sub_def_scope) = self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block); - let Some((subindex, field)) = variant.fields + let Some((subindex, field)) = variant + .fields .iter_enumerated() - .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident) else { + .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident) + else { let mut err = type_error_struct!( self.tcx().sess, ident.span, container, E0609, "no field named `{subfield}` on enum variant `{container}::{ident}`", - ); + ); err.span_label(field.span, "this enum variant..."); err.span_label(subident.span, "...does not have this field"); err.emit(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 33dfa16a651..f31e50fd352 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1864,35 +1864,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ExprBindingObligation(_, _, hir_id, ..) = code && !fn_sig.output().is_unit() { - let mut block_num = 0; - let mut found_semi = false; - for (_, node) in self.tcx.hir().parent_iter(hir_id) { - match node { - hir::Node::Stmt(stmt) => if let hir::StmtKind::Semi(ref expr) = stmt.kind { + let mut block_num = 0; + let mut found_semi = false; + for (_, node) in self.tcx.hir().parent_iter(hir_id) { + match node { + hir::Node::Stmt(stmt) => { + if let hir::StmtKind::Semi(ref expr) = stmt.kind { let expr_ty = self.typeck_results.borrow().expr_ty(expr); let return_ty = fn_sig.output(); - if !matches!(expr.kind, hir::ExprKind::Ret(..)) && - self.can_coerce(expr_ty, return_ty) { + if !matches!(expr.kind, hir::ExprKind::Ret(..)) + && self.can_coerce(expr_ty, return_ty) + { found_semi = true; } - }, - hir::Node::Block(_block) => if found_semi { + } + } + hir::Node::Block(_block) => { + if found_semi { block_num += 1; } - hir::Node::Item(item) => if let hir::ItemKind::Fn(..) = item.kind { + } + hir::Node::Item(item) => { + if let hir::ItemKind::Fn(..) = item.kind { break; } - _ => {} } + _ => {} } - if block_num > 1 && found_semi { - diag.span_suggestion_verbose( - span.shrink_to_lo(), - "you might have meant to return this to infer its type parameters", - "return ", - Applicability::MaybeIncorrect, - ); - } + } + if block_num > 1 && found_semi { + diag.span_suggestion_verbose( + span.shrink_to_lo(), + "you might have meant to return this to infer its type parameters", + "return ", + Applicability::MaybeIncorrect, + ); + } } diag.emit(); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index a1ce5983f30..bd653e79913 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -238,7 +238,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), + Some(def) => infer::RegionParameterDefinition(span, def.name), None => infer::MiscVariable(span), }; Some(self.next_region_var(v)) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c43d4932fb9..a37a595e4a8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -846,7 +846,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); let ty = Binder::bind_with_vars(ty, bound_vars); let ty = self.normalize(hir_ty.span, ty); - let ty = self.tcx.erase_late_bound_regions(ty); + let ty = self.tcx.instantiate_bound_regions_with_erased(ty); if self.can_coerce(expected, ty) { err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, @@ -1023,7 +1023,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::FnRetTy::Return(ty) = fn_decl.output { let ty = self.astconv().ast_ty_to_ty(ty); let bound_vars = self.tcx.late_bound_vars(fn_id); - let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); + let ty = self + .tcx + .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars)); let ty = match self.tcx.asyncness(fn_id.owner) { ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { span_bug!(fn_decl.output.span(), "failed to get output type of async function") @@ -2326,14 +2328,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - let needs_parens = match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - }; - if let Some((sugg, msg)) = self.can_use_as_ref(expr) { return Some(( sugg, @@ -2361,18 +2355,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let sugg = mutability.ref_prefix_str(); - let (sugg, verbose) = if needs_parens { - ( - vec![ - (sp.shrink_to_lo(), format!("{prefix}{sugg}(")), - (sp.shrink_to_hi(), ")".to_string()), - ], - false, - ) - } else { - (vec![(sp.shrink_to_lo(), format!("{prefix}{sugg}"))], true) + let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| { + let needs_parens = match expr.kind { + // parenthesize if needed (Issue #46756) + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + }; + + if needs_parens { + ( + vec![ + (span.shrink_to_lo(), format!("{prefix}{sugg}(")), + (span.shrink_to_hi(), ")".to_string()), + ], + false, + ) + } else { + (vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true) + } }; + + // Suggest dereferencing the lhs for expressions such as `&T == T` + if let Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Binary(_, lhs, ..), + .. + })) = self.tcx.hir().find_parent(expr.hir_id) + && let &ty::Ref(..) = self.check_expr(lhs).kind() + { + let (sugg, verbose) = make_sugg(lhs, lhs.span, "*"); + + return Some(( + sugg, + "consider dereferencing the borrow".to_string(), + Applicability::MachineApplicable, + verbose, + false, + )); + } + + let sugg = mutability.ref_prefix_str(); + let (sugg, verbose) = make_sugg(expr, sp, sugg); return Some(( sugg, format!("consider {}borrowing here", mutability.mutably_str()), diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a51fa8354c9..d2a72f8f233 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -809,7 +809,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return; } - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); + let new_trait_ref = this.instantiate_bound_regions_with_erased(new_trait_ref); let (xform_self_ty, xform_ret_ty) = this.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.args); @@ -1885,7 +1885,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn_sig.instantiate(self.tcx, args) }; - self.erase_late_bound_regions(xform_fn_sig) + self.instantiate_bound_regions_with_erased(xform_fn_sig) } /// Gets the type of an impl and generate substitutions with inference vars. @@ -1897,7 +1897,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } /// Replaces late-bound-regions bound by `value` with `'static` using - /// `ty::erase_late_bound_regions`. + /// `ty::instantiate_bound_regions_with_erased`. /// /// This is only a reasonable thing to do during the *probe* phase, not the *confirm* phase, of /// method matching. It is reasonable during the probe phase because we don't consider region @@ -1914,11 +1914,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// region got replaced with the same variable, which requires a bit more coordination /// and/or tracking the substitution and /// so forth. - fn erase_late_bound_regions<T>(&self, value: ty::Binder<'tcx, T>) -> T + fn instantiate_bound_regions_with_erased<T>(&self, value: ty::Binder<'tcx, T>) -> T where T: TypeFoldable<TyCtxt<'tcx>>, { - self.tcx.erase_late_bound_regions(value) + self.tcx.instantiate_bound_regions_with_erased(value) } /// Determine if the given associated item type is relevant in the current context. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index bd40e6c1047..6956778f974 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1,6 +1,8 @@ //! Give useful errors and suggestions to users when an item can't be //! found or is otherwise invalid. +// ignore-tidy-filelength + use crate::errors; use crate::errors::{CandidateTraitNote, NoAssociatedItem}; use crate::Expectation; @@ -369,25 +371,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_name.name == Symbol::intern("write_fmt"); - let mut err = - if is_write && let SelfSource::MethodCall(rcvr_expr) = source - { - self.suggest_missing_writer(rcvr_ty, rcvr_expr) - } else { - tcx.sess.create_err(NoAssociatedItem { - span, - item_kind, - item_name, - ty_prefix: if trait_missing_method { - // FIXME(mu001999) E0599 maybe not suitable here because it is for types - Cow::from("trait") - } else { - rcvr_ty.prefix_string(self.tcx) - }, - ty_str: ty_str_reported, - trait_missing_method, - }) - }; + let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_missing_writer(rcvr_ty, rcvr_expr) + } else { + tcx.sess.create_err(NoAssociatedItem { + span, + item_kind, + item_name, + ty_prefix: if trait_missing_method { + // FIXME(mu001999) E0599 maybe not suitable here because it is for types + Cow::from("trait") + } else { + rcvr_ty.prefix_string(self.tcx) + }, + ty_str: ty_str_reported, + trait_missing_method, + }) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } @@ -1240,20 +1240,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } // If an appropriate error source is not found, check method chain for possible candiates - if unsatisfied_predicates.is_empty() && let Mode::MethodCall = mode && let SelfSource::MethodCall(mut source_expr) = source { + if unsatisfied_predicates.is_empty() + && let Mode::MethodCall = mode + && let SelfSource::MethodCall(mut source_expr) = source + { let mut stack_methods = vec![]; while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, method_span) = - source_expr.kind + source_expr.kind { - // Pop the matching receiver, to align on it's notional span - if let Some(prev_match) = stack_methods.pop() { - err.span_label(method_span, format!("{item_kind} `{item_name}` is available on `{prev_match}`")); + // Pop the matching receiver, to align on it's notional span + if let Some(prev_match) = stack_methods.pop() { + err.span_label( + method_span, + format!("{item_kind} `{item_name}` is available on `{prev_match}`"), + ); } let rcvr_ty = self.resolve_vars_if_possible( self.typeck_results .borrow() .expr_ty_adjusted_opt(rcvr_expr) - .unwrap_or(Ty::new_misc_error(self.tcx)),); + .unwrap_or(Ty::new_misc_error(self.tcx)), + ); for _matched_method in self.probe_for_name_many( Mode::MethodCall, @@ -1262,15 +1269,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), rcvr_ty, source_expr.hir_id, - ProbeScope::TraitsInScope,) { - // found a match, push to stack - stack_methods.push(rcvr_ty); + ProbeScope::TraitsInScope, + ) { + // found a match, push to stack + stack_methods.push(rcvr_ty); } source_expr = rcvr_expr; } // If there is a match at the start of the chain, add a label for it too! if let Some(prev_match) = stack_methods.pop() { - err.span_label(source_expr.span, format!("{item_kind} `{item_name}` is available on `{prev_match}`")); + err.span_label( + source_expr.span, + format!("{item_kind} `{item_name}` is available on `{prev_match}`"), + ); } } self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected); @@ -1357,10 +1368,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err, self_source, args, - trait_ref.instantiate( - self.tcx, - self.fresh_args_for_item(sugg_span, impl_did) - ).with_self_ty(self.tcx, rcvr_ty), + trait_ref + .instantiate( + self.tcx, + self.fresh_args_for_item(sugg_span, impl_did), + ) + .with_self_ty(self.tcx, rcvr_ty), idx, sugg_span, item, @@ -1397,8 +1410,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::TraitRef::new( self.tcx, trait_did, - self.fresh_args_for_item(sugg_span, trait_did) - ).with_self_ty(self.tcx, rcvr_ty), + self.fresh_args_for_item(sugg_span, trait_did), + ) + .with_self_ty(self.tcx, rcvr_ty), idx, sugg_span, item, @@ -1409,7 +1423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if !suggs.is_empty() && let Some(span) = sugg_span { + if !suggs.is_empty() + && let Some(span) = sugg_span + { err.span_suggestions( span.with_hi(item_name.span.lo()), "use fully-qualified syntax to disambiguate", @@ -1438,7 +1454,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter_map(|item| { // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`. let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output(); - let ret_ty = self.tcx.erase_late_bound_regions(ret_ty); + let ret_ty = self.tcx.instantiate_bound_regions_with_erased(ret_ty); let ty::Adt(def, args) = ret_ty.kind() else { return None; }; @@ -1585,39 +1601,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut applicability = Applicability::MachineApplicable; let args = if let SelfSource::MethodCall(receiver) = source - && let Some(args) = args - { - // The first arg is the same kind as the receiver - let explicit_args = if first_arg.is_some() { - std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>() - } else { - // There is no `Self` kind to infer the arguments from - if has_unsuggestable_args { - applicability = Applicability::HasPlaceholders; - } - args.iter().collect() - }; - format!( - "({}{})", - first_arg.unwrap_or(""), - explicit_args - .iter() - .map(|arg| self - .tcx - .sess - .source_map() - .span_to_snippet(arg.span) - .unwrap_or_else(|_| { - applicability = Applicability::HasPlaceholders; - "_".to_owned() - })) - .collect::<Vec<_>>() - .join(", "), - ) + && let Some(args) = args + { + // The first arg is the same kind as the receiver + let explicit_args = if first_arg.is_some() { + std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>() } else { - applicability = Applicability::HasPlaceholders; - "(...)".to_owned() + // There is no `Self` kind to infer the arguments from + if has_unsuggestable_args { + applicability = Applicability::HasPlaceholders; + } + args.iter().collect() }; + format!( + "({}{})", + first_arg.unwrap_or(""), + explicit_args + .iter() + .map(|arg| self + .tcx + .sess + .source_map() + .span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::<Vec<_>>() + .join(", "), + ) + } else { + applicability = Applicability::HasPlaceholders; + "(...)".to_owned() + }; err.span_suggestion( sugg_span, "use associated function syntax instead", @@ -1967,69 +1983,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, return_type: Option<Ty<'tcx>>, ) { - if let SelfSource::MethodCall(expr) = source - && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() - && let Some((fields, args)) = - self.get_field_candidates_considering_privacy(span, actual, mod_id) - { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); + if let SelfSource::MethodCall(expr) = source { + let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); + for (fields, args) in + self.get_field_candidates_considering_privacy(span, actual, mod_id, expr.hir_id) + { + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); - let lang_items = self.tcx.lang_items(); - let never_mention_traits = [ - lang_items.clone_trait(), - lang_items.deref_trait(), - lang_items.deref_mut_trait(), - self.tcx.get_diagnostic_item(sym::AsRef), - self.tcx.get_diagnostic_item(sym::AsMut), - self.tcx.get_diagnostic_item(sym::Borrow), - self.tcx.get_diagnostic_item(sym::BorrowMut), - ]; - let candidate_fields: Vec<_> = fields - .filter_map(|candidate_field| { - self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe_for_diagnostic( - item_name, - field_ty, - call_expr, - ProbeScope::TraitsInScope, - return_type, - ) - .is_ok_and(|pick| { - !never_mention_traits - .iter() - .flatten() - .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) - }) - }, - candidate_field, - args, - vec![], - mod_id, - ) - }) - .map(|field_path| { - field_path - .iter() - .map(|id| id.name.to_ident_string()) - .collect::<Vec<String>>() - .join(".") - }) - .collect(); + let lang_items = self.tcx.lang_items(); + let never_mention_traits = [ + lang_items.clone_trait(), + lang_items.deref_trait(), + lang_items.deref_mut_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), + self.tcx.get_diagnostic_item(sym::AsMut), + self.tcx.get_diagnostic_item(sym::Borrow), + self.tcx.get_diagnostic_item(sym::BorrowMut), + ]; + let candidate_fields: Vec<_> = fields + .iter() + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe_for_diagnostic( + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + return_type, + ) + .is_ok_and(|pick| { + !never_mention_traits + .iter() + .flatten() + .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) + }) + }, + candidate_field, + args, + vec![], + mod_id, + expr.hir_id, + ) + }) + .map(|field_path| { + field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::<Vec<String>>() + .join(".") + }) + .collect(); - let len = candidate_fields.len(); - if len > 0 { - err.span_suggestions( - item_name.span.shrink_to_lo(), - format!( - "{} of the expressions' fields {} a method of the same name", - if len > 1 { "some" } else { "one" }, - if len > 1 { "have" } else { "has" }, - ), - candidate_fields.iter().map(|path| format!("{path}.")), - Applicability::MaybeIncorrect, - ); + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + item_name.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a method of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); + } } } } @@ -3296,8 +3315,13 @@ fn print_disambiguation_help<'tcx>( { let def_kind_descr = tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id); let item_name = item.ident(tcx); - let rcvr_ref = tcx.fn_sig(item.def_id).skip_binder().skip_binder().inputs()[0] - .ref_mutability() + let rcvr_ref = tcx + .fn_sig(item.def_id) + .skip_binder() + .skip_binder() + .inputs() + .get(0) + .and_then(|ty| ty.ref_mutability()) .map_or("", |mutbl| mutbl.ref_prefix_str()); let args = format!( "({}{})", diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 2dfe7272673..2462ac93632 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -768,41 +768,22 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { } } -struct EraseEarlyRegions<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEarlyRegions<'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_type_flags(ty::TypeFlags::HAS_FREE_REGIONS) { - ty.super_fold_with(self) - } else { - ty - } - } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if r.is_bound() { r } else { self.tcx.lifetimes.re_erased } - } -} - impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> { fn interner(&self) -> TyCtxt<'tcx> { self.fcx.tcx } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + let tcx = self.fcx.tcx; match self.fcx.fully_resolve(t) { Ok(t) if self.fcx.next_trait_solver() => { // We must normalize erasing regions here, since later lints // expect that types that show up in the typeck are fully // normalized. - if let Ok(t) = self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t) { + if let Ok(t) = tcx.try_normalize_erasing_regions(self.fcx.param_env, t) { t } else { - EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t) + tcx.fold_regions(t, |_, _| tcx.lifetimes.re_erased) } } Ok(t) => { @@ -810,7 +791,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> { // (e.g. keep `for<'a>` named `for<'a>`). // This allows NLL to generate error messages that // refer to the higher-ranked lifetime names written by the user. - EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t) + tcx.fold_regions(t, |_, _| tcx.lifetimes.re_erased) } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index dde138973b9..81d4d914287 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -2,9 +2,9 @@ #![deny(missing_docs)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index ece61ff1252..d0b4889b45f 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -237,23 +237,12 @@ impl<T: Idx> BitSet<T> { new_word != word } - /// Gets a slice of the underlying words. - pub fn words(&self) -> &[Word] { - &self.words - } - /// Iterates over the indices of set bits in a sorted order. #[inline] pub fn iter(&self) -> BitIter<'_, T> { BitIter::new(&self.words) } - /// Duplicates the set as a hybrid set. - pub fn to_hybrid(&self) -> HybridBitSet<T> { - // Note: we currently don't bother trying to make a Sparse set. - HybridBitSet::Dense(self.to_owned()) - } - /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`). /// @@ -1601,11 +1590,11 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> { pub fn from_row_n(row: &BitSet<C>, num_rows: usize) -> BitMatrix<R, C> { let num_columns = row.domain_size(); let words_per_row = num_words(num_columns); - assert_eq!(words_per_row, row.words().len()); + assert_eq!(words_per_row, row.words.len()); BitMatrix { num_rows, num_columns, - words: iter::repeat(row.words()).take(num_rows).flatten().cloned().collect(), + words: iter::repeat(&row.words).take(num_rows).flatten().cloned().collect(), marker: PhantomData, } } @@ -1700,9 +1689,9 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> { assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); let mut changed = false; - for (read_index, write_index) in iter::zip(0..with.words().len(), write_start..write_end) { + for (read_index, write_index) in iter::zip(0..with.words.len(), write_start..write_end) { let word = self.words[write_index]; - let new_word = word | with.words()[read_index]; + let new_word = word | with.words[read_index]; self.words[write_index] = new_word; changed |= word != new_word; } @@ -2002,54 +1991,6 @@ impl std::fmt::Debug for FiniteBitSet<u32> { } } -impl FiniteBitSetTy for u64 { - const DOMAIN_SIZE: u32 = 64; - - const FILLED: Self = Self::MAX; - const EMPTY: Self = Self::MIN; - - const ONE: Self = 1u64; - const ZERO: Self = 0u64; - - fn checked_shl(self, rhs: u32) -> Option<Self> { - self.checked_shl(rhs) - } - - fn checked_shr(self, rhs: u32) -> Option<Self> { - self.checked_shr(rhs) - } -} - -impl std::fmt::Debug for FiniteBitSet<u64> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:064b}", self.0) - } -} - -impl FiniteBitSetTy for u128 { - const DOMAIN_SIZE: u32 = 128; - - const FILLED: Self = Self::MAX; - const EMPTY: Self = Self::MIN; - - const ONE: Self = 1u128; - const ZERO: Self = 0u128; - - fn checked_shl(self, rhs: u32) -> Option<Self> { - self.checked_shl(rhs) - } - - fn checked_shr(self, rhs: u32) -> Option<Self> { - self.checked_shr(rhs) - } -} - -impl std::fmt::Debug for FiniteBitSet<u128> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:0128b}", self.0) - } -} - /// A fixed-sized bitset type represented by an integer type. Indices outwith than the range /// representable by `T` are considered set. #[derive(Copy, Clone, Eq, PartialEq, Decodable, Encodable)] diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 99e72e49f8e..66c5cc774b2 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -137,10 +137,6 @@ impl<I: Idx, T> IndexVec<I, T> { self.raw.truncate(a) } - pub fn convert_index_type<Ix: Idx>(self) -> IndexVec<Ix, T> { - IndexVec::from_raw(self.raw) - } - /// Grows the index vector so that it contains an entry for /// `elem`; if that is already true, then has no /// effect. Otherwise, inserts new values as needed by invoking diff --git a/compiler/rustc_index/src/vec/tests.rs b/compiler/rustc_index/src/vec/tests.rs index cb0f0db220d..7e5e41bd2d1 100644 --- a/compiler/rustc_index/src/vec/tests.rs +++ b/compiler/rustc_index/src/vec/tests.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - // Allows the macro invocation below to work use crate as rustc_index; diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 869878420ae..3ee4710bdef 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -17,7 +17,7 @@ impl<'a> DescriptionCtx<'a> { alt_span: Option<Span>, ) -> Option<Self> { let (span, kind, arg) = match *region { - ty::ReEarlyBound(ref br) => { + ty::ReEarlyParam(ref br) => { let scope = region.free_region_binding_scope(tcx).expect_local(); let span = if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) @@ -32,7 +32,7 @@ impl<'a> DescriptionCtx<'a> { (Some(span), "as_defined_anon", String::new()) } } - ty::ReFree(ref fr) => { + ty::ReLateParam(ref fr) => { if !fr.bound_region.is_named() && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) { @@ -70,12 +70,12 @@ impl<'a> DescriptionCtx<'a> { ty::RePlaceholder(_) | ty::ReError(_) => return None, // FIXME(#13998) RePlaceholder should probably print like - // ReFree rather than dumping Debug output on the user. + // ReLateParam rather than dumping Debug output on the user. // // We shouldn't really be having unification failures with ReVar // and ReBound though. // - // FIXME(@lcnr): figure out why we `ReBound` have to handle `ReBound` + // FIXME(@lcnr): figure out why we have to handle `ReBound` // here, this feels somewhat off. ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => { (alt_span, "revar", format!("{region:?}")) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index ff8e354f53d..b502590c1bf 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -13,7 +13,6 @@ use rustc_middle::ty::flags::FlagComputation; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::GenericArg; use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; -use std::sync::atomic::Ordering; use rustc_data_structures::fx::FxHashMap; use rustc_index::Idx; @@ -43,8 +42,6 @@ impl<'tcx> InferCtxt<'tcx> { where V: TypeFoldable<TyCtxt<'tcx>>, { - self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); - Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state) } @@ -62,8 +59,6 @@ impl<'tcx> InferCtxt<'tcx> { where V: TypeFoldable<TyCtxt<'tcx>>, { - self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); - Canonicalizer::canonicalize( value, self, @@ -138,8 +133,6 @@ impl<'tcx> InferCtxt<'tcx> { where V: TypeFoldable<TyCtxt<'tcx>>, { - self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); - Canonicalizer::canonicalize( value, self, @@ -179,7 +172,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match *r { - ty::ReFree(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r, + ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r, ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) }, @@ -230,7 +223,11 @@ impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic | ty::ReError(_) => r, + ty::ReEarlyParam(_) + | ty::ReLateParam(_) + | ty::ReErased + | ty::ReStatic + | ty::ReError(_) => r, ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), ty::RePlaceholder(..) | ty::ReBound(..) => { // We only expect region names that the user can type. @@ -366,9 +363,9 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> { } ty::ReStatic - | ty::ReEarlyBound(..) + | ty::ReEarlyParam(..) | ty::ReError(_) - | ty::ReFree(_) + | ty::ReLateParam(_) | ty::RePlaceholder(..) | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f09a33be839..56e3280088e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -168,7 +168,7 @@ pub(super) fn note_and_explain_region<'tcx>( alt_span: Option<Span>, ) { let (description, span) = match *region { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::RePlaceholder(_) | ty::ReStatic => { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { msg_span_from_named_region(tcx, region, alt_span) } @@ -202,7 +202,7 @@ fn msg_span_from_named_region<'tcx>( alt_span: Option<Span>, ) -> (String, Option<Span>) { match *region { - ty::ReEarlyBound(ref br) => { + ty::ReEarlyParam(ref br) => { let scope = region.free_region_binding_scope(tcx).expect_local(); let span = if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) @@ -218,7 +218,7 @@ fn msg_span_from_named_region<'tcx>( }; (text, Some(span)) } - ty::ReFree(ref fr) => { + ty::ReLateParam(ref fr) => { if !fr.bound_region.is_named() && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) { @@ -315,7 +315,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( // Explain the region we are capturing. match *hidden_region { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { // Assuming regionck succeeded (*), we ought to always be // capturing *some* region from the fn header, and hence it // ought to be free. So under normal circumstances, we will go @@ -860,7 +860,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_boxing_for_return_impl_trait( err, ret_sp, - prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s), + prior_arms.iter().chain(std::iter::once(&arm_span)).copied(), ); } } @@ -2364,7 +2364,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span, format!("{labeled_user_string} may not live long enough"), match sub.kind() { - ty::ReEarlyBound(_) | ty::ReFree(_) if sub.has_name() => error_code!(E0309), + ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => error_code!(E0309), ty::ReStatic => error_code!(E0310), _ => error_code!(E0311), }, @@ -2372,7 +2372,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { '_explain: { let (description, span) = match sub.kind() { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { msg_span_from_named_region(self.tcx, sub, Some(span)) } _ => (format!("lifetime `{sub}`"), Some(span)), @@ -2515,7 +2515,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (lifetime_def_id, lifetime_scope) = match self.tcx.is_suitable_region(lifetime) { Some(info) if !lifetime.has_name() => { - (info.boundregion.get_id().unwrap().expect_local(), info.def_id) + (info.bound_region.get_id().unwrap().expect_local(), info.def_id) } _ => return lifetime.get_name_or_anon().to_string(), }; @@ -2714,8 +2714,8 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - if (a.is_var() && b.is_free_or_static()) - || (b.is_var() && a.is_free_or_static()) + if (a.is_var() && b.is_free()) + || (b.is_var() && a.is_free()) || (a.is_var() && b.is_var()) || a == b { @@ -2779,7 +2779,9 @@ impl<'tcx> InferCtxt<'tcx> { br_string(br), self.tcx.associated_item(def_id).name ), - infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{name}`"), + infer::RegionParameterDefinition(_, name) => { + format!(" for lifetime parameter `{name}`") + } infer::UpvarRegion(ref upvar_id, _) => { let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); format!(" for capture of `{var_name}` by closure") diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index 1a60bab18db..f56d5d7d345 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -70,9 +70,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let anon_reg_sub = self.tcx().is_suitable_region(sub)?; let scope_def_id_sup = anon_reg_sup.def_id; - let bregion_sup = anon_reg_sup.boundregion; + let bregion_sup = anon_reg_sup.bound_region; let scope_def_id_sub = anon_reg_sub.def_id; - let bregion_sub = anon_reg_sub.boundregion; + let bregion_sub = anon_reg_sub.bound_region; let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index be6d1a3750c..7252a812466 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -41,8 +41,8 @@ pub fn find_param_with_region<'tcx>( replace_region: Region<'tcx>, ) -> Option<AnonymousParamInfo<'tcx>> { let (id, bound_region) = match *anon_region { - ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), - ty::ReEarlyBound(ebr) => { + ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region), + ty::ReEarlyParam(ebr) => { (tcx.parent(ebr.def_id), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name)) } _ => return None, // not a free region diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index a8ae43d5297..59e9cbf7da6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -87,7 +87,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | (ty::Alias(ty::Projection, proj), ty::Param(p)) if !tcx.is_impl_trait_in_trait(proj.def_id) => { - let parent = tcx.generics_of(body_owner_def_id) + let parent = tcx + .generics_of(body_owner_def_id) .opt_type_param(p, tcx) .and_then(|param| { let p_def_id = param.def_id; diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index ed1a2a11719..0dde3082d48 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -22,8 +22,8 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> { Self { tcx, free_regions } } - pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { - self.free_regions.lub_free_regions(self.tcx, r_a, r_b) + pub fn lub_param_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { + self.free_regions.lub_param_regions(self.tcx, r_a, r_b) } } @@ -59,7 +59,7 @@ impl<'tcx> FreeRegionMap<'tcx> { r_a: Region<'tcx>, r_b: Region<'tcx>, ) -> bool { - assert!(r_a.is_free_or_static() && r_b.is_free_or_static()); + assert!(r_a.is_free() && r_b.is_free()); let re_static = tcx.lifetimes.re_static; if self.check_relation(re_static, r_b) { // `'a <= 'static` is always true, and not stored in the @@ -80,15 +80,15 @@ impl<'tcx> FreeRegionMap<'tcx> { /// cases, this is more conservative than necessary, in order to /// avoid making arbitrary choices. See /// `TransitiveRelation::postdom_upper_bound` for more details. - pub fn lub_free_regions( + pub fn lub_param_regions( &self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>, ) -> Region<'tcx> { - debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); - assert!(r_a.is_free()); - assert!(r_b.is_free()); + debug!("lub_param_regions(r_a={:?}, r_b={:?})", r_a, r_b); + assert!(r_a.is_param()); + assert!(r_b.is_param()); let result = if r_a == r_b { r_a } else { @@ -97,7 +97,7 @@ impl<'tcx> FreeRegionMap<'tcx> { Some(r) => r, } }; - debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); + debug!("lub_param_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); result } } diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index e212547eaaf..11ab86277c1 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -115,8 +115,8 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> { r } - ty::ReEarlyBound(..) - | ty::ReFree(_) + ty::ReEarlyParam(..) + | ty::ReLateParam(_) | ty::ReVar(_) | ty::RePlaceholder(..) | ty::ReStatic diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs index 84891022720..9e24b020510 100644 --- a/compiler/rustc_infer/src/infer/generalize.rs +++ b/compiler/rustc_infer/src/infer/generalize.rs @@ -340,8 +340,8 @@ where ty::RePlaceholder(..) | ty::ReVar(..) | ty::ReStatic - | ty::ReEarlyBound(..) - | ty::ReFree(..) => { + | ty::ReEarlyParam(..) + | ty::ReLateParam(..) => { // see common code below } } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 4ff9d749422..800aee1f4d3 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -17,7 +17,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ReBound, RePlaceholder, ReVar}; -use rustc_middle::ty::{ReEarlyBound, ReErased, ReError, ReFree, ReStatic}; +use rustc_middle::ty::{ReEarlyParam, ReErased, ReError, ReLateParam, ReStatic}; use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; use std::fmt; @@ -390,7 +390,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } - ReStatic | ReEarlyBound(_) | ReFree(_) => { + ReStatic | ReEarlyParam(_) | ReLateParam(_) => { // nothing lives longer than `'static` // All empty regions are less than early-bound, free, @@ -423,9 +423,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } - ReStatic | ReEarlyBound(_) | ReFree(_) => { + ReStatic | ReEarlyParam(_) | ReLateParam(_) => { // nothing lives longer than `'static` - // All empty regions are less than early-bound, free, + // All empty regions are less than early-bound, late-bound, // and scope regions. true } @@ -450,8 +450,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // Check for the case where we know that `'b: 'static` -- in that case, // `a <= b` for all `a`. - let b_free_or_static = b.is_free_or_static(); - if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) { + if b.is_free() && sub_free_regions(tcx.lifetimes.re_static, b) { return true; } @@ -460,8 +459,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // `lub` relationship defined below, since sometimes the "lub" // is actually the `postdom_upper_bound` (see // `TransitiveRelation` for more details). - let a_free_or_static = a.is_free_or_static(); - if a_free_or_static && b_free_or_static { + if a.is_free() && b.is_free() { return sub_free_regions(a, b); } @@ -501,8 +499,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().lifetimes.re_static } - (ReEarlyBound(_) | ReFree(_), ReEarlyBound(_) | ReFree(_)) => { - self.region_rels.lub_free_regions(a, b) + (ReEarlyParam(_) | ReLateParam(_), ReEarlyParam(_) | ReLateParam(_)) => { + self.region_rels.lub_param_regions(a, b) } // For these types, we cannot define any additional @@ -723,13 +721,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { return; } - // We place free regions first because we are special casing - // SubSupConflict(ReFree, ReFree) when reporting error, and so + // We place late-bound regions first because we are special casing + // SubSupConflict(ReLateParam, ReLateParam) when reporting error, and so // the user will more likely get a specific suggestion. fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 { match *x.region { - ReEarlyBound(_) => 0, - ReFree(_) => 1, + ReEarlyParam(_) => 0, + ReLateParam(_) => 1, _ => 2, } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 3c539f18eb9..956d097a5b2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -508,7 +508,10 @@ pub enum RegionVariableOrigin { Coercion(Span), /// Region variables created as the values for early-bound regions. - EarlyBoundRegion(Span, Symbol), + /// + /// FIXME(@lcnr): This can also store a `DefId`, similar to + /// `TypeVariableOriginKind::TypeParameterDefinition`. + RegionParameterDefinition(Span, Symbol), /// Region variables created when instantiating a binder with /// existential variables, e.g. when calling a function or method. @@ -1165,7 +1168,7 @@ impl<'tcx> InferCtxt<'tcx> { GenericParamDefKind::Lifetime => { // Create a region inference variable for the given // region parameter definition. - self.next_region_var(EarlyBoundRegion(span, param.name)).into() + self.next_region_var(RegionParameterDefinition(span, param.name)).into() } GenericParamDefKind::Type { .. } => { // Create a type inference variable for the given @@ -2041,7 +2044,7 @@ impl RegionVariableOrigin { | AddrOfRegion(a) | Autoref(a) | Coercion(a) - | EarlyBoundRegion(a, ..) + | RegionParameterDefinition(a, ..) | BoundRegion(a, ..) | UpvarRegion(_, a) => a, Nll(..) => bug!("NLL variable used with `span`"), diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 47e3dd762b0..f8dbfdde30c 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -138,8 +138,8 @@ impl<'tcx> OutlivesEnvironmentBuilder<'tcx> { } OutlivesBound::RegionSubRegion(r_a, r_b) => match (*r_a, *r_b) { ( - ty::ReStatic | ty::ReEarlyBound(_) | ty::ReFree(_), - ty::ReStatic | ty::ReEarlyBound(_) | ty::ReFree(_), + ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_), + ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_), ) => self.region_relation.add(r_a, r_b), (ty::ReError(_), _) | (_, ty::ReError(_)) => {} // FIXME(#109628): We shouldn't have existential variables in implied bounds. diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index 2db8579af52..52cc107ae52 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -112,7 +112,9 @@ where }; for (idx, s) in args.iter().enumerate() { - if variances.map(|variances| variances[idx]) != Some(ty::Variance::Bivariant) { + if variances.map(|variances| variances[idx]) + != Some(ty::Variance::Bivariant) + { s.visit_with(self)?; } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index de142f05fb7..e888340bde3 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -662,8 +662,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { match *region { ty::ReStatic | ty::ReErased - | ty::ReFree(..) - | ty::ReEarlyBound(..) + | ty::ReLateParam(..) + | ty::ReEarlyParam(..) | ty::ReError(_) => ty::UniverseIndex::ROOT, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReVar(vid) => self.var_universe(vid), diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 4a6d1bc682b..4ca3ced21ee 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -13,9 +13,9 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c4962707f69..6225424fe4a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -38,21 +38,17 @@ pub type Result<T> = result::Result<T, ErrorGuaranteed>; /// Can be used to run `rustc_interface` queries. /// Created by passing [`Config`] to [`run_compiler`]. pub struct Compiler { - pub(crate) sess: Lrc<Session>, - codegen_backend: Lrc<dyn CodegenBackend>, - pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>, + sess: Session, + codegen_backend: Box<dyn CodegenBackend>, pub(crate) override_queries: Option<fn(&Session, &mut Providers)>, } impl Compiler { - pub fn session(&self) -> &Lrc<Session> { + pub fn session(&self) -> &Session { &self.sess } - pub fn codegen_backend(&self) -> &Lrc<dyn CodegenBackend> { - &self.codegen_backend - } - pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> { - &self.register_lints + pub fn codegen_backend(&self) -> &dyn CodegenBackend { + &*self.codegen_backend } pub fn build_output_filenames( &self, @@ -178,7 +174,9 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) - check_cfg.exhaustive_names = true; for arg in args { - if arg.is_word() && let Some(ident) = arg.ident() { + if arg.is_word() + && let Some(ident) = arg.ident() + { check_cfg.expecteds.entry(ident.name).or_insert(ExpectedValues::Any); } else { error!("`names()` arguments must be simple identifiers"); @@ -188,7 +186,9 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) - set_old_syntax(); if let Some((name, values)) = args.split_first() { - if name.is_word() && let Some(ident) = name.ident() { + if name.is_word() + && let Some(ident) = name.ident() + { let expected_values = check_cfg .expecteds .entry(ident.name) @@ -236,12 +236,16 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) - let mut values_any_specified = false; for arg in args { - if arg.is_word() && let Some(ident) = arg.ident() { + if arg.is_word() + && let Some(ident) = arg.ident() + { if values_specified { error!("`cfg()` names cannot be after values"); } names.push(ident); - } else if arg.has_name(sym::any) && let Some(args) = arg.meta_item_list() { + } else if arg.has_name(sym::any) + && let Some(args) = arg.meta_item_list() + { if any_specified { error!("`any()` cannot be specified multiple times"); } @@ -249,7 +253,9 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) - if !args.is_empty() { error!("`any()` must be empty"); } - } else if arg.has_name(sym::values) && let Some(args) = arg.meta_item_list() { + } else if arg.has_name(sym::values) + && let Some(args) = arg.meta_item_list() + { if names.is_empty() { error!("`values()` cannot be specified before the names"); } else if values_specified { @@ -260,7 +266,9 @@ pub(crate) fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) - for arg in args { if let Some(LitKind::Str(s, _)) = arg.lit().map(|lit| &lit.kind) { values.insert(Some(*s)); - } else if arg.has_name(sym::any) && let Some(args) = arg.meta_item_list() { + } else if arg.has_name(sym::any) + && let Some(args) = arg.meta_item_list() + { if values_any_specified { error!("`any()` in `values()` cannot be specified multiple times"); } @@ -473,12 +481,18 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se sess.opts.untracked_state_hash = hasher.finish() } - let compiler = Compiler { - sess: Lrc::new(sess), - codegen_backend: Lrc::from(codegen_backend), - register_lints: config.register_lints, - override_queries: config.override_queries, - }; + // Even though the session holds the lint store, we can't build the + // lint store until after the session exists. And we wait until now + // so that `register_lints` sees the fully initialized session. + let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints()); + if let Some(register_lints) = config.register_lints.as_deref() { + register_lints(&sess, &mut lint_store); + sess.registered_lints = true; + } + sess.lint_store = Some(Lrc::new(lint_store)); + + let compiler = + Compiler { sess, codegen_backend, override_queries: config.override_queries }; rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { let r = { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7d14d088e59..0baf77c4f7e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -72,17 +72,6 @@ fn count_nodes(krate: &ast::Crate) -> usize { counter.count } -pub(crate) fn create_lint_store( - sess: &Session, - register_lints: Option<impl Fn(&Session, &mut LintStore)>, -) -> LintStore { - let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints()); - if let Some(register_lints) = register_lints { - register_lints(sess, &mut lint_store); - } - lint_store -} - fn pre_expansion_lint<'a>( sess: &Session, features: &Features, @@ -138,7 +127,7 @@ fn configure_and_expand( let tcx = resolver.tcx(); let sess = tcx.sess; let features = tcx.features(); - let lint_store = unerased_lint_store(tcx); + let lint_store = unerased_lint_store(&tcx.sess); let crate_name = tcx.crate_name(LOCAL_CRATE); let lint_check_node = (&krate, pre_configured_attrs); pre_expansion_lint( @@ -330,7 +319,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { } }); - let lint_store = unerased_lint_store(tcx); + let lint_store = unerased_lint_store(&tcx.sess); rustc_lint::check_ast_node( sess, tcx.features(), @@ -645,7 +634,6 @@ pub fn create_global_ctxt<'tcx>( compiler: &'tcx Compiler, crate_types: Vec<CrateType>, stable_crate_id: StableCrateId, - lint_store: Lrc<LintStore>, dep_graph: DepGraph, untracked: Untracked, gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>, @@ -676,7 +664,6 @@ pub fn create_global_ctxt<'tcx>( sess, crate_types, stable_crate_id, - lint_store, arena, hir_arena, untracked, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 3a5f788e8dd..fbf2dbcdae5 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal}; +use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, OnceLock, WorkerLocal}; use rustc_hir::def_id::{StableCrateId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; @@ -101,10 +101,11 @@ impl<'tcx> Queries<'tcx> { } } - fn session(&self) -> &Lrc<Session> { - &self.compiler.sess + fn session(&self) -> &Session { + &self.compiler.session() } - fn codegen_backend(&self) -> &Lrc<dyn CodegenBackend> { + + fn codegen_backend(&self) -> &dyn CodegenBackend { self.compiler.codegen_backend() } @@ -148,8 +149,6 @@ impl<'tcx> Queries<'tcx> { ); let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id)?; - let lint_store = - Lrc::new(passes::create_lint_store(sess, self.compiler.register_lints.as_deref())); let cstore = FreezeLock::new(Box::new(CStore::new( self.codegen_backend().metadata_loader(), stable_crate_id, @@ -164,7 +163,6 @@ impl<'tcx> Queries<'tcx> { self.compiler, crate_types, stable_crate_id, - lint_store, dep_graph, untracked, &self.gcx_cell, @@ -200,7 +198,7 @@ impl<'tcx> Queries<'tcx> { // Hook for UI tests. Self::check_for_rustc_errors_attr(tcx); - Ok(passes::start_codegen(&**self.codegen_backend(), tcx)) + Ok(passes::start_codegen(self.codegen_backend(), tcx)) }) } @@ -239,67 +237,48 @@ impl<'tcx> Queries<'tcx> { } pub fn linker(&'tcx self, ongoing_codegen: Box<dyn Any>) -> Result<Linker> { - let sess = self.session().clone(); - let codegen_backend = self.codegen_backend().clone(); - - let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| { - ( - if tcx.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None }, - tcx.output_filenames(()).clone(), - tcx.dep_graph.clone(), - ) - }); - - Ok(Linker { - sess, - codegen_backend, - - dep_graph, - prepare_outputs, - crate_hash, - ongoing_codegen, + self.global_ctxt()?.enter(|tcx| { + Ok(Linker { + dep_graph: tcx.dep_graph.clone(), + output_filenames: tcx.output_filenames(()).clone(), + crate_hash: if tcx.needs_crate_hash() { + Some(tcx.crate_hash(LOCAL_CRATE)) + } else { + None + }, + ongoing_codegen, + }) }) } } pub struct Linker { - // compilation inputs - sess: Lrc<Session>, - codegen_backend: Lrc<dyn CodegenBackend>, - - // compilation outputs dep_graph: DepGraph, - prepare_outputs: Arc<OutputFilenames>, + output_filenames: Arc<OutputFilenames>, // Only present when incr. comp. is enabled. crate_hash: Option<Svh>, ongoing_codegen: Box<dyn Any>, } impl Linker { - pub fn link(self) -> Result<()> { - let (codegen_results, work_products) = self.codegen_backend.join_codegen( - self.ongoing_codegen, - &self.sess, - &self.prepare_outputs, - )?; + pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> { + let (codegen_results, work_products) = + codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)?; - self.sess.compile_status()?; + sess.compile_status()?; - let sess = &self.sess; - let dep_graph = self.dep_graph; sess.time("serialize_work_products", || { - rustc_incremental::save_work_product_index(sess, &dep_graph, work_products) + rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) }); - let prof = self.sess.prof.clone(); - prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph)); + let prof = sess.prof.clone(); + prof.generic_activity("drop_dep_graph").run(move || drop(self.dep_graph)); // Now that we won't touch anything in the incremental compilation directory // any more, we can finalize it (which involves renaming it) - rustc_incremental::finalize_session_directory(&self.sess, self.crate_hash); + rustc_incremental::finalize_session_directory(sess, self.crate_hash); - if !self - .sess + if !sess .opts .output_types .keys() @@ -309,14 +288,14 @@ impl Linker { } if sess.opts.unstable_opts.no_link { - let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT); + let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT); CodegenResults::serialize_rlink(sess, &rlink_file, &codegen_results) .map_err(|error| sess.emit_fatal(FailedWritingFile { path: &rlink_file, error }))?; return Ok(()); } let _timer = sess.prof.verbose_generic_activity("link_crate"); - self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs) + codegen_backend.link(sess, codegen_results, &self.output_filenames) } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index d3081695523..e6e132978ed 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -679,7 +679,6 @@ fn test_unstable_options_tracking_hash() { untracked!(incremental_info, true); untracked!(incremental_verify_ich, true); untracked!(input_stats, true); - untracked!(keep_hygiene_data, true); untracked!(link_native_libraries, false); untracked!(llvm_time_trace, true); untracked!(ls, vec!["all".to_owned()]); @@ -691,7 +690,6 @@ fn test_unstable_options_tracking_hash() { untracked!(no_leak_check, true); untracked!(no_parallel_llvm, true); untracked!(parse_only, true); - untracked!(perf_stats, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); untracked!(print_codegen_stats, true); @@ -771,6 +769,7 @@ fn test_unstable_options_tracking_hash() { tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_directives, false); tracked!(link_only, true); + tracked!(llvm_module_flag, vec![("bar".to_string(), 123, "max".to_string())]); tracked!(llvm_plugins, vec![String::from("plugin_name")]); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); tracked!(maximal_hir_to_mir_coverage, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 22d12793464..8df3648a065 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -415,7 +415,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C let mut base = session.opts.crate_types.clone(); if base.is_empty() { let attr_types = attrs.iter().filter_map(|a| { - if a.has_name(sym::crate_type) && let Some(s) = a.value_str() { + if a.has_name(sym::crate_type) + && let Some(s) = a.value_str() + { categorize_crate_type(s) } else { None diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6f6150a4172..e3dd8faeedb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1910,7 +1910,7 @@ impl ExplicitOutlivesRequirements { .iter() .filter_map(|(clause, _)| match clause.kind().skip_binder() { ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { - ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b), + ty::ReEarlyParam(ebr) if ebr.def_id == def_id => Some(b), _ => None, }, _ => None, @@ -1953,7 +1953,7 @@ impl ExplicitOutlivesRequirements { let is_inferred = match tcx.named_bound_var(lifetime.hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives .iter() - .any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })), + .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { ebr.def_id == def_id })), _ => false, }; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a5f4c5ff045..d500c3b66d6 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -497,9 +497,6 @@ pub struct LateContext<'tcx> { /// Items accessible from the crate being checked. pub effective_visibilities: &'tcx EffectiveVisibilities, - /// The store of registered lints and the lint levels. - pub lint_store: &'tcx LintStore, - pub last_node_with_lint_attrs: hir::HirId, /// Generic type parameters in scope for the item we are in. @@ -515,21 +512,14 @@ pub struct EarlyContext<'a> { pub buffered: LintBuffer, } -pub trait LintPassObject: Sized {} - -impl LintPassObject for EarlyLintPassObject {} - -impl LintPassObject for LateLintPassObject<'_> {} - -pub trait LintContext: Sized { - type PassObject: LintPassObject; - +pub trait LintContext { fn sess(&self) -> &Session; - fn lints(&self) -> &LintStore; - /// Emit a lint at the appropriate level, with an optional associated span and an existing diagnostic. + /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// diagnostic. /// - /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed + /// explanation. /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] @@ -713,7 +703,7 @@ pub trait LintContext: Sized { db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information"); }, BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => { - let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect(); + let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().copied().collect(); // Suggest the most probable if we found one if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { @@ -1059,17 +1049,11 @@ impl<'a> EarlyContext<'a> { } impl<'tcx> LintContext for LateContext<'tcx> { - type PassObject = LateLintPassObject<'tcx>; - /// Gets the overall compiler `Session` object. fn sess(&self) -> &Session { &self.tcx.sess } - fn lints(&self) -> &LintStore { - &*self.lint_store - } - #[rustc_lint_diagnostics] fn lookup<S: Into<MultiSpan>>( &self, @@ -1094,17 +1078,11 @@ impl<'tcx> LintContext for LateContext<'tcx> { } impl LintContext for EarlyContext<'_> { - type PassObject = EarlyLintPassObject; - /// Gets the overall compiler `Session` object. fn sess(&self) -> &Session { &self.builder.sess() } - fn lints(&self) -> &LintStore { - self.builder.lint_store() - } - #[rustc_lint_diagnostics] fn lookup<S: Into<MultiSpan>>( &self, diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 86b3b4ad0ca..31d9c0d33fe 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -341,8 +341,8 @@ fn structurally_same_type_impl<'tcx>( // We don't compare regions, but leaving bound regions around ICEs, so // we erase them. - let a_sig = tcx.erase_late_bound_regions(a_poly_sig); - let b_sig = tcx.erase_late_bound_regions(b_poly_sig); + let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig); + let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig); (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 6c8b60c8d74..10c4c0dc79f 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -17,22 +17,25 @@ use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; use rustc_ast as ast; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::join; +use rustc_data_structures::sync::{join, Lrc}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::intravisit as hir_visit; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::LintPass; +use rustc_session::Session; use rustc_span::Span; use std::any::Any; use std::cell::Cell; /// Extract the `LintStore` from the query context. -/// This function exists because we've erased `LintStore` as `dyn Any` in the context. -pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { - let store: &dyn Any = &*tcx.lint_store; +/// This function exists because we've erased `LintStore` as `dyn Any` in the session. +pub fn unerased_lint_store(sess: &Session) -> &LintStore { + assert!(sess.lint_store.is_some()); + let store: &Lrc<_> = sess.lint_store.as_ref().unwrap(); + let store: &dyn Any = &**store; store.downcast_ref().unwrap() } @@ -353,7 +356,6 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), effective_visibilities: &tcx.effective_visibilities(()), - lint_store: unerased_lint_store(tcx), last_node_with_lint_attrs: tcx.hir().local_def_id_to_hir_id(module_def_id), generics: None, only_module: true, @@ -362,8 +364,11 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( // Note: `passes` is often empty. In that case, it's faster to run // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); + let mut passes: Vec<_> = unerased_lint_store(&tcx.sess) + .late_module_passes + .iter() + .map(|mk_pass| (mk_pass)(tcx)) + .collect(); if passes.is_empty() { late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); } else { @@ -400,7 +405,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { // Note: `passes` is often empty. let mut passes: Vec<_> = - unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); + unerased_lint_store(&tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); if passes.is_empty() { return; @@ -412,7 +417,6 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), effective_visibilities: &tcx.effective_visibilities(()), - lint_store: unerased_lint_store(tcx), last_node_with_lint_attrs: hir::CRATE_HIR_ID, generics: None, only_module: false, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 0d20f6232db..ee5fa87e45d 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -123,7 +123,7 @@ impl LintLevelSets { } fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> { - let store = unerased_lint_store(tcx); + let store = unerased_lint_store(&tcx.sess); let mut builder = LintLevelsBuilder { sess: tcx.sess, @@ -152,7 +152,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp #[instrument(level = "trace", skip(tcx), ret)] fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { - let store = unerased_lint_store(tcx); + let store = unerased_lint_store(&tcx.sess); let attrs = tcx.hir_attrs(owner); let mut levels = LintLevelsBuilder { @@ -548,10 +548,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.features } - pub(crate) fn lint_store(&self) -> &LintStore { - self.store - } - fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> { self.provider.current_specs() } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 54adedd3c09..606e1886616 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -27,8 +27,8 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(box_patterns)] #![feature(control_flow_enum)] diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index d44691b5e9b..82483ac7dc0 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -155,7 +155,7 @@ fn is_cast_from_const_to_mut<'tcx>( let start_ty = cx.typeck_results().node_type(e.hir_id); if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() { - // If an UnsafeCell method is involved we need to additionaly check the + // If an UnsafeCell method is involved we need to additionally check the // inner type for the presence of the Freeze trait (ie does NOT contain // an UnsafeCell), since in that case we would incorrectly lint on valid casts. // diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index c04053d1865..706c8c7add5 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1234,7 +1234,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } - let sig = tcx.erase_late_bound_regions(sig); + let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { match self.check_type_for_ffi(cache, *arg) { FfiSafe => {} @@ -1391,7 +1391,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// types that have external ABIs, as these still need checked. fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.erase_late_bound_regions(sig); + let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) { @@ -1409,7 +1409,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Check if a function's argument types and result type are "ffi-safe". fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.erase_late_bound_regions(sig); + let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index fe13162cd4a..ed1e8771323 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -242,6 +242,12 @@ fn main() { cmd.arg("--system-libs"); } + // We need libkstat for getHostCPUName on SPARC builds. + // See also: https://github.com/llvm/llvm-project/issues/64186 + if target.starts_with("sparcv9") && target.contains("solaris") { + println!("cargo:rustc-link-lib=kstat"); + } + if (target.starts_with("arm") && !target.contains("freebsd")) || target.starts_with("mips-") || target.starts_with("mipsel-") diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 4390486b0de..7ada2eff593 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -983,6 +983,9 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType( unwrapDI<DIType>(Ty), fromRust(Flags), unwrap<llvm::ConstantInt>(val), +#if LLVM_VERSION_GE(18, 0) + llvm::dwarf::DW_TAG_member, +#endif AlignInBits )); } @@ -1105,7 +1108,11 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File), LineNumber, SizeInBits, AlignInBits, DINodeArray(unwrapDI<MDTuple>(Elements)), - unwrapDI<DIType>(ClassTy), "", IsScoped)); + unwrapDI<DIType>(ClassTy), +#if LLVM_VERSION_GE(18, 0) + /* RunTimeLang */ 0, +#endif + "", IsScoped)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 518c20c9fa8..ca0aec71052 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -1,9 +1,9 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] // NOTE: This crate only exists to allow linking on mingw targets. diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 0c9ec556549..e3464cb8a2d 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -14,7 +14,7 @@ //! //! ``` //! fn main() { -//! rustc_log::init_env_logger("LOG").unwrap(); +//! rustc_log::init_logger(rustc_log::LoggerConfig::from_env("LOG")).unwrap(); //! //! let edition = rustc_span::edition::Edition::Edition2021; //! rustc_span::create_session_globals_then(edition, || { @@ -52,13 +52,36 @@ use tracing_subscriber::fmt::{ }; use tracing_subscriber::layer::SubscriberExt; -pub fn init_env_logger(env: &str) -> Result<(), Error> { - let filter = match env::var(env) { +/// The values of all the environment variables that matter for configuring a logger. +/// Errors are explicitly preserved so that we can share error handling. +pub struct LoggerConfig { + pub filter: Result<String, VarError>, + pub color_logs: Result<String, VarError>, + pub verbose_entry_exit: Result<String, VarError>, + pub verbose_thread_ids: Result<String, VarError>, + pub backtrace: Result<String, VarError>, +} + +impl LoggerConfig { + pub fn from_env(env: &str) -> Self { + LoggerConfig { + filter: env::var(env), + color_logs: env::var(format!("{env}_COLOR")), + verbose_entry_exit: env::var(format!("{env}_ENTRY_EXIT")), + verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")), + backtrace: env::var(format!("{env}_BACKTRACE")), + } + } +} + +/// Initialize the logger with the given values for the filter, coloring, and other options env variables. +pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { + let filter = match cfg.filter { Ok(env) => EnvFilter::new(env), _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)), }; - let color_logs = match env::var(String::from(env) + "_COLOR") { + let color_logs = match cfg.color_logs { Ok(value) => match value.as_ref() { "always" => true, "never" => false, @@ -69,14 +92,14 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue), }; - let verbose_entry_exit = match env::var_os(String::from(env) + "_ENTRY_EXIT") { - None => false, - Some(v) => &v != "0", + let verbose_entry_exit = match cfg.verbose_entry_exit { + Ok(v) => &v != "0", + Err(_) => false, }; - let verbose_thread_ids = match env::var_os(String::from(env) + "_THREAD_IDS") { - None => false, - Some(v) => &v == "1", + let verbose_thread_ids = match cfg.verbose_thread_ids { + Ok(v) => &v == "1", + Err(_) => false, }; let layer = tracing_tree::HierarchicalLayer::default() @@ -91,7 +114,7 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { .with_thread_names(verbose_thread_ids); let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); - match env::var(format!("{env}_BACKTRACE")) { + match cfg.backtrace { Ok(str) => { let fmt_layer = tracing_subscriber::fmt::layer() .with_writer(io::stderr) diff --git a/compiler/rustc_macros/src/current_version.rs b/compiler/rustc_macros/src/current_version.rs index 5e3b91c17bf..42ca60a6d8a 100644 --- a/compiler/rustc_macros/src/current_version.rs +++ b/compiler/rustc_macros/src/current_version.rs @@ -1,37 +1,16 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::parse::{Parse, ParseStream}; -use syn::{parenthesized, parse_macro_input, LitStr, Token}; -pub struct Input { - variable: LitStr, -} - -mod kw { - syn::custom_keyword!(env); -} - -impl Parse for Input { - // Input syntax is `env!("CFG_RELEASE")` to facilitate grepping. - fn parse(input: ParseStream<'_>) -> syn::Result<Self> { - let paren; - input.parse::<kw::env>()?; - input.parse::<Token![!]>()?; - parenthesized!(paren in input); - let variable: LitStr = paren.parse()?; - Ok(Input { variable }) - } -} - -pub(crate) fn current_version(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as Input); - - TokenStream::from(match RustcVersion::parse_env_var(&input.variable) { +pub(crate) fn current_version(_input: TokenStream) -> TokenStream { + let env_var = "CFG_RELEASE"; + TokenStream::from(match RustcVersion::parse_cfg_release(env_var) { Ok(RustcVersion { major, minor, patch }) => quote!( + // The produced literal has type `rustc_session::RustcVersion`. Self { major: #major, minor: #minor, patch: #patch } ), - Err(err) => syn::Error::new(Span::call_site(), err).into_compile_error(), + Err(err) => syn::Error::new(Span::call_site(), format!("{env_var} env var: {err}")) + .into_compile_error(), }) } @@ -42,8 +21,8 @@ struct RustcVersion { } impl RustcVersion { - fn parse_env_var(env_var: &LitStr) -> Result<Self, Box<dyn std::error::Error>> { - let value = proc_macro::tracked_env::var(env_var.value())?; + fn parse_cfg_release(env_var: &str) -> Result<Self, Box<dyn std::error::Error>> { + let value = proc_macro::tracked_env::var(env_var)?; Self::parse_str(&value) .ok_or_else(|| format!("failed to parse rustc version: {:?}", value).into()) } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 1a8174bfd96..31ad9cdb216 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -229,8 +229,8 @@ fn generate_test(slug: &syn::Path, structure: &Structure<'_>) -> TokenStream { } } use std::sync::atomic::{AtomicUsize, Ordering}; - // We need to make sure that the same diagnostic slug can be used multiple times without causing an - // error, so just have a global counter here. + // We need to make sure that the same diagnostic slug can be used multiple times without + // causing an error, so just have a global counter here. static COUNTER: AtomicUsize = AtomicUsize::new(0); let slug = slug.get_ident().unwrap(); let ident = quote::format_ident!("verify_{slug}_{}", COUNTER.fetch_add(1, Ordering::Relaxed)); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index e9a5cd9de97..2755a161d91 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -53,6 +53,7 @@ pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> { /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. pub slug: SpannedOption<Path>, + /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. pub code: SpannedOption<()>, @@ -68,7 +69,7 @@ impl DiagnosticDeriveBuilder { /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions /// or attributes on the type itself when input is an enum. - pub fn each_variant<'s, F>(&mut self, structure: &mut Structure<'s>, f: F) -> TokenStream + pub(crate) fn each_variant<'s, F>(&mut self, structure: &mut Structure<'s>, f: F) -> TokenStream where F: for<'a, 'v> Fn(DiagnosticDeriveVariantBuilder<'a>, &VariantInfo<'v>) -> TokenStream, { @@ -121,7 +122,7 @@ impl DiagnosticDeriveBuilder { impl<'a> DiagnosticDeriveVariantBuilder<'a> { /// Generates calls to `code` and similar functions based on the attributes on the type or /// variant. - pub fn preamble(&mut self, variant: &VariantInfo<'_>) -> TokenStream { + pub(crate) fn preamble(&mut self, variant: &VariantInfo<'_>) -> TokenStream { let ast = variant.ast(); let attrs = &ast.attrs; let preamble = attrs.iter().map(|attr| { @@ -135,7 +136,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { /// Generates calls to `span_label` and similar functions based on the attributes on fields or /// calls to `set_arg` when no attributes are present. - pub fn body(&mut self, variant: &VariantInfo<'_>) -> TokenStream { + pub(crate) fn body(&mut self, variant: &VariantInfo<'_>) -> TokenStream { let mut body = quote! {}; // Generate `set_arg` calls first.. for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 877271ff077..0f9e68cdc50 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -478,7 +478,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { + pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { let kind_slugs = self.identify_kind()?; if kind_slugs.is_empty() { if self.is_enum { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 12563292181..2700f02e33a 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -17,7 +17,7 @@ use synstructure::{BindingInfo, VariantInfo}; use super::error::invalid_attr; thread_local! { - pub static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0); + pub(crate) static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0); } /// Returns an ident of the form `__code_N` where `N` is incremented once with every call. @@ -208,7 +208,7 @@ impl<'ty> FieldInnerTy<'ty> { } } - pub fn span(&self) -> proc_macro2::Span { + pub(crate) fn span(&self) -> proc_macro2::Span { match self { FieldInnerTy::Option(ty) | FieldInnerTy::Vec(ty) | FieldInnerTy::Plain(ty) => ty.span(), } @@ -537,7 +537,7 @@ impl fmt::Display for SuggestionKind { } impl SuggestionKind { - pub fn to_suggestion_style(&self) -> TokenStream { + pub(crate) fn to_suggestion_style(&self) -> TokenStream { match self { SuggestionKind::Normal => { quote! { rustc_errors::SuggestionStyle::ShowCode } diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index 75a2f7009c2..6d23b9ac99d 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -38,33 +38,16 @@ fn parse_attributes(field: &syn::Field) -> Attributes { attrs } -pub fn hash_stable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { +pub(crate) fn hash_stable_generic_derive( + mut s: synstructure::Structure<'_>, +) -> proc_macro2::TokenStream { let generic: syn::GenericParam = parse_quote!(__CTX); s.add_bounds(synstructure::AddBounds::Generics); s.add_impl_generic(generic); s.add_where_predicate(parse_quote! { __CTX: crate::HashStableContext }); - let body = s.each(|bi| { - let attrs = parse_attributes(bi.ast()); - if attrs.ignore { - quote! {} - } else if let Some(project) = attrs.project { - quote! { - (&#bi.#project).hash_stable(__hcx, __hasher); - } - } else { - quote! { - #bi.hash_stable(__hcx, __hasher); - } - } - }); - let discriminant = match s.ast().data { - syn::Data::Enum(_) => quote! { - ::std::mem::discriminant(self).hash_stable(__hcx, __hasher); - }, - syn::Data::Struct(_) => quote! {}, - syn::Data::Union(_) => panic!("cannot derive on union"), - }; + let discriminant = hash_stable_discriminant(&mut s); + let body = hash_stable_body(&mut s); s.bound_impl( quote!(::rustc_data_structures::stable_hasher::HashStable<__CTX>), @@ -81,32 +64,13 @@ pub fn hash_stable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_ma ) } -pub fn hash_stable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { +pub(crate) fn hash_stable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let generic: syn::GenericParam = parse_quote!('__ctx); s.add_bounds(synstructure::AddBounds::Generics); s.add_impl_generic(generic); - let body = s.each(|bi| { - let attrs = parse_attributes(bi.ast()); - if attrs.ignore { - quote! {} - } else if let Some(project) = attrs.project { - quote! { - (&#bi.#project).hash_stable(__hcx, __hasher); - } - } else { - quote! { - #bi.hash_stable(__hcx, __hasher); - } - } - }); - let discriminant = match s.ast().data { - syn::Data::Enum(_) => quote! { - ::std::mem::discriminant(self).hash_stable(__hcx, __hasher); - }, - syn::Data::Struct(_) => quote! {}, - syn::Data::Union(_) => panic!("cannot derive on union"), - }; + let discriminant = hash_stable_discriminant(&mut s); + let body = hash_stable_body(&mut s); s.bound_impl( quote!( @@ -126,3 +90,30 @@ pub fn hash_stable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::To }, ) } + +fn hash_stable_discriminant(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream { + match s.ast().data { + syn::Data::Enum(_) => quote! { + ::std::mem::discriminant(self).hash_stable(__hcx, __hasher); + }, + syn::Data::Struct(_) => quote! {}, + syn::Data::Union(_) => panic!("cannot derive on union"), + } +} + +fn hash_stable_body(s: &mut synstructure::Structure<'_>) -> proc_macro2::TokenStream { + s.each(|bi| { + let attrs = parse_attributes(bi.ast()); + if attrs.ignore { + quote! {} + } else if let Some(project) = attrs.project { + quote! { + (&#bi.#project).hash_stable(__hcx, __hasher); + } + } else { + quote! { + #bi.hash_stable(__hcx, __hasher); + } + } + }) +} diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 193dbd75fbd..a6666636032 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -26,6 +26,9 @@ mod symbols; mod type_foldable; mod type_visitable; +// Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and +// produces a `RustcVersion` literal containing that version (e.g. +// `RustcVersion { major: 1, minor: 75, patch: 0 }`). #[proc_macro] pub fn current_rustc_version(input: TokenStream) -> TokenStream { current_version::current_version(input) diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 4129712a6b2..488d4504a2d 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -19,7 +19,9 @@ //! ```bash //! cargo install cargo-expand # this is necessary only once //! cd compiler/rustc_span -//! cargo expand > /tmp/rustc_span.rs # it's a big file +//! # The specific version number in CFG_RELEASE doesn't matter. +//! # The output is large. +//! CFG_RELEASE="0.0.0" cargo +nightly expand > /tmp/rustc_span.rs //! ``` use proc_macro2::{Span, TokenStream}; @@ -83,7 +85,9 @@ impl Parse for Value { } } Expr::Macro(expr) => { - if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() { + if expr.mac.path.is_ident("env") + && let Ok(lit) = expr.mac.parse_body() + { return Ok(Value::Env(lit, expr.mac.clone())); } } @@ -318,13 +322,4 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) { }; (output, errors.list) - - // To see the generated code, use the "cargo expand" command. - // Do this once to install: - // cargo install cargo-expand - // - // Then, cd to rustc_span and run: - // cargo expand > /tmp/rustc_span_expanded.rs - // - // and read that file. } diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index b06b4fb87cd..d326a3aa8d0 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,11 +1,10 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(decl_macro)] #![feature(extract_if)] -#![cfg_attr(bootstrap, feature(generators))] -#![cfg_attr(not(bootstrap), feature(coroutines))] +#![feature(coroutines)] #![feature(iter_from_coroutine)] #![feature(let_chains)] #![feature(if_let_guard)] diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index ab135851b8e..f352fa6d46a 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -470,7 +470,7 @@ impl<'tcx> Collector<'tcx> { } fn i686_arg_list_size(&self, item: DefId) -> usize { - let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions( + let argument_types: &List<Ty<'_>> = self.tcx.instantiate_bound_regions_with_erased( self.tcx .type_of(item) .instantiate_identity() diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2042863d189..b6ffdc7378f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2396,8 +2396,10 @@ pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: hir::BodyId) -> String { // * character escapes // // FIXME: This passes through `-/*spacer*/0` verbatim. - Literal if !value.span.from_expansion() - && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) => { + Literal + if !value.span.from_expansion() + && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) => + { snippet } @@ -2408,10 +2410,12 @@ pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: hir::BodyId) -> String { // FIXME: Omit the curly braces if the enclosing expression is an array literal // with a repeated element (an `ExprKind::Repeat`) as in such case it // would not actually need any disambiguation. - Complex => if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst { - "{ _ }".to_owned() - } else { - "_".to_owned() + Complex => { + if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst { + "{ _ }".to_owned() + } else { + "_".to_owned() + } } } } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 76ef62f9f27..dc0da165af6 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -8,8 +8,8 @@ mod dep_node; pub use rustc_query_system::dep_graph::debug::EdgeFilter; pub use rustc_query_system::dep_graph::{ - debug::DepNodeFilter, hash_result, DepContext, DepGraphQuery, DepNodeColor, DepNodeIndex, Deps, - SerializedDepGraph, SerializedDepNodeIndex, TaskDeps, TaskDepsRef, WorkProduct, WorkProductId, + debug::DepNodeFilter, hash_result, DepContext, DepGraphQuery, DepNodeIndex, Deps, + SerializedDepGraph, SerializedDepNodeIndex, TaskDepsRef, WorkProduct, WorkProductId, WorkProductMap, }; diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 92d1fcc7ed2..6e50e894046 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -64,8 +64,8 @@ impl<'tcx> UnifyValue for UnifiedRegion<'tcx> { cmp::min_by_key(r1, r2, |r| match r.kind() { ty::ReStatic | ty::ReErased - | ty::ReFree(..) - | ty::ReEarlyBound(..) + | ty::ReLateParam(..) + | ty::ReEarlyParam(..) | ty::ReError(_) => ty::UniverseIndex::ROOT, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReVar(..) | ty::ReBound(..) => bug!("not a universal region"), diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 448a3029ae9..4af875e8d79 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -23,8 +23,8 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(allocator_api)] #![feature(array_windows)] #![feature(assert_matches)] @@ -32,8 +32,7 @@ #![feature(core_intrinsics)] #![feature(discriminant_kind)] #![feature(exhaustive_patterns)] -#![cfg_attr(bootstrap, feature(generators))] -#![cfg_attr(not(bootstrap), feature(coroutines))] +#![feature(coroutines)] #![feature(get_mut_unchecked)] #![feature(if_let_guard)] #![feature(inline_const)] @@ -59,7 +58,6 @@ #![feature(extract_if)] #![feature(intra_doc_pointers)] #![feature(yeet_expr)] -#![feature(result_option_inspect)] #![feature(const_option)] #![feature(trait_alias)] #![feature(ptr_alignment_type)] diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 56fed05c63f..b76d1d6e141 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -77,7 +77,7 @@ use std::ops::Deref; /// picture, but rather the ending point. // // FIXME(pnkfelix): this currently derives `PartialOrd` and `Ord` to -// placate the same deriving in `ty::FreeRegion`, but we may want to +// placate the same deriving in `ty::LateParamRegion`, but we may want to // actually attach a more meaningful ordering to scopes than the one // generated via deriving here. #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index e360fb3eaaf..b87c6885e04 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -525,6 +525,13 @@ impl<'tcx> TyCtxt<'tcx> { self.alloc_map.lock().reserve() } + /// Miri's provenance GC needs to see all live allocations. The interpreter manages most + /// allocations but some are managed by [`TyCtxt`] and without this method the interpreter + /// doesn't know their [`AllocId`]s are in use. + pub fn iter_allocs<F: FnMut(AllocId)>(self, func: F) { + self.alloc_map.lock().alloc_map.keys().copied().for_each(func) + } + /// Reserves a new ID *if* this allocation has not been dedup-reserved before. /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we /// don't want to dedup IDs for "real" memory! diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7054cede2d8..874c997c23b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1611,14 +1611,29 @@ impl Location { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DefLocation { Argument, - Body(Location), + Assignment(Location), + CallReturn { call: BasicBlock, target: Option<BasicBlock> }, } impl DefLocation { pub fn dominates(self, location: Location, dominators: &Dominators<BasicBlock>) -> bool { match self { DefLocation::Argument => true, - DefLocation::Body(def) => def.successor_within_block().dominates(location, dominators), + DefLocation::Assignment(def) => { + def.successor_within_block().dominates(location, dominators) + } + DefLocation::CallReturn { target: None, .. } => false, + DefLocation::CallReturn { call, target: Some(target) } => { + // The definition occurs on the call -> target edge. The definition dominates a use + // if and only if the edge is on all paths from the entry to the use. + // + // Note that a call terminator has only one edge that can reach the target, so when + // the call strongly dominates the target, all paths from the entry to the target + // go through the call -> target edge. + call != target + && dominators.dominates(call, target) + && dominators.dominates(target, location.block) + } } } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a1324858416..1974a35cb85 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1337,7 +1337,7 @@ pub fn write_allocations<'tcx>( fn alloc_ids_from_alloc( alloc: ConstAllocation<'_>, ) -> impl DoubleEndedIterator<Item = AllocId> + '_ { - alloc.inner().provenance().ptrs().values().map(|id| *id) + alloc.inner().provenance().ptrs().values().copied() } fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ { diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index a5358687c14..38538a0b316 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -507,11 +507,7 @@ fn write_span<W>( where W: Write, { - let maybe_alt_class = if layer > 0 { - if alt { " odd" } else { " even" } - } else { - "" - }; + let maybe_alt_class = if layer > 0 { if alt { " odd" } else { " even" } } else { "" }; let maybe_title_attr = if !tooltip.is_empty() { format!(" title=\"{}\"", escape_attr(tooltip)) } else { diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 9dfbe1733cc..9a6ac6ff57a 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -28,7 +28,9 @@ impl SwitchTargets { /// Inverse of `SwitchTargets::static_if`. pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> { - if let &[value] = &self.values[..] && let &[then, else_] = &self.targets[..] { + if let &[value] = &self.values[..] + && let &[then, else_] = &self.targets[..] + { Some((value, then, else_)) } else { None diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3086082fe8d..00c33113692 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -655,7 +655,9 @@ impl<'tcx> Pat<'tcx> { pub fn pat_error_reported(&self) -> Result<(), ErrorGuaranteed> { let mut error = None; self.walk(|pat| { - if let PatKind::Error(e) = pat.kind && error.is_none() { + if let PatKind::Error(e) = pat.kind + && error.is_none() + { error = Some(e); } error.is_none() diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index a5916c4ab85..7883cd338be 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -122,6 +122,8 @@ pub enum ProbeStep<'tcx> { /// used whenever there are multiple candidates to prove the /// current goalby . NestedProbe(Probe<'tcx>), + CommitIfOkStart, + CommitIfOkSuccess, } /// What kind of probe we're in. In case the probe represents a candidate, or @@ -142,6 +144,9 @@ pub enum ProbeKind<'tcx> { /// Used in the probe that wraps normalizing the non-self type for the unsize /// trait, which is also structurally matched on. UnsizeAssembly, + /// A call to `EvalCtxt::commit_if_ok` which failed, causing the work + /// to be discarded. + CommitIfOk, /// During upcasting from some source object to target object type, used to /// do a probe to find out what projection type(s) may be used to prove that /// the source type upholds all of the target type's object bounds. diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 4b73d8e41a1..ab9e0283918 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -109,6 +109,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::UpcastProjectionCompatibility => { writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } + ProbeKind::CommitIfOk => { + writeln!(self.f, "COMMIT_IF_OK:") + } ProbeKind::MiscCandidate { name, result } => { writeln!(self.f, "CANDIDATE {name}: {result:?}") } @@ -123,6 +126,8 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?, ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?, ProbeStep::NestedProbe(probe) => this.format_probe(probe)?, + ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?, + ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?, } } Ok(()) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1598d05251d..ee23c9c4897 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -39,7 +39,7 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal}; +use rustc_data_structures::sync::{FreezeReadGuard, Lock, WorkerLocal}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ DecorateLint, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, MultiSpan, @@ -69,7 +69,6 @@ use rustc_type_ir::TyKind::*; use rustc_type_ir::WithCachedTypeInfo; use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; -use std::any::Any; use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt; @@ -113,9 +112,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ExprConst = ty::Expr<'tcx>; type Region = Region<'tcx>; - type EarlyBoundRegion = ty::EarlyBoundRegion; + type EarlyParamRegion = ty::EarlyParamRegion; type BoundRegion = ty::BoundRegion; - type FreeRegion = ty::FreeRegion; + type LateParamRegion = ty::LateParamRegion; type InferRegion = ty::RegionVid; type PlaceholderRegion = ty::PlaceholderRegion; @@ -445,14 +444,14 @@ impl<'tcx> CommonConsts<'tcx> { } } -/// This struct contains information regarding the `ReFree(FreeRegion)` corresponding to a lifetime -/// conflict. +/// This struct contains information regarding a free parameter region, +/// either a `ReEarlyParam` or `ReLateParam`. #[derive(Debug)] pub struct FreeRegionInfo { - /// `LocalDefId` corresponding to FreeRegion + /// `LocalDefId` of the free region. pub def_id: LocalDefId, - /// the bound region corresponding to FreeRegion - pub boundregion: ty::BoundRegionKind, + /// the bound region corresponding to free region. + pub bound_region: ty::BoundRegionKind, /// checks if bound region is in Impl Item pub is_impl_item: bool, } @@ -544,12 +543,6 @@ pub struct GlobalCtxt<'tcx> { /// `rustc_symbol_mangling` crate for more information. stable_crate_id: StableCrateId, - /// This only ever stores a `LintStore` but we don't want a dependency on that type here. - /// - /// FIXME(Centril): consider `dyn LintStoreMarker` once - /// we can upcast to `Any` for some additional type safety. - pub lint_store: Lrc<dyn Any + sync::DynSync + sync::DynSend>, - pub dep_graph: DepGraph, pub prof: SelfProfilerRef, @@ -709,7 +702,6 @@ impl<'tcx> TyCtxt<'tcx> { s: &'tcx Session, crate_types: Vec<CrateType>, stable_crate_id: StableCrateId, - lint_store: Lrc<dyn Any + sync::DynSend + sync::DynSync>, arena: &'tcx WorkerLocal<Arena<'tcx>>, hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>, untracked: Untracked, @@ -730,7 +722,6 @@ impl<'tcx> TyCtxt<'tcx> { sess: s, crate_types, stable_crate_id, - lint_store, arena, hir_arena, interners, @@ -1080,8 +1071,8 @@ impl<'tcx> TyCtxt<'tcx> { pub fn is_suitable_region(self, mut region: Region<'tcx>) -> Option<FreeRegionInfo> { let (suitable_region_binding_scope, bound_region) = loop { let def_id = match region.kind() { - ty::ReFree(fr) => fr.bound_region.get_id()?.as_local()?, - ty::ReEarlyBound(ebr) => ebr.def_id.expect_local(), + ty::ReLateParam(fr) => fr.bound_region.get_id()?.as_local()?, + ty::ReEarlyParam(ebr) => ebr.def_id.expect_local(), _ => return None, // not a free region }; let scope = self.local_parent(def_id); @@ -1102,11 +1093,7 @@ impl<'tcx> TyCtxt<'tcx> { _ => false, }; - Some(FreeRegionInfo { - def_id: suitable_region_binding_scope, - boundregion: bound_region, - is_impl_item, - }) + Some(FreeRegionInfo { def_id: suitable_region_binding_scope, bound_region, is_impl_item }) } /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type. @@ -1743,7 +1730,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { match param.kind { GenericParamDefKind::Lifetime => { - ty::Region::new_early_bound(self, param.to_early_bound_region_data()).into() + ty::Region::new_early_param(self, param.to_early_bound_region_data()).into() } GenericParamDefKind::Type { .. } => Ty::new_param(self, param.index, param.name).into(), GenericParamDefKind::Const { .. } => ty::Const::new_param( @@ -2040,7 +2027,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Given the def-id of an early-bound lifetime on an RPIT corresponding to /// a duplicated captured lifetime, map it back to the early- or late-bound /// lifetime of the function from which it originally as captured. If it is - /// a late-bound lifetime, this will represent the liberated (`ReFree`) lifetime + /// a late-bound lifetime, this will represent the liberated (`ReLateParam`) lifetime /// of the signature. // FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just // re-use the generics of the opaque, this function will need to be tweaked slightly. @@ -2079,9 +2066,9 @@ impl<'tcx> TyCtxt<'tcx> { } let generics = self.generics_of(new_parent); - return ty::Region::new_early_bound( + return ty::Region::new_early_param( self, - ty::EarlyBoundRegion { + ty::EarlyParamRegion { def_id: ebv, index: generics .param_def_id_to_index(self, ebv) @@ -2092,7 +2079,7 @@ impl<'tcx> TyCtxt<'tcx> { } Some(resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv)) => { let new_parent = self.parent(lbv); - return ty::Region::new_free( + return ty::Region::new_late_param( self, new_parent, ty::BoundRegionKind::BrNamed( diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 75ea53195a3..f95ceeff150 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -189,14 +189,14 @@ pub struct DeepRejectCtxt { } impl DeepRejectCtxt { - pub fn args_refs_may_unify<'tcx>( + pub fn args_may_unify<'tcx>( self, obligation_args: GenericArgsRef<'tcx>, impl_args: GenericArgsRef<'tcx>, ) -> bool { iter::zip(obligation_args, impl_args).all(|(obl, imp)| { match (obl.unpack(), imp.unpack()) { - // We don't fast reject based on regions for now. + // We don't fast reject based on regions. (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => true, (GenericArgKind::Type(obl), GenericArgKind::Type(imp)) => { self.types_may_unify(obl, imp) @@ -231,7 +231,7 @@ impl DeepRejectCtxt { | ty::Never | ty::Tuple(..) | ty::FnPtr(..) - | ty::Foreign(..) => {} + | ty::Foreign(..) => debug_assert!(impl_ty.is_known_rigid()), ty::FnDef(..) | ty::Closure(..) | ty::Coroutine(..) @@ -260,7 +260,7 @@ impl DeepRejectCtxt { }, ty::Adt(obl_def, obl_args) => match k { &ty::Adt(impl_def, impl_args) => { - obl_def == impl_def && self.args_refs_may_unify(obl_args, impl_args) + obl_def == impl_def && self.args_may_unify(obl_args, impl_args) } _ => false, }, diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index e8ff3b230cb..3e64f9a2a90 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -250,7 +250,7 @@ impl<'tcx> TyCtxt<'tcx> { /// /// This method only replaces late bound regions. Any types or /// constants bound by `value` will cause an ICE. - pub fn replace_late_bound_regions<T, F>( + pub fn instantiate_bound_regions<T, F>( self, value: Binder<'tcx, T>, mut fld_r: F, @@ -261,11 +261,11 @@ impl<'tcx> TyCtxt<'tcx> { { let mut region_map = BTreeMap::new(); let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); - let value = self.replace_late_bound_regions_uncached(value, real_fld_r); + let value = self.instantiate_bound_regions_uncached(value, real_fld_r); (value, region_map) } - pub fn replace_late_bound_regions_uncached<T, F>( + pub fn instantiate_bound_regions_uncached<T, F>( self, value: Binder<'tcx, T>, mut replace_regions: F, @@ -325,8 +325,8 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - self.replace_late_bound_regions_uncached(value, |br| { - ty::Region::new_free(self, all_outlive_scope, br.kind) + self.instantiate_bound_regions_uncached(value, |br| { + ty::Region::new_late_param(self, all_outlive_scope, br.kind) }) } @@ -361,11 +361,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also /// method lookup and a few other places where precise region relationships are not required. - pub fn erase_late_bound_regions<T>(self, value: Binder<'tcx, T>) -> T + pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'tcx, T>) -> T where T: TypeFoldable<TyCtxt<'tcx>>, { - self.replace_late_bound_regions(value, |_| self.lifetimes.re_erased).0 + self.instantiate_bound_regions(value, |_| self.lifetimes.re_erased).0 } /// Anonymize all bound variables in `value`, this is mostly used to improve caching. diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index c9607b2245a..8fd08c724d2 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -809,7 +809,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ArgFolder<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { #[cold] #[inline(never)] - fn region_param_out_of_range(data: ty::EarlyBoundRegion, args: &[GenericArg<'_>]) -> ! { + fn region_param_out_of_range(data: ty::EarlyParamRegion, args: &[GenericArg<'_>]) -> ! { bug!( "Region parameter out of range when substituting in region {} (index={}, args = {:?})", data.name, @@ -820,7 +820,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ArgFolder<'a, 'tcx> { #[cold] #[inline(never)] - fn region_param_invalid(data: ty::EarlyBoundRegion, other: GenericArgKind<'_>) -> ! { + fn region_param_invalid(data: ty::EarlyParamRegion, other: GenericArgKind<'_>) -> ! { bug!( "Unexpected parameter {:?} when substituting in region {} (index={})", other, @@ -835,7 +835,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ArgFolder<'a, 'tcx> { // regions that appear in a function signature is done using // the specialized routine `ty::replace_late_regions()`. match *r { - ty::ReEarlyBound(data) => { + ty::ReEarlyParam(data) => { let rk = self.args.get(data.index as usize).map(|k| k.unpack()); match rk { Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), @@ -844,7 +844,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ArgFolder<'a, 'tcx> { } } ty::ReBound(..) - | ty::ReFree(_) + | ty::ReLateParam(_) | ty::ReStatic | ty::RePlaceholder(_) | ty::ReErased diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 888ee1d237a..4a6e3cfacd3 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -6,7 +6,7 @@ use rustc_hir::def_id::DefId; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; -use super::{Clause, EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt}; +use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt}; #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum GenericParamDefKind { @@ -62,9 +62,9 @@ pub struct GenericParamDef { } impl GenericParamDef { - pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { + pub fn to_early_bound_region_data(&self) -> ty::EarlyParamRegion { if let GenericParamDefKind::Lifetime = self.kind { - ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } + ty::EarlyParamRegion { def_id: self.def_id, index: self.index, name: self.name } } else { bug!("cannot convert a non-lifetime parameter def to an early bound region") } @@ -260,10 +260,10 @@ impl<'tcx> Generics { } } - /// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`. + /// Returns the `GenericParamDef` associated with this `EarlyParamRegion`. pub fn region_param( &'tcx self, - param: &EarlyBoundRegion, + param: &ty::EarlyParamRegion, tcx: TyCtxt<'tcx>, ) -> &'tcx GenericParamDef { let param = self.param_at(param.index as usize, tcx); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 4223e503f5e..aca6acd783b 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -215,7 +215,7 @@ pub enum LayoutError<'tcx> { SizeOverflow(Ty<'tcx>), NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), ReferencesError(ErrorGuaranteed), - Cycle, + Cycle(ErrorGuaranteed), } impl<'tcx> LayoutError<'tcx> { @@ -226,7 +226,7 @@ impl<'tcx> LayoutError<'tcx> { Unknown(_) => middle_unknown_layout, SizeOverflow(_) => middle_values_too_big, NormalizationFailure(_, _) => middle_cannot_be_normalized, - Cycle => middle_cycle, + Cycle(_) => middle_cycle, ReferencesError(_) => middle_layout_references_error, } } @@ -240,7 +240,7 @@ impl<'tcx> LayoutError<'tcx> { NormalizationFailure(ty, e) => { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } } - Cycle => E::Cycle, + Cycle(_) => E::Cycle, ReferencesError(_) => E::ReferencesError, } } @@ -261,7 +261,7 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { t, e.get_type_for_failure() ), - LayoutError::Cycle => write!(f, "a cycle occurred during layout computation"), + LayoutError::Cycle(_) => write!(f, "a cycle occurred during layout computation"), LayoutError::ReferencesError(_) => write!(f, "the type has an unknown layout"), } } @@ -333,7 +333,7 @@ impl<'tcx> SizeSkeleton<'tcx> { Err(err @ LayoutError::Unknown(_)) => err, // We can't extract SizeSkeleton info from other layout errors Err( - e @ LayoutError::Cycle + e @ LayoutError::Cycle(_) | e @ LayoutError::SizeOverflow(_) | e @ LayoutError::NormalizationFailure(..) | e @ LayoutError::ReferencesError(_), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e1c616ba078..559bf9fb825 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -98,11 +98,12 @@ pub use self::sty::BoundRegionKind::*; pub use self::sty::{ AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind, CanonicalPolyFnSig, ClauseKind, ClosureArgs, ClosureArgsParts, ConstKind, - ConstVid, CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate, - ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, InlineConstArgs, - InlineConstArgsParts, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection, - PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, PredicateKind, Region, - RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo, + ConstVid, CoroutineArgs, CoroutineArgsParts, EarlyParamRegion, EffectVid, ExistentialPredicate, + ExistentialProjection, ExistentialTraitRef, FnSig, GenSig, InlineConstArgs, + InlineConstArgsParts, LateParamRegion, ParamConst, ParamTy, PolyExistentialPredicate, + PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, + PredicateKind, Region, RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, UpvarArgs, + VarianceDiagInfo, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ @@ -463,7 +464,7 @@ pub struct CReaderCacheKey { #[rustc_pass_by_value] pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo<TyKind<'tcx>>>); -impl ty::EarlyBoundRegion { +impl EarlyParamRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). pub fn has_name(&self) -> bool { diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index fd125af2074..27c436c82f5 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -97,6 +97,10 @@ impl<'tcx> TyCtxt<'tcx> { /// N.B., currently, higher-ranked type bounds inhibit /// normalization. Therefore, each time we erase them in /// codegen, we need to normalize the contents. + // FIXME(@lcnr): This method should not be necessary, we now normalize + // inside of binders. We should be able to only use + // `tcx.instantiate_bound_regions_with_erased`. Same for the `try_X` + // variant. #[tracing::instrument(level = "debug", skip(self, param_env))] pub fn normalize_erasing_late_bound_regions<T>( self, @@ -106,7 +110,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - let value = self.erase_late_bound_regions(value); + let value = self.instantiate_bound_regions_with_erased(value); self.normalize_erasing_regions(param_env, value) } @@ -126,7 +130,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable<TyCtxt<'tcx>>, { - let value = self.erase_late_bound_regions(value); + let value = self.instantiate_bound_regions_with_erased(value); self.try_normalize_erasing_regions(param_env, value) } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index cabf2ab5dfe..1305f63bdbc 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -113,7 +113,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { ty::ReError(_) => return r, // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) => {} + ty::ReEarlyParam(_) | ty::ReLateParam(_) => {} ty::RePlaceholder(_) | ty::ReVar(_) => { // All of the regions in the type should either have been diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 21bf5b4b3b1..ad070dcc9e3 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2158,10 +2158,10 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions; match *region { - ty::ReEarlyBound(ref data) => data.has_name(), + ty::ReEarlyParam(ref data) => data.has_name(), ty::ReBound(_, ty::BoundRegion { kind: br, .. }) - | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) + | ty::ReLateParam(ty::LateParamRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { bound: ty::BoundRegion { kind: br, .. }, .. }) => { @@ -2228,14 +2228,14 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // to fit that into a short string. Hence the recommendation to use // `explain_region()` or `note_and_explain_region()`. match *region { - ty::ReEarlyBound(ref data) => { + ty::ReEarlyParam(ref data) => { if data.name != kw::Empty { p!(write("{}", data.name)); return Ok(()); } } ty::ReBound(_, ty::BoundRegion { kind: br, .. }) - | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) + | ty::ReLateParam(ty::LateParamRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { bound: ty::BoundRegion { kind: br, .. }, .. }) => { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 6af68bc5dba..e223ffd7c5d 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -79,9 +79,9 @@ impl fmt::Debug for ty::BoundRegionKind { } } -impl fmt::Debug for ty::FreeRegion { +impl fmt::Debug for ty::LateParamRegion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) + write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) } } @@ -444,7 +444,7 @@ TrivialTypeTraversalImpls! { crate::ty::Placeholder<crate::ty::BoundRegion>, crate::ty::Placeholder<crate::ty::BoundTy>, crate::ty::Placeholder<ty::BoundVar>, - crate::ty::FreeRegion, + crate::ty::LateParamRegion, crate::ty::InferTy, crate::ty::IntVarValue, crate::ty::adjustment::PointerCoercion, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d0df4498a66..b22ff5c57c3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -61,9 +61,9 @@ pub struct TypeAndMut<'tcx> { #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] -/// A "free" region `fr` can be interpreted as "some region +/// The parameter representation of late-bound function parameters, "some region /// at least as big as the scope `fr.scope`". -pub struct FreeRegion { +pub struct LateParamRegion { pub scope: DefId, pub bound_region: BoundRegionKind, } @@ -1036,7 +1036,7 @@ impl<'tcx, T> Binder<'tcx, T> { /// risky thing to do because it's easy to get confused about /// De Bruijn indices and the like. It is usually better to /// discharge the binder using `no_bound_vars` or - /// `replace_late_bound_regions` or something like + /// `instantiate_bound_regions` or something like /// that. `skip_binder` is only valid when you are either /// extracting data that has nothing to do with bound vars, you /// are doing some sort of test that does not involve bound @@ -1245,6 +1245,28 @@ impl<'tcx> AliasTy<'tcx> { } } + /// Whether this alias type is an opaque. + pub fn is_opaque(self, tcx: TyCtxt<'tcx>) -> bool { + matches!(self.opt_kind(tcx), Some(ty::AliasKind::Opaque)) + } + + /// FIXME: rename `AliasTy` to `AliasTerm` and always handle + /// constants. This function can then be removed. + pub fn opt_kind(self, tcx: TyCtxt<'tcx>) -> Option<ty::AliasKind> { + match tcx.def_kind(self.def_id) { + DefKind::AssocTy + if let DefKind::Impl { of_trait: false } = + tcx.def_kind(tcx.parent(self.def_id)) => + { + Some(ty::Inherent) + } + DefKind::AssocTy => Some(ty::Projection), + DefKind::OpaqueTy => Some(ty::Opaque), + DefKind::TyAlias => Some(ty::Weak), + _ => None, + } + } + pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { Ty::new_alias(tcx, self.kind(tcx), self) } @@ -1468,11 +1490,11 @@ pub struct Region<'tcx>(pub Interned<'tcx, RegionKind<'tcx>>); impl<'tcx> Region<'tcx> { #[inline] - pub fn new_early_bound( + pub fn new_early_param( tcx: TyCtxt<'tcx>, - early_bound_region: ty::EarlyBoundRegion, + early_bound_region: ty::EarlyParamRegion, ) -> Region<'tcx> { - tcx.intern_region(ty::ReEarlyBound(early_bound_region)) + tcx.intern_region(ty::ReEarlyParam(early_bound_region)) } #[inline] @@ -1493,12 +1515,12 @@ impl<'tcx> Region<'tcx> { } #[inline] - pub fn new_free( + pub fn new_late_param( tcx: TyCtxt<'tcx>, scope: DefId, bound_region: ty::BoundRegionKind, ) -> Region<'tcx> { - tcx.intern_region(ty::ReFree(ty::FreeRegion { scope, bound_region })) + tcx.intern_region(ty::ReLateParam(ty::LateParamRegion { scope, bound_region })) } #[inline] @@ -1549,10 +1571,10 @@ impl<'tcx> Region<'tcx> { /// to avoid the cost of the `match`. pub fn new_from_kind(tcx: TyCtxt<'tcx>, kind: RegionKind<'tcx>) -> Region<'tcx> { match kind { - ty::ReEarlyBound(region) => Region::new_early_bound(tcx, region), + ty::ReEarlyParam(region) => Region::new_early_param(tcx, region), ty::ReBound(debruijn, region) => Region::new_bound(tcx, debruijn, region), - ty::ReFree(ty::FreeRegion { scope, bound_region }) => { - Region::new_free(tcx, scope, bound_region) + ty::ReLateParam(ty::LateParamRegion { scope, bound_region }) => { + Region::new_late_param(tcx, scope, bound_region) } ty::ReStatic => tcx.lifetimes.re_static, ty::ReVar(vid) => Region::new_var(tcx, vid), @@ -1574,13 +1596,13 @@ impl<'tcx> Deref for Region<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)] #[derive(HashStable)] -pub struct EarlyBoundRegion { +pub struct EarlyParamRegion { pub def_id: DefId, pub index: u32, pub name: Symbol, } -impl fmt::Debug for EarlyBoundRegion { +impl fmt::Debug for EarlyParamRegion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}, {}, {}", self.def_id, self.index, self.name) } @@ -1722,9 +1744,9 @@ impl<'tcx> Region<'tcx> { pub fn get_name(self) -> Option<Symbol> { if self.has_name() { match *self { - ty::ReEarlyBound(ebr) => Some(ebr.name), + ty::ReEarlyParam(ebr) => Some(ebr.name), ty::ReBound(_, br) => br.kind.get_name(), - ty::ReFree(fr) => fr.bound_region.get_name(), + ty::ReLateParam(fr) => fr.bound_region.get_name(), ty::ReStatic => Some(kw::StaticLifetime), ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(), _ => None, @@ -1744,9 +1766,9 @@ impl<'tcx> Region<'tcx> { /// Is this region named by the user? pub fn has_name(self) -> bool { match *self { - ty::ReEarlyBound(ebr) => ebr.has_name(), + ty::ReEarlyParam(ebr) => ebr.has_name(), ty::ReBound(_, br) => br.kind.is_named(), - ty::ReFree(fr) => fr.bound_region.is_named(), + ty::ReLateParam(fr) => fr.bound_region.is_named(), ty::ReStatic => true, ty::ReVar(..) => false, ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(), @@ -1802,12 +1824,12 @@ impl<'tcx> Region<'tcx> { flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PLACEHOLDER; } - ty::ReEarlyBound(..) => { + ty::ReEarlyParam(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PARAM; } - ty::ReFree { .. } => { + ty::ReLateParam { .. } => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; } @@ -1851,22 +1873,28 @@ impl<'tcx> Region<'tcx> { /// function might return the `DefId` of a closure. pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId { match *self { - ty::ReEarlyBound(br) => tcx.parent(br.def_id), - ty::ReFree(fr) => fr.scope, + ty::ReEarlyParam(br) => tcx.parent(br.def_id), + ty::ReLateParam(fr) => fr.scope, _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), } } /// True for free regions other than `'static`. - pub fn is_free(self) -> bool { - matches!(*self, ty::ReEarlyBound(_) | ty::ReFree(_)) + pub fn is_param(self) -> bool { + matches!(*self, ty::ReEarlyParam(_) | ty::ReLateParam(_)) } - /// True if `self` is a free region or static. - pub fn is_free_or_static(self) -> bool { + /// True for free region in the current context. + /// + /// This is the case for `'static` and param regions. + pub fn is_free(self) -> bool { match *self { - ty::ReStatic => true, - _ => self.is_free(), + ty::ReStatic | ty::ReEarlyParam(..) | ty::ReLateParam(..) => true, + ty::ReVar(..) + | ty::RePlaceholder(..) + | ty::ReBound(..) + | ty::ReErased + | ty::ReError(..) => false, } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 1a76a8d350d..914ff1fabd1 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -126,7 +126,7 @@ pub struct TypeckResults<'tcx> { /// fn(&'a u32) -> u32 /// ``` /// - /// Note that `'a` is not bound (it would be an `ReFree`) and + /// Note that `'a` is not bound (it would be an `ReLateParam`) and /// that the `Foo` opaque type is replaced by its hidden type. liberated_fn_sigs: ItemLocalMap<ty::FnSig<'tcx>>, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 144e98f784a..e9f65d99a2e 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -35,12 +35,14 @@ pub struct Discr<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CheckRegions { No, - /// Only permit early bound regions. This is useful for Adts which - /// can never have late bound regions. - OnlyEarlyBound, - /// Permit both late bound and early bound regions. Use this for functions, - /// which frequently have late bound regions. - Bound, + /// Only permit parameter regions. This should be used + /// for everything apart from functions, which may use + /// `ReBound` to represent late-bound regions. + OnlyParam, + /// Check region parameters from a function definition. + /// Allows `ReEarlyParam` and `ReBound` to handle early + /// and late-bound region parameters. + FromFunction, } #[derive(Copy, Clone, Debug)] @@ -431,7 +433,7 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|&(_, k)| { match k.unpack() { GenericArgKind::Lifetime(region) => match region.kind() { - ty::ReEarlyBound(ref ebr) => { + ty::ReEarlyParam(ref ebr) => { !impl_generics.region_param(ebr, self).pure_wrt_drop } // Error: not a region param @@ -468,17 +470,17 @@ impl<'tcx> TyCtxt<'tcx> { for arg in args { match arg.unpack() { GenericArgKind::Lifetime(lt) => match (ignore_regions, lt.kind()) { - (CheckRegions::Bound, ty::ReBound(di, reg)) => { + (CheckRegions::FromFunction, ty::ReBound(di, reg)) => { if !seen_late.insert((di, reg)) { return Err(NotUniqueParam::DuplicateParam(lt.into())); } } - (CheckRegions::OnlyEarlyBound | CheckRegions::Bound, ty::ReEarlyBound(p)) => { + (CheckRegions::OnlyParam | CheckRegions::FromFunction, ty::ReEarlyParam(p)) => { if !seen.insert(p.index) { return Err(NotUniqueParam::DuplicateParam(lt.into())); } } - (CheckRegions::OnlyEarlyBound | CheckRegions::Bound, _) => { + (CheckRegions::OnlyParam | CheckRegions::FromFunction, _) => { return Err(NotUniqueParam::NotParam(lt.into())); } (CheckRegions::No, _) => {} diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index f30993c9a69..2b4ae373626 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -114,12 +114,11 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>> } impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> { - fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self { // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really, // tcx.arena.alloc is pretty much equal to leaking). - // FIXME: `Cycle` should carry the ErrorGuaranteed - Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle))) + Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle(guar)))) } } diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index 3de2f45ad9a..d302d538ad4 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -162,6 +162,19 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { expected: expected.to_string(), } } + + fn stmt_error(&self, stmt: StmtId, expected: &'static str) -> ParseError { + let stmt = &self.thir[stmt]; + let span = match stmt.kind { + StmtKind::Expr { expr, .. } => self.thir[expr].span, + StmtKind::Let { span, .. } => span, + }; + ParseError { + span, + item_description: format!("{:?}", stmt.kind), + expected: expected.to_string(), + } + } } type PResult<T> = Result<T, ParseError>; diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs index e2ab2cb90c7..a6f9caada2d 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse.rs @@ -27,10 +27,13 @@ macro_rules! parse_by_kind { $expr_name:pat, $expected:literal, $( - @call($name:literal, $args:ident) => $call_expr:expr, + @call($name:ident, $args:ident) => $call_expr:expr, )* $( - $pat:pat => $expr:expr, + @variant($adt:ident, $variant:ident) => $variant_expr:expr, + )* + $( + $pat:pat $(if $guard:expr)? => $expr:expr, )* ) => {{ let expr_id = $self.preparse($expr_id); @@ -42,14 +45,20 @@ macro_rules! parse_by_kind { ExprKind::Call { ty, fun: _, args: $args, .. } if { match ty.kind() { ty::FnDef(did, _) => { - $self.tcx.is_diagnostic_item(rustc_span::Symbol::intern($name), *did) + $self.tcx.is_diagnostic_item(rustc_span::sym::$name, *did) } _ => false, } } => $call_expr, )* $( - $pat => $expr, + ExprKind::Adt(box AdtExpr { adt_def, variant_index, .. }) if { + $self.tcx.is_diagnostic_item(rustc_span::sym::$adt, adt_def.did()) && + adt_def.variants()[*variant_index].name == rustc_span::sym::$variant + } => $variant_expr, + )* + $( + $pat $(if $guard)? => $expr, )* #[allow(unreachable_patterns)] _ => return Err($self.expr_error(expr_id, $expected)) @@ -172,7 +181,8 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ExprKind::Block { block } => &self.thir[*block].stmts, ); for (i, block_def) in block_defs.iter().enumerate() { - let block = self.parse_block_def(self.statement_as_expr(*block_def)?)?; + let is_cleanup = self.body.basic_blocks_mut()[BasicBlock::from_usize(i)].is_cleanup; + let block = self.parse_block_def(self.statement_as_expr(*block_def)?, is_cleanup)?; self.body.basic_blocks_mut()[BasicBlock::from_usize(i)] = block; } @@ -181,15 +191,28 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { fn parse_block_decls(&mut self, stmts: impl Iterator<Item = StmtId>) -> PResult<()> { for stmt in stmts { - let (var, _, _) = self.parse_let_statement(stmt)?; - let data = BasicBlockData::new(None); - let block = self.body.basic_blocks_mut().push(data); - self.block_map.insert(var, block); + self.parse_basic_block_decl(stmt)?; } - Ok(()) } + fn parse_basic_block_decl(&mut self, stmt: StmtId) -> PResult<()> { + match &self.thir[stmt].kind { + StmtKind::Let { pattern, initializer: Some(initializer), .. } => { + let (var, ..) = self.parse_var(pattern)?; + let mut data = BasicBlockData::new(None); + data.is_cleanup = parse_by_kind!(self, *initializer, _, "basic block declaration", + @variant(mir_basic_block, Normal) => false, + @variant(mir_basic_block, Cleanup) => true, + ); + let block = self.body.basic_blocks_mut().push(data); + self.block_map.insert(var, block); + Ok(()) + } + _ => Err(self.stmt_error(stmt, "let statement with an initializer")), + } + } + fn parse_local_decls(&mut self, mut stmts: impl Iterator<Item = StmtId>) -> PResult<()> { let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?; self.local_map.insert(ret_var, Local::from_u32(0)); @@ -219,7 +242,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { }; let span = self.thir[expr].span; let (name, operand) = parse_by_kind!(self, expr, _, "debuginfo", - @call("mir_debuginfo", args) => { + @call(mir_debuginfo, args) => { (args[0], args[1]) }, ); @@ -281,12 +304,13 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { } } - fn parse_block_def(&self, expr_id: ExprId) -> PResult<BasicBlockData<'tcx>> { + fn parse_block_def(&self, expr_id: ExprId, is_cleanup: bool) -> PResult<BasicBlockData<'tcx>> { let block = parse_by_kind!(self, expr_id, _, "basic block", ExprKind::Block { block } => &self.thir[*block], ); let mut data = BasicBlockData::new(None); + data.is_cleanup = is_cleanup; for stmt_id in &*block.stmts { let stmt = self.statement_as_expr(*stmt_id)?; let span = self.thir[stmt].span; diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index fd2c57a0a6f..4ce7f831c87 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -13,19 +13,19 @@ use super::{parse_by_kind, PResult, ParseCtxt}; impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { pub fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> { parse_by_kind!(self, expr_id, _, "statement", - @call("mir_storage_live", args) => { + @call(mir_storage_live, args) => { Ok(StatementKind::StorageLive(self.parse_local(args[0])?)) }, - @call("mir_storage_dead", args) => { + @call(mir_storage_dead, args) => { Ok(StatementKind::StorageDead(self.parse_local(args[0])?)) }, - @call("mir_deinit", args) => { + @call(mir_deinit, args) => { Ok(StatementKind::Deinit(Box::new(self.parse_place(args[0])?))) }, - @call("mir_retag", args) => { + @call(mir_retag, args) => { Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?))) }, - @call("mir_set_discriminant", args) => { + @call(mir_set_discriminant, args) => { let place = self.parse_place(args[0])?; let var = self.parse_integer_literal(args[1])? as u32; Ok(StatementKind::SetDiscriminant { @@ -43,24 +43,30 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { pub fn parse_terminator(&self, expr_id: ExprId) -> PResult<TerminatorKind<'tcx>> { parse_by_kind!(self, expr_id, expr, "terminator", - @call("mir_return", _args) => { + @call(mir_return, _args) => { Ok(TerminatorKind::Return) }, - @call("mir_goto", args) => { + @call(mir_goto, args) => { Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } ) }, - @call("mir_unreachable", _args) => { + @call(mir_unreachable, _args) => { Ok(TerminatorKind::Unreachable) }, - @call("mir_drop", args) => { + @call(mir_unwind_resume, _args) => { + Ok(TerminatorKind::UnwindResume) + }, + @call(mir_unwind_terminate, args) => { + Ok(TerminatorKind::UnwindTerminate(self.parse_unwind_terminate_reason(args[0])?)) + }, + @call(mir_drop, args) => { Ok(TerminatorKind::Drop { place: self.parse_place(args[0])?, target: self.parse_block(args[1])?, - unwind: UnwindAction::Continue, + unwind: self.parse_unwind_action(args[2])?, replace: false, }) }, - @call("mir_call", args) => { + @call(mir_call, args) => { self.parse_call(args) }, ExprKind::Match { scrutinee, arms, .. } => { @@ -70,6 +76,34 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ) } + fn parse_unwind_terminate_reason(&self, expr_id: ExprId) -> PResult<UnwindTerminateReason> { + parse_by_kind!(self, expr_id, _, "unwind terminate reason", + @variant(mir_unwind_terminate_reason, Abi) => { + Ok(UnwindTerminateReason::Abi) + }, + @variant(mir_unwind_terminate_reason, InCleanup) => { + Ok(UnwindTerminateReason::InCleanup) + }, + ) + } + + fn parse_unwind_action(&self, expr_id: ExprId) -> PResult<UnwindAction> { + parse_by_kind!(self, expr_id, _, "unwind action", + @call(mir_unwind_continue, _args) => { + Ok(UnwindAction::Continue) + }, + @call(mir_unwind_unreachable, _args) => { + Ok(UnwindAction::Unreachable) + }, + @call(mir_unwind_terminate, args) => { + Ok(UnwindAction::Terminate(self.parse_unwind_terminate_reason(args[0])?)) + }, + @call(mir_unwind_cleanup, args) => { + Ok(UnwindAction::Cleanup(self.parse_block(args[0])?)) + }, + ) + } + fn parse_match(&self, arms: &[ArmId], span: Span) -> PResult<SwitchTargets> { let Some((otherwise, rest)) = arms.split_last() else { return Err(ParseError { @@ -113,6 +147,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ); let destination = self.parse_place(destination)?; let target = self.parse_block(args[1])?; + let unwind = self.parse_unwind_action(args[2])?; parse_by_kind!(self, call, _, "function call", ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => { @@ -126,7 +161,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { args, destination, target: Some(target), - unwind: UnwindAction::Continue, + unwind, call_source: if *from_hir_call { CallSource::Normal } else { CallSource::OverloadedOperator }, @@ -138,25 +173,25 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> { parse_by_kind!(self, expr_id, expr, "rvalue", - @call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant), - @call("mir_cast_transmute", args) => { + @call(mir_discriminant, args) => self.parse_place(args[0]).map(Rvalue::Discriminant), + @call(mir_cast_transmute, args) => { let source = self.parse_operand(args[0])?; Ok(Rvalue::Cast(CastKind::Transmute, source, expr.ty)) }, - @call("mir_checked", args) => { + @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", ExprKind::Binary { op, lhs, rhs } => Ok(Rvalue::CheckedBinaryOp( *op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)) )), ) }, - @call("mir_offset", args) => { + @call(mir_offset, args) => { let ptr = self.parse_operand(args[0])?; let offset = self.parse_operand(args[1])?; Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, - @call("mir_len", args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), - @call("mir_copy_for_deref", args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), + @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), + @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) ), @@ -206,9 +241,9 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { pub fn parse_operand(&self, expr_id: ExprId) -> PResult<Operand<'tcx>> { parse_by_kind!(self, expr_id, expr, "operand", - @call("mir_move", args) => self.parse_place(args[0]).map(Operand::Move), - @call("mir_static", args) => self.parse_static(args[0]), - @call("mir_static_mut", args) => self.parse_static(args[0]), + @call(mir_move, args) => self.parse_place(args[0]).map(Operand::Move), + @call(mir_static, args) => self.parse_static(args[0]), + @call(mir_static_mut, args) => self.parse_static(args[0]), ExprKind::Literal { .. } | ExprKind::NamedConst { .. } | ExprKind::NonHirLiteral { .. } @@ -229,7 +264,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { fn parse_place_inner(&self, expr_id: ExprId) -> PResult<(Place<'tcx>, PlaceTy<'tcx>)> { let (parent, proj) = parse_by_kind!(self, expr_id, expr, "place", - @call("mir_field", args) => { + @call(mir_field, args) => { let (parent, ty) = self.parse_place_inner(args[0])?; let field = FieldIdx::from_u32(self.parse_integer_literal(args[1])? as u32); let field_ty = ty.field_ty(self.tcx, field); @@ -237,7 +272,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { let place = parent.project_deeper(&[proj], self.tcx); return Ok((place, PlaceTy::from_ty(field_ty))); }, - @call("mir_variant", args) => { + @call(mir_variant, args) => { (args[0], PlaceElem::Downcast( None, VariantIdx::from_u32(self.parse_integer_literal(args[1])? as u32) @@ -245,7 +280,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { }, ExprKind::Deref { arg } => { parse_by_kind!(self, *arg, _, "does not matter", - @call("mir_make_place", args) => return self.parse_place_inner(args[0]), + @call(mir_make_place, args) => return self.parse_place_inner(args[0]), _ => (*arg, PlaceElem::Deref), ) }, diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 7c729016521..886d805454d 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -656,17 +656,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - let args = args.as_coroutine(); let yield_ty = args.yield_ty(); let return_ty = args.return_ty(); - let self_ty = Ty::new_adt( - tcx, - tcx.adt_def(tcx.lang_items().pin_type().unwrap()), - tcx.mk_args(&[Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty).into()]), - ); - let coroutine_state = Ty::new_adt( - tcx, - tcx.adt_def(tcx.lang_items().coroutine_state().unwrap()), - tcx.mk_args(&[yield_ty.into(), return_ty.into()]), - ); - (vec![self_ty, args.resume_ty()], coroutine_state, Some(yield_ty)) + (vec![coroutine_ty, args.resume_ty()], return_ty, Some(yield_ty)) } dk => bug!("{:?} is not a body: {:?}", def_id, dk), }; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index dfd39b512e2..50f57b28a98 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -192,10 +192,10 @@ impl<'tcx> Cx<'tcx> { cast: PointerCoercion::ArrayToPointer, } } else if let hir::ExprKind::Path(ref qpath) = source.kind - && let res = self.typeck_results().qpath_res(qpath, source.hir_id) - && let ty = self.typeck_results().node_type(source.hir_id) - && let ty::Adt(adt_def, args) = ty.kind() - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res + && let res = self.typeck_results().qpath_res(qpath, source.hir_id) + && let ty = self.typeck_results().node_type(source.hir_id) + && let ty::Adt(adt_def, args) = ty.kind() + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res { // Check whether this is casting an enum variant discriminant. // To prevent cycles, we refer to the discriminant initializer, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index bcd9140f0e1..261bd30b94a 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -135,7 +135,7 @@ impl<'tcx> Cx<'tcx> { let env_region = ty::Region::new_bound(self.tcx, ty::INNERMOST, br); let closure_env_ty = self.tcx.closure_env_ty(closure_def_id, closure_args, env_region).unwrap(); - let liberated_closure_env_ty = self.tcx.erase_late_bound_regions( + let liberated_closure_env_ty = self.tcx.instantiate_bound_regions_with_erased( ty::Binder::bind_with_vars(closure_env_ty, bound_vars), ); let env_param = Param { diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 48a590f5d37..440767927d7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -200,7 +200,9 @@ impl<'tcx> ConstToPat<'tcx> { // We errored. Signal that in the pattern, so that follow up errors can be silenced. let kind = PatKind::Error(e); return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); - } else if let ty::Adt(..) = cv.ty().kind() && matches!(cv, mir::Const::Val(..)) { + } else if let ty::Adt(..) = cv.ty().kind() + && matches!(cv, mir::Const::Val(..)) + { // This branch is only entered when the current `cv` is `mir::Const::Val`. // This is because `mir::Const::ty` has already been handled by `Self::recur` // and the invalid types may be ignored. diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 9bb26693cb2..79bed960b95 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -69,7 +69,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => { let fn_ty = self.instantiate_ty(f.const_.ty()); - self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() && tcx.is_intrinsic(def_id) { + self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() + && tcx.is_intrinsic(def_id) + { // Don't give intrinsics the extra penalty for calls INSTR_COST } else { diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index b34ec95b4e8..1bc3c25c653 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -138,7 +138,9 @@ impl CoverageCounters { // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also // have an expression (to be injected into an existing `BasicBlock` represented by this // `BasicCoverageBlock`). - if let Some(node_counter) = self.bcb_counter(to_bcb) && !node_counter.is_expression() { + if let Some(node_counter) = self.bcb_counter(to_bcb) + && !node_counter.is_expression() + { bug!( "attempt to add an incoming edge counter from {from_bcb:?} \ when the target BCB already has {node_counter:?}" diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index b318134ae67..e0abb5da047 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -475,7 +475,9 @@ impl<'a> CoverageSpansGenerator<'a> { } while let Some(curr) = self.sorted_spans_iter.next() { debug!("FOR curr={:?}", curr); - if let Some(prev) = &self.some_prev && prev.span.lo() > curr.span.lo() { + if let Some(prev) = &self.some_prev + && prev.span.lo() > curr.span.lo() + { // Skip curr because prev has already advanced beyond the end of curr. // This can only happen if a prior iteration updated `prev` to skip past // a region of code, such as skipping past a closure. diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 81d2bba989a..bb2a90a06da 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -597,7 +597,9 @@ fn propagatable_scalar( state: &State<FlatSet<Scalar>>, map: &Map, ) -> Option<Scalar> { - if let FlatSet::Elem(value) = state.get_idx(place, map) && value.try_to_int().is_ok() { + if let FlatSet::Elem(value) = state.get_idx(place, map) + && value.try_to_int().is_ok() + { // Do not attempt to propagate pointers, as we may fail to preserve their identity. Some(value) } else { @@ -836,7 +838,8 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { location: Location, ) { if let PlaceElem::Index(local) = elem - && let Some(value) = self.visitor.try_make_constant(self.ecx, local.into(), self.state, self.map) + && let Some(value) = + self.visitor.try_make_constant(self.ecx, local.into(), self.state, self.map) { self.visitor.patch.before_effect.insert((location, local.into()), value); } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index dce298e92e1..98a03618286 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -461,7 +461,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } NullaryOp(null_op, ty) => { let layout = self.ecx.layout_of(ty).ok()?; - if let NullOp::SizeOf | NullOp::AlignOf = null_op && layout.is_unsized() { + if let NullOp::SizeOf | NullOp::AlignOf = null_op + && layout.is_unsized() + { return None; } let val = match null_op { @@ -865,7 +867,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { .collect(); let fields = fields?; - if let AggregateTy::Array = ty && fields.len() > 4 { + if let AggregateTy::Array = ty + && fields.len() > 4 + { let first = fields[0]; if fields.iter().all(|&v| v == first) { let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); @@ -1008,8 +1012,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { // Do not try to simplify a constant, it's already in canonical shape. && !matches!(rvalue, Rvalue::Use(Operand::Constant(_))) { - if let Some(value) = self.simplify_rvalue(rvalue, location) - { + if let Some(value) = self.simplify_rvalue(rvalue, location) { if let Some(const_) = self.try_as_constant(value) { *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); } else if let Some(local) = self.try_as_local(value, location) diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 7b918be4474..22300ad24be 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -247,7 +247,9 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { let last_non_rec = self.opportunities.len(); let predecessors = &self.body.basic_blocks.predecessors()[bb]; - if let &[pred] = &predecessors[..] && bb != START_BLOCK { + if let &[pred] = &predecessors[..] + && bb != START_BLOCK + { let term = self.body.basic_blocks[pred].terminator(); match term.kind { TerminatorKind::SwitchInt { ref discr, ref targets } => { @@ -419,8 +421,10 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // Do not support unions. AggregateKind::Adt(.., Some(_)) => return None, AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { - if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) - && let Some(discr_value) = discriminant_for_variant(agg_ty, *variant_index) + if let Some(discr_target) = + self.map.apply(lhs, TrackElem::Discriminant) + && let Some(discr_value) = + discriminant_for_variant(agg_ty, *variant_index) { self.process_operand(bb, discr_target, &discr_value, state); } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 5f3d8dfc6c4..249e0fc633e 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -4,7 +4,6 @@ use crate::MirPass; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::sym; -use rustc_target::abi::{FieldIdx, VariantIdx}; pub struct LowerIntrinsics; @@ -251,37 +250,6 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { }); terminator.kind = TerminatorKind::Goto { target }; } - sym::option_payload_ptr => { - if let (Some(target), Some(arg)) = (*target, args[0].place()) { - let ty::RawPtr(ty::TypeAndMut { ty: dest_ty, .. }) = - destination.ty(local_decls, tcx).ty.kind() - else { - bug!(); - }; - - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::AddressOf( - Mutability::Not, - arg.project_deeper( - &[ - PlaceElem::Deref, - PlaceElem::Downcast( - Some(sym::Some), - VariantIdx::from_u32(1), - ), - PlaceElem::Field(FieldIdx::from_u32(0), *dest_ty), - ], - tcx, - ), - ), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } - } sym::transmute | sym::transmute_unchecked => { let dst_ty = destination.ty(local_decls, tcx).ty; let Ok([arg]) = <[_; 1]>::try_from(std::mem::take(args)) else { diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 5aa3c3cfe4d..9f59f9d1245 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -17,6 +17,11 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts { if tcx.type_of(body.source.def_id()).instantiate_identity().is_coroutine() { return; } + + if !tcx.consider_optimizing(|| format!("RemoveZsts - {:?}", body.source.def_id())) { + return; + } + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); let local_decls = &body.local_decls; let mut replacer = Replacer { tcx, param_env, local_decls }; @@ -125,12 +130,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { if let Some(place_for_ty) = place_for_ty && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty && self.known_to_be_zst(ty) - && self.tcx.consider_optimizing(|| { - format!( - "RemoveZsts - Place: {:?} SourceInfo: {:?}", - place_for_ty, statement.source_info - ) - }) { statement.make_nop(); } else { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 4ae5ea4c8d6..a9640146a52 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -179,7 +179,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) GenericArgs::identity_for_item(tcx, def_id) }; let sig = tcx.fn_sig(def_id).instantiate(tcx, args); - let sig = tcx.erase_late_bound_regions(sig); + let sig = tcx.instantiate_bound_regions_with_erased(sig); let span = tcx.def_span(def_id); let source_info = SourceInfo::outermost(span); @@ -416,7 +416,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { // otherwise going to be TySelf and we can't index // or access fields of a Place of type TySelf. let sig = tcx.fn_sig(def_id).instantiate(tcx, &[self_ty.into()]); - let sig = tcx.erase_late_bound_regions(sig); + let sig = tcx.instantiate_bound_regions_with_erased(sig); let span = tcx.def_span(def_id); CloneShimBuilder { @@ -654,7 +654,7 @@ fn build_call_shim<'tcx>( // to substitute into the signature of the shim. It is not necessary for users of this // MIR body to perform further substitutions (see `InstanceDef::has_polymorphic_mir_body`). let (sig_args, untuple_args) = if let ty::InstanceDef::FnPtrShim(_, ty) = instance { - let sig = tcx.erase_late_bound_regions(ty.fn_sig(tcx)); + let sig = tcx.instantiate_bound_regions_with_erased(ty.fn_sig(tcx)); let untuple_args = sig.inputs(); @@ -668,7 +668,7 @@ fn build_call_shim<'tcx>( let def_id = instance.def_id(); let sig = tcx.fn_sig(def_id); - let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig)); + let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig)); assert_eq!(sig_args.is_some(), !instance.has_polymorphic_mir_body()); let mut sig = if let Some(sig_args) = sig_args { diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 1f59c790b4e..3a6e1ef3488 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -40,7 +40,8 @@ impl SsaLocals { let dominators = body.basic_blocks.dominators(); let direct_uses = IndexVec::from_elem(0, &body.local_decls); - let mut visitor = SsaVisitor { assignments, assignment_order, dominators, direct_uses }; + let mut visitor = + SsaVisitor { body, assignments, assignment_order, dominators, direct_uses }; for local in body.args_iter() { visitor.assignments[local] = Set1::One(DefLocation::Argument); @@ -110,7 +111,7 @@ impl SsaLocals { body: &'a Body<'tcx>, ) -> impl Iterator<Item = (Local, &'a Rvalue<'tcx>, Location)> + 'a { self.assignment_order.iter().filter_map(|&local| { - if let Set1::One(DefLocation::Body(loc)) = self.assignments[local] { + if let Set1::One(DefLocation::Assignment(loc)) = self.assignments[local] { let stmt = body.stmt_at(loc).left()?; // `loc` must point to a direct assignment to `local`. let Some((target, rvalue)) = stmt.kind.as_assign() else { bug!() }; @@ -134,21 +135,21 @@ impl SsaLocals { AssignedValue::Arg, Location { block: START_BLOCK, statement_index: 0 }, ), - Set1::One(DefLocation::Body(loc)) => { + Set1::One(DefLocation::Assignment(loc)) => { let bb = &mut basic_blocks[loc.block]; - let value = if loc.statement_index < bb.statements.len() { - // `loc` must point to a direct assignment to `local`. - let stmt = &mut bb.statements[loc.statement_index]; - let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else { - bug!() - }; - assert_eq!(target.as_local(), Some(local)); - AssignedValue::Rvalue(rvalue) - } else { - let term = bb.terminator_mut(); - AssignedValue::Terminator(&mut term.kind) + // `loc` must point to a direct assignment to `local`. + let stmt = &mut bb.statements[loc.statement_index]; + let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else { + bug!() }; - f(local, value, loc) + assert_eq!(target.as_local(), Some(local)); + f(local, AssignedValue::Rvalue(rvalue), loc) + } + Set1::One(DefLocation::CallReturn { call, .. }) => { + let bb = &mut basic_blocks[call]; + let loc = Location { block: call, statement_index: bb.statements.len() }; + let term = bb.terminator_mut(); + f(local, AssignedValue::Terminator(&mut term.kind), loc) } _ => {} } @@ -201,14 +202,15 @@ impl SsaLocals { } } -struct SsaVisitor<'a> { +struct SsaVisitor<'tcx, 'a> { + body: &'a Body<'tcx>, dominators: &'a Dominators<BasicBlock>, assignments: IndexVec<Local, Set1<DefLocation>>, assignment_order: Vec<Local>, direct_uses: IndexVec<Local, u32>, } -impl SsaVisitor<'_> { +impl SsaVisitor<'_, '_> { fn check_dominates(&mut self, local: Local, loc: Location) { let set = &mut self.assignments[local]; let assign_dominates = match *set { @@ -224,7 +226,7 @@ impl SsaVisitor<'_> { } } -impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> { +impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> { fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) { match ctxt { PlaceContext::MutatingUse(MutatingUseContext::Projection) @@ -250,9 +252,18 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> { fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, loc: Location) { let location = match ctxt { - PlaceContext::MutatingUse( - MutatingUseContext::Store | MutatingUseContext::Call | MutatingUseContext::Yield, - ) => Some(DefLocation::Body(loc)), + PlaceContext::MutatingUse(MutatingUseContext::Store) => { + Some(DefLocation::Assignment(loc)) + } + PlaceContext::MutatingUse(MutatingUseContext::Call) => { + let call = loc.block; + let TerminatorKind::Call { target, .. } = + self.body.basic_blocks[call].terminator().kind + else { + bug!() + }; + Some(DefLocation::CallReturn { call, target }) + } _ => None, }; if let Some(location) = location @@ -359,7 +370,7 @@ impl StorageLiveLocals { for (statement_index, statement) in bbdata.statements.iter().enumerate() { if let StatementKind::StorageLive(local) = statement.kind { storage_live[local] - .insert(DefLocation::Body(Location { block, statement_index })); + .insert(DefLocation::Assignment(Location { block, statement_index })); } } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8ab1ec298a1..99e66fddc70 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2278,9 +2278,8 @@ pub(crate) enum InvalidMutInPattern { #[note(parse_note_mut_pattern_usage)] NonIdent { #[primary_span] - #[suggestion(code = "{pat}", applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable")] span: Span, - pat: String, }, } diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 104de47b97d..bad7c19cc27 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -249,7 +249,7 @@ impl<'a> Parser<'a> { /// The delimiters or `=` are still put into the resulting token stream. pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> { let item = match &self.token.kind { - token::Interpolated(nt) => match &**nt { + token::Interpolated(nt) => match &nt.0 { Nonterminal::NtMeta(item) => Some(item.clone().into_inner()), _ => None, }, @@ -369,7 +369,7 @@ impl<'a> Parser<'a> { /// ``` pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { let nt_meta = match &self.token.kind { - token::Interpolated(nt) => match &**nt { + token::Interpolated(nt) => match &nt.0 { token::NtMeta(e) => Some(e.clone()), _ => None, }, diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 2a8eb6edd23..262e7b899ad 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -24,11 +24,12 @@ use crate::parser; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind}; +use rustc_ast::tokenstream::AttrTokenTree; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block, - BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind, - Path, PathSegment, QSelf, Ty, TyKind, + BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat, + PatKind, Path, PathSegment, QSelf, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -506,7 +507,9 @@ impl<'a> Parser<'a> { if expected.contains(&TokenType::Token(token::Semi)) { // If the user is trying to write a ternary expression, recover it and // return an Err to prevent a cascade of irrelevant diagnostics - if self.prev_token == token::Question && let Err(e) = self.maybe_recover_from_ternary_operator() { + if self.prev_token == token::Question + && let Err(e) = self.maybe_recover_from_ternary_operator() + { return Err(e); } @@ -2250,6 +2253,59 @@ impl<'a> Parser<'a> { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err.span_label(span, "expected expression"); + + // Walk the chain of macro expansions for the current token to point at how the original + // code was interpreted. This helps the user realize when a macro argument of one type is + // later reinterpreted as a different type, like `$x:expr` being reinterpreted as `$x:pat` + // in a subsequent macro invocation (#71039). + let mut tok = self.token.clone(); + let mut labels = vec![]; + while let TokenKind::Interpolated(node) = &tok.kind { + let tokens = node.0.tokens(); + labels.push(node.clone()); + if let Some(tokens) = tokens + && let tokens = tokens.to_attr_token_stream() + && let tokens = tokens.0.deref() + && let [AttrTokenTree::Token(token, _)] = &tokens[..] + { + tok = token.clone(); + } else { + break; + } + } + let mut iter = labels.into_iter().peekable(); + let mut show_link = false; + while let Some(node) = iter.next() { + let descr = node.0.descr(); + if let Some(next) = iter.peek() { + let next_descr = next.0.descr(); + if next_descr != descr { + err.span_label(next.1, format!("this macro fragment matcher is {next_descr}")); + err.span_label(node.1, format!("this macro fragment matcher is {descr}")); + err.span_label( + next.0.use_span(), + format!("this is expected to be {next_descr}"), + ); + err.span_label( + node.0.use_span(), + format!( + "this is interpreted as {}, but it is expected to be {}", + next_descr, descr, + ), + ); + show_link = true; + } else { + err.span_label(node.1, ""); + } + } + } + if show_link { + err.note( + "when forwarding a matched fragment to another macro-by-example, matchers in the \ + second macro will see an opaque AST of the fragment type, not the underlying \ + tokens", + ); + } err } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 235b28b6e26..d8e99d34016 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -46,7 +46,7 @@ use thin_vec::{thin_vec, ThinVec}; macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = &$p.token.kind { - match &**nt { + match &nt.0 { token::NtExpr(e) | token::NtLiteral(e) => { let e = e.clone(); $p.bump(); @@ -1952,7 +1952,7 @@ impl<'a> Parser<'a> { mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { if let token::Interpolated(nt) = &self.token.kind - && let token::NtExpr(e) | token::NtLiteral(e) = &**nt + && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0 && matches!(e.kind, ExprKind::Err) { let mut err = errors::InvalidInterpolatedExpression { span: self.token.span } @@ -2904,15 +2904,16 @@ impl<'a> Parser<'a> { "=>", Applicability::MachineApplicable, ); - err.emit(); - this.bump(); - } else if matches!( - (&this.prev_token.kind, &this.token.kind), - (token::DotDotEq, token::Gt) - ) { - // `error_inclusive_range_match_arrow` handles cases like `0..=> {}`, - // so we suppress the error here - err.delay_as_bug(); + if matches!( + (&this.prev_token.kind, &this.token.kind), + (token::DotDotEq, token::Gt) + ) { + // `error_inclusive_range_match_arrow` handles cases like `0..=> {}`, + // so we suppress the error here + err.delay_as_bug(); + } else { + err.emit(); + } this.bump(); } else { return Err(err); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 801860c2123..d124ea571ab 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -123,7 +123,7 @@ impl<'a> Parser<'a> { // Don't use `maybe_whole` so that we have precise control // over when we bump the parser if let token::Interpolated(nt) = &self.token.kind - && let token::NtItem(item) = &**nt + && let token::NtItem(item) = &nt.0 { let mut item = item.clone(); self.bump(); @@ -2750,7 +2750,7 @@ impl<'a> Parser<'a> { fn is_named_param(&self) -> bool { let offset = match &self.token.kind { - token::Interpolated(nt) => match **nt { + token::Interpolated(nt) => match &nt.0 { token::NtPat(..) => return self.look_ahead(1, |t| t == &token::Colon), _ => 0, }, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 76f3f21a516..9bd436f01ac 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -93,7 +93,7 @@ pub enum TrailingToken { macro_rules! maybe_whole { ($p:expr, $constructor:ident, |$x:ident| $e:expr) => { if let token::Interpolated(nt) = &$p.token.kind { - if let token::$constructor(x) = &**nt { + if let token::$constructor(x) = &nt.0 { let $x = x.clone(); $p.bump(); return Ok($e); @@ -107,15 +107,15 @@ macro_rules! maybe_whole { macro_rules! maybe_recover_from_interpolated_ty_qpath { ($self: expr, $allow_qpath_recovery: expr) => { if $allow_qpath_recovery - && $self.may_recover() - && $self.look_ahead(1, |t| t == &token::ModSep) - && let token::Interpolated(nt) = &$self.token.kind - && let token::NtTy(ty) = &**nt - { - let ty = ty.clone(); - $self.bump(); - return $self.maybe_recover_from_bad_qpath_stage_2($self.prev_token.span, ty); - } + && $self.may_recover() + && $self.look_ahead(1, |t| t == &token::ModSep) + && let token::Interpolated(nt) = &$self.token.kind + && let token::NtTy(ty) = &nt.0 + { + let ty = ty.clone(); + $self.bump(); + return $self.maybe_recover_from_bad_qpath_stage_2($self.prev_token.span, ty); + } }; } @@ -367,12 +367,14 @@ impl TokenDescription { pub(super) fn token_descr(token: &Token) -> String { let name = pprust::token_to_string(token).to_string(); - let kind = TokenDescription::from_token(token).map(|kind| match kind { - TokenDescription::ReservedIdentifier => "reserved identifier", - TokenDescription::Keyword => "keyword", - TokenDescription::ReservedKeyword => "reserved keyword", - TokenDescription::DocComment => "doc comment", - }); + let kind = match (TokenDescription::from_token(token), &token.kind) { + (Some(TokenDescription::ReservedIdentifier), _) => Some("reserved identifier"), + (Some(TokenDescription::Keyword), _) => Some("keyword"), + (Some(TokenDescription::ReservedKeyword), _) => Some("reserved keyword"), + (Some(TokenDescription::DocComment), _) => Some("doc comment"), + (None, TokenKind::Interpolated(node)) => Some(node.0.descr()), + (None, _) => None, + }; if let Some(kind) = kind { format!("{kind} `{name}`") } else { format!("`{name}`") } } @@ -662,7 +664,7 @@ impl<'a> Parser<'a> { fn check_inline_const(&self, dist: usize) -> bool { self.is_keyword_ahead(dist, &[kw::Const]) && self.look_ahead(dist + 1, |t| match &t.kind { - token::Interpolated(nt) => matches!(**nt, token::NtBlock(..)), + token::Interpolated(nt) => matches!(&nt.0, token::NtBlock(..)), token::OpenDelim(Delimiter::Brace) => true, _ => false, }) @@ -830,8 +832,8 @@ impl<'a> Parser<'a> { // https://github.com/rust-lang/rust/issues/72373 if self.prev_token.is_ident() && self.token.kind == token::DotDot { let msg = format!( - "if you meant to bind the contents of \ - the rest of the array pattern into `{}`, use `@`", + "if you meant to bind the contents of the rest of the array \ + pattern into `{}`, use `@`", pprust::token_to_string(&self.prev_token) ); expect_err diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 025b0615a7e..06cc39fbb5a 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -50,12 +50,12 @@ impl<'a> Parser<'a> { NonterminalKind::Literal => token.can_begin_literal_maybe_minus(), NonterminalKind::Vis => match token.kind { // The follow-set of :vis + "priv" keyword + interpolated - token::Comma | token::Ident(..) | token::Interpolated(..) => true, + token::Comma | token::Ident(..) | token::Interpolated(_) => true, _ => token.can_begin_type(), }, NonterminalKind::Block => match &token.kind { token::OpenDelim(Delimiter::Brace) => true, - token::Interpolated(nt) => match **nt { + token::Interpolated(nt) => match &nt.0 { NtBlock(_) | NtLifetime(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true, NtItem(_) | NtPat(_) | NtTy(_) | NtIdent(..) | NtMeta(_) | NtPath(_) | NtVis(_) => false, @@ -64,7 +64,7 @@ impl<'a> Parser<'a> { }, NonterminalKind::Path | NonterminalKind::Meta => match &token.kind { token::ModSep | token::Ident(..) => true, - token::Interpolated(nt) => may_be_ident(nt), + token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, }, NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { @@ -75,7 +75,7 @@ impl<'a> Parser<'a> { token::BinOp(token::And) | // reference token::BinOp(token::Minus) | // negative literal token::AndAnd | // double reference - token::Literal(..) | // literal + token::Literal(_) | // literal token::DotDot | // range pattern (future compat) token::DotDotDot | // range pattern (future compat) token::ModSep | // path @@ -83,14 +83,14 @@ impl<'a> Parser<'a> { token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), - token::Interpolated(nt) => may_be_ident(nt), + token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, } } NonterminalKind::Lifetime => match &token.kind { token::Lifetime(_) => true, token::Interpolated(nt) => { - matches!(**nt, NtLifetime(_)) + matches!(&nt.0, NtLifetime(_)) } _ => false, }, @@ -191,7 +191,7 @@ impl<'a> Parser<'a> { panic!( "Missing tokens for nt {:?} at {:?}: {:?}", nt, - nt.span(), + nt.use_span(), pprust::nonterminal_to_string(&nt) ); } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 0a4c7c17d06..5cec3f5762d 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -592,7 +592,7 @@ impl<'a> Parser<'a> { // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`. if let token::Interpolated(nt) = &self.token.kind { - if let token::NtPat(_) = **nt { + if let token::NtPat(..) = &nt.0 { self.expected_ident_found_err().emit(); } } @@ -638,13 +638,13 @@ impl<'a> Parser<'a> { /// Error on `mut $pat` where `$pat` is not an ident. fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) { - let span = lo.to(pat.span); - let pat = pprust::pat_to_string(&pat); - self.sess.emit_err(if changed_any_binding { - InvalidMutInPattern::NestedIdent { span, pat } + InvalidMutInPattern::NestedIdent { + span: lo.to(pat.span), + pat: pprust::pat_to_string(&pat), + } } else { - InvalidMutInPattern::NonIdent { span, pat } + InvalidMutInPattern::NonIdent { span: lo.until(pat.span) } }); } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 8626dbe40af..0f4ba9617c6 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -185,7 +185,7 @@ impl<'a> Parser<'a> { }); if let token::Interpolated(nt) = &self.token.kind { - if let token::NtTy(ty) = &**nt { + if let token::NtTy(ty) = &nt.0 { if let ast::TyKind::Path(None, path) = &ty.kind { let path = path.clone(); self.bump(); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index aa939a71d63..d1c5ca4b93c 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -53,7 +53,7 @@ impl<'a> Parser<'a> { // Don't use `maybe_whole` so that we have precise control // over when we bump the parser if let token::Interpolated(nt) = &self.token.kind - && let token::NtStmt(stmt) = &**nt + && let token::NtStmt(stmt) = &nt.0 { let mut stmt = stmt.clone(); self.bump(); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index be2cbaf3020..dc0f1396523 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -135,7 +135,7 @@ impl<'a> Parser<'a> { ) } - /// Parse a type suitable for a field defintion. + /// Parse a type suitable for a field definition. /// The difference from `parse_ty` is that this version /// allows anonymous structs and unions. pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P<Ty>> { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 7b6153eea09..e886db3da29 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -9,13 +9,10 @@ html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] -// We want to be able to build this crate with a stable compiler, so no -// `#![feature]` attributes should be added. +// WARNING: We want to be able to build this crate with a stable compiler, +// so no `#![feature]` attributes should be added! use rustc_lexer::unescape; pub use Alignment::*; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 2e8c58b0241..6b2b842543a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -314,7 +314,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { // for the `#[expect]` case. // // Note that an item can and will be duplicated on the worklist with different - // `ComesFromAllowExpect`, particulary if it was added from the + // `ComesFromAllowExpect`, particularly if it was added from the // `effective_visibilities` query or from the `#[allow]`/`#[expect]` checks, // this "duplication" is essential as otherwise a function with `#[expect]` // called from a `pub fn` may be falsely reported as not live, falsely diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 946a9e68da6..9eb1df0b2e2 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -6,9 +6,9 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(map_try_insert)] diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 6a2498f3f99..018d42793b2 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -196,7 +196,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if let Some((depr, span)) = &depr && depr.is_since_rustc_version() && stab.is_none() { + if let Some((depr, span)) = &depr + && depr.is_since_rustc_version() + && stab.is_none() + { self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span }); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 4bb7e65747f..a14c2916392 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,7 +1,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(associated_type_defaults)] #![feature(rustc_private)] #![feature(try_blocks)] @@ -1756,7 +1756,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> { // fn from(_: Priv) -> Pub {...} // } // - // lints shouldn't be emmited even if `from` effective visibility + // lints shouldn't be emitted even if `from` effective visibility // is larger than `Priv` nominal visibility and if `Priv` can leak // in some scenarios due to type inference. let impl_ev = EffectiveVisibility::of_impl::<false>( diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index a1465dabed6..59812efc324 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -1,8 +1,8 @@ //! Support for serializing the dep-graph and reloading it. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] // this shouldn't be necessary, but the check for `&mut _` is too naive and denies returning a function pointer that takes a mut ref #![feature(const_mut_refs)] #![feature(const_refs_to_cell)] diff --git a/compiler/rustc_query_system/src/dep_graph/edges.rs b/compiler/rustc_query_system/src/dep_graph/edges.rs index 6ba3924f65e..400f128d583 100644 --- a/compiler/rustc_query_system/src/dep_graph/edges.rs +++ b/compiler/rustc_query_system/src/dep_graph/edges.rs @@ -5,7 +5,7 @@ use std::iter::Extend; use std::ops::Deref; #[derive(Default, Debug)] -pub struct EdgesVec { +pub(crate) struct EdgesVec { max: u32, edges: SmallVec<[DepNodeIndex; EdgesVec::INLINE_CAPACITY]>, } @@ -18,21 +18,21 @@ impl Hash for EdgesVec { } impl EdgesVec { - pub const INLINE_CAPACITY: usize = 8; + pub(crate) const INLINE_CAPACITY: usize = 8; #[inline] - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self::default() } #[inline] - pub fn push(&mut self, edge: DepNodeIndex) { + pub(crate) fn push(&mut self, edge: DepNodeIndex) { self.max = self.max.max(edge.as_u32()); self.edges.push(edge); } #[inline] - pub fn max_index(&self) -> u32 { + pub(crate) fn max_index(&self) -> u32 { self.max } } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 6cace01955e..831062b1678 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -18,7 +18,7 @@ use std::sync::atomic::Ordering::Relaxed; use super::query::DepGraphQuery; use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex}; use super::{DepContext, DepKind, DepNode, Deps, HasDepContext, WorkProductId}; -use crate::dep_graph::EdgesVec; +use crate::dep_graph::edges::EdgesVec; use crate::ich::StableHashingContext; use crate::query::{QueryContext, QuerySideEffects}; @@ -41,8 +41,7 @@ rustc_index::newtype_index! { } impl DepNodeIndex { - pub const INVALID: DepNodeIndex = DepNodeIndex::MAX; - pub const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::from_u32(0); + const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::from_u32(0); pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1); } @@ -53,20 +52,20 @@ impl From<DepNodeIndex> for QueryInvocationId { } } -pub struct MarkFrame<'a> { +pub(crate) struct MarkFrame<'a> { index: SerializedDepNodeIndex, parent: Option<&'a MarkFrame<'a>>, } #[derive(PartialEq)] -pub enum DepNodeColor { +enum DepNodeColor { Red, Green(DepNodeIndex), } impl DepNodeColor { #[inline] - pub fn is_green(self) -> bool { + fn is_green(self) -> bool { match self { DepNodeColor::Red => false, DepNodeColor::Green(_) => true, @@ -74,7 +73,7 @@ impl DepNodeColor { } } -pub struct DepGraphData<D: Deps> { +pub(crate) struct DepGraphData<D: Deps> { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into @@ -185,7 +184,7 @@ impl<D: Deps> DepGraph<D> { } #[inline] - pub fn data(&self) -> Option<&DepGraphData<D>> { + pub(crate) fn data(&self) -> Option<&DepGraphData<D>> { self.data.as_deref() } @@ -333,7 +332,7 @@ impl<D: Deps> DepGraphData<D> { /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html #[inline(always)] - pub fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>( + pub(crate) fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>( &self, key: DepNode, cx: Ctxt, @@ -398,7 +397,7 @@ impl<D: Deps> DepGraphData<D> { /// Executes something within an "anonymous" task, that is, a task the /// `DepNode` of which is determined by the list of inputs it read from. - pub fn with_anon_task<Tcx: DepContext<Deps = D>, OP, R>( + pub(crate) fn with_anon_task<Tcx: DepContext<Deps = D>, OP, R>( &self, cx: Tcx, dep_kind: DepKind, @@ -618,7 +617,7 @@ impl<D: Deps> DepGraph<D> { impl<D: Deps> DepGraphData<D> { #[inline] - pub fn dep_node_index_of_opt(&self, dep_node: &DepNode) -> Option<DepNodeIndex> { + fn dep_node_index_of_opt(&self, dep_node: &DepNode) -> Option<DepNodeIndex> { if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { self.current.prev_index_to_index.lock()[prev_index] } else { @@ -627,7 +626,7 @@ impl<D: Deps> DepGraphData<D> { } #[inline] - pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { + fn dep_node_exists(&self, dep_node: &DepNode) -> bool { self.dep_node_index_of_opt(dep_node).is_some() } @@ -643,21 +642,21 @@ impl<D: Deps> DepGraphData<D> { /// Returns true if the given node has been marked as green during the /// current compilation session. Used in various assertions #[inline] - pub fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { + pub(crate) fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool { self.colors.get(prev_index).is_some_and(|c| c.is_green()) } #[inline] - pub fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint { + pub(crate) fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint { self.previous.fingerprint_by_index(prev_index) } #[inline] - pub fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> DepNode { + pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> DepNode { self.previous.index_to_node(prev_index) } - pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { + pub(crate) fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { self.debug_loaded_from_disk.lock().insert(dep_node); } } @@ -684,8 +683,9 @@ impl<D: Deps> DepGraph<D> { self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) } + #[cfg(debug_assertions)] #[inline(always)] - pub fn register_dep_node_debug_str<F>(&self, dep_node: DepNode, debug_str_gen: F) + pub(crate) fn register_dep_node_debug_str<F>(&self, dep_node: DepNode, debug_str_gen: F) where F: FnOnce() -> String, { @@ -725,7 +725,7 @@ impl<D: Deps> DepGraphData<D> { /// A node will have an index, when it's already been marked green, or when we can mark it /// green. This function will mark the current task as a reader of the specified node, when /// a node index can be found for that node. - pub fn try_mark_green<Qcx: QueryContext<Deps = D>>( + pub(crate) fn try_mark_green<Qcx: QueryContext<Deps = D>>( &self, qcx: Qcx, dep_node: &DepNode, diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 624ae680a8f..feb69ecd078 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -6,11 +6,8 @@ mod query; mod serialized; pub use dep_node::{DepKind, DepKindStruct, DepNode, DepNodeParams, WorkProductId}; -pub use edges::EdgesVec; -pub use graph::{ - hash_result, DepGraph, DepGraphData, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, - WorkProduct, WorkProductMap, -}; +pub(crate) use graph::DepGraphData; +pub use graph::{hash_result, DepGraph, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap}; pub use query::DepGraphQuery; pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index fcf46be6e6f..e97ef807204 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -37,7 +37,7 @@ use super::query::DepGraphQuery; use super::{DepKind, DepNode, DepNodeIndex, Deps}; -use crate::dep_graph::EdgesVec; +use crate::dep_graph::edges::EdgesVec; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::PackedFingerprint; use rustc_data_structures::fx::FxHashMap; diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 5593a15412f..eec0433ae68 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -28,7 +28,7 @@ pub struct StableHashingContext<'a> { // `CachingSourceMapView`, so we initialize it lazily. raw_source_map: &'a SourceMap, caching_source_map: Option<CachingSourceMapView<'a>>, - pub(super) hashing_controls: HashingControls, + hashing_controls: HashingControls, } /// The `BodyResolver` allows mapping a `BodyId` to the corresponding `hir::Body`. diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 1944ac443ea..2ed420f3564 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -2,9 +2,7 @@ #![feature(core_intrinsics)] #![feature(hash_raw_entry)] #![feature(min_specialization)] -#![feature(extern_types)] #![feature(let_chains)] -#![feature(inline_const)] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 93db6cfc463..28e6fe9b4b7 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2028,7 +2028,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }, ) }); - (format!("use of undeclared crate or module `{ident}`"), suggestion) + if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( + ident, + ScopeSet::All(ValueNS), + parent_scope, + None, + false, + ignore_binding, + ) { + let descr = binding.res().descr(); + (format!("{descr} `{ident}` is not a crate or module"), suggestion) + } else { + (format!("use of undeclared crate or module `{ident}`"), suggestion) + } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3be962dab90..9b7897d69e7 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -603,6 +603,8 @@ struct DiagnosticMetadata<'ast> { /// Only used for better errors on `let <pat>: <expr, not type>;`. current_let_binding: Option<(Span, Option<Span>, Option<Span>)>, + current_pat: Option<&'ast Pat>, + /// Used to detect possible `if let` written without `let` and to provide structured suggestion. in_if_condition: Option<&'ast Expr>, @@ -703,6 +705,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, fn visit_expr(&mut self, expr: &'ast Expr) { self.resolve_expr(expr, None); } + fn visit_pat(&mut self, p: &'ast Pat) { + let prev = self.diagnostic_metadata.current_pat; + self.diagnostic_metadata.current_pat = Some(p); + visit::walk_pat(self, p); + self.diagnostic_metadata.current_pat = prev; + } fn visit_local(&mut self, local: &'ast Local) { let local_spans = match local.pat.kind { // We check for this to avoid tuple struct fields. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index fd5d6fabf02..6aecd3610c6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -431,6 +431,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { code, ); + self.suggest_at_operator_in_slice_pat_with_range(&mut err, path); self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); if let Some((span, label)) = base_error.span_label { @@ -1063,6 +1064,32 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { true } + fn suggest_at_operator_in_slice_pat_with_range( + &mut self, + err: &mut Diagnostic, + path: &[Segment], + ) { + if let Some(pat) = self.diagnostic_metadata.current_pat + && let ast::PatKind::Range(Some(start), None, range) = &pat.kind + && let ExprKind::Path(None, range_path) = &start.kind + && let [segment] = &range_path.segments[..] + && let [s] = path + && segment.ident == s.ident + { + // We've encountered `[first, rest..]` where the user might have meant + // `[first, rest @ ..]` (#88404). + err.span_suggestion_verbose( + segment.ident.span.between(range.span), + format!( + "if you meant to collect the rest of the slice in `{}`, use the at operator", + segment.ident, + ), + " @ ", + Applicability::MaybeIncorrect, + ); + } + } + fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diagnostic, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 501747df5c9..23e8ebc49b0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -7,8 +7,8 @@ //! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(extract_if)] diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index cfa54072eb9..35275821453 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -5,9 +5,9 @@ html_playground_url = "https://play.rust-lang.org/", test(attr(allow(unused_variables), deny(warnings))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![allow(internal_features)] +#![feature(rustdoc_internals)] #![feature(allocator_api)] #![feature(associated_type_bounds)] #![feature(const_option)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index add40b83d21..d4f9122e7e3 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2926,12 +2926,13 @@ fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) -> "thir-tree" => ThirTree, "thir-flat" => ThirFlat, "mir" => Mir, + "stable-mir" => StableMir, "mir-cfg" => MirCFG, name => handler.early_error(format!( "argument to `unpretty` must be one of `normal`, `identified`, \ `expanded`, `expanded,identified`, `expanded,hygiene`, \ `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ - `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir` or \ + `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \ `mir-cfg`; got {name}" )), }; @@ -3106,6 +3107,8 @@ pub enum PpMode { Mir, /// `-Zunpretty=mir-cfg` MirCFG, + /// `-Zunpretty=stable-mir` + StableMir, } impl PpMode { @@ -3122,7 +3125,8 @@ impl PpMode { | ThirTree | ThirFlat | Mir - | MirCFG => true, + | MirCFG + | StableMir => true, } } pub fn needs_hir(&self) -> bool { @@ -3130,13 +3134,13 @@ impl PpMode { match *self { Source(_) | AstTree | AstTreeExpanded => false, - Hir(_) | HirTree | ThirTree | ThirFlat | Mir | MirCFG => true, + Hir(_) | HirTree | ThirTree | ThirFlat | Mir | MirCFG | StableMir => true, } } pub fn needs_analysis(&self) -> bool { use PpMode::*; - matches!(*self, Hir(PpHirMode::Typed) | Mir | MirCFG | ThirTree | ThirFlat) + matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat) } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 76d69646f1d..b824eb51ef7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -429,6 +429,7 @@ mod desc { pub const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `unsplit-debuginfo`, `split-debuginfo`, `split-debuginfo-path`, `object`, `all`"; pub const parse_inlining_threshold: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; + pub const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; } mod parse { @@ -1331,6 +1332,33 @@ mod parse { } true } + + pub(crate) fn parse_llvm_module_flag( + slot: &mut Vec<(String, u32, String)>, + v: Option<&str>, + ) -> bool { + let elements = v.unwrap_or_default().split(':').collect::<Vec<_>>(); + let [key, md_type, value, behavior] = elements.as_slice() else { + return false; + }; + if *md_type != "u32" { + // Currently we only support u32 metadata flags, but require the + // type for forward-compatibility. + return false; + } + let Ok(value) = value.parse::<u32>() else { + return false; + }; + let behavior = behavior.to_lowercase(); + let all_behaviors = + ["error", "warning", "require", "override", "append", "appendunique", "max", "min"]; + if !all_behaviors.contains(&behavior.as_str()) { + return false; + } + + slot.push((key.to_string(), value, behavior)); + true + } } options! { @@ -1621,8 +1649,6 @@ options! { `=skip-entry` `=skip-exit` Multiple options can be combined with commas."), - keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], - "keep hygiene data after analysis (default: no)"), layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED], "seed layout randomization"), link_directives: bool = (true, parse_bool, [TRACKED], @@ -1631,6 +1657,8 @@ options! { "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link` (default: no)"), + llvm_module_flag: Vec<(String, u32, String)> = (Vec::new(), parse_llvm_module_flag, [TRACKED], + "a list of module flags to pass to LLVM (space separated)"), llvm_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED], "a list LLVM plugins to enable (space separated)"), llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], @@ -1710,8 +1738,6 @@ options! { "panic strategy for panics in drops"), parse_only: bool = (false, parse_bool, [UNTRACKED], "parse only; do not compile, assemble, or link (default: no)"), - perf_stats: bool = (false, parse_bool, [UNTRACKED], - "print some performance-related statistics (default: no)"), plt: Option<bool> = (None, parse_opt_bool, [TRACKED], "whether to use the PLT when calling into shared libraries; only has effect for PIC code on systems with ELF binaries diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 80a549b30f7..38e09f47eac 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -15,9 +15,9 @@ pub use rustc_ast::Attribute; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::jobserver::{self, Client}; -use rustc_data_structures::profiling::{duration_to_secs_str, SelfProfiler, SelfProfilerRef}; +use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{ - AtomicU64, AtomicUsize, Lock, Lrc, OneThread, Ordering, Ordering::SeqCst, + AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread, Ordering::SeqCst, }; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{DynEmitter, EmitterWriter, HumanReadableErrorType}; @@ -39,6 +39,7 @@ use rustc_target::spec::{ DebuginfoKind, SanitizerSet, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel, }; +use std::any::Any; use std::cell::{self, RefCell}; use std::env; use std::fmt; @@ -46,7 +47,6 @@ use std::ops::{Div, Mul}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::{atomic::AtomicBool, Arc}; -use std::time::Duration; pub struct OptimizationFuel { /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. @@ -157,9 +157,6 @@ pub struct Session { /// Used by `-Z self-profile`. pub prof: SelfProfilerRef, - /// Some measurements that are being gathered during compilation. - pub perf_stats: PerfStats, - /// Data about code being compiled, gathered during compilation. pub code_stats: CodeStats, @@ -173,6 +170,15 @@ pub struct Session { /// false positives about a job server in our environment. pub jobserver: Client, + /// This only ever stores a `LintStore` but we don't want a dependency on that type here. + /// + /// FIXME(Centril): consider `dyn LintStoreMarker` once + /// we can upcast to `Any` for some additional type safety. + pub lint_store: Option<Lrc<dyn Any + DynSync + DynSend>>, + + /// Should be set if any lints are registered in `lint_store`. + pub registered_lints: bool, + /// Cap lint level specified by a driver specifically. pub driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, @@ -215,17 +221,6 @@ pub struct Session { pub expanded_args: Vec<String>, } -pub struct PerfStats { - /// The accumulated time spent on computing symbol hashes. - pub symbol_hash_time: Lock<Duration>, - /// Total number of values canonicalized queries constructed. - pub queries_canonicalized: AtomicUsize, - /// Number of times this query is invoked. - pub normalize_generic_arg_after_erasing_regions: AtomicUsize, - /// Number of times this query is invoked. - pub normalize_projection_ty: AtomicUsize, -} - #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum MetadataKind { None, @@ -883,25 +878,6 @@ impl Session { self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) } - pub fn print_perf_stats(&self) { - eprintln!( - "Total time spent computing symbol hashes: {}", - duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) - ); - eprintln!( - "Total queries canonicalized: {}", - self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_generic_arg_after_erasing_regions: {}", - self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_projection_ty: {}", - self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) - ); - } - /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. /// This expends fuel if applicable, and records fuel if applicable. pub fn consider_optimizing( @@ -1515,16 +1491,12 @@ pub fn build_session( io, incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), prof, - perf_stats: PerfStats { - symbol_hash_time: Lock::new(Duration::from_secs(0)), - queries_canonicalized: AtomicUsize::new(0), - normalize_generic_arg_after_erasing_regions: AtomicUsize::new(0), - normalize_projection_ty: AtomicUsize::new(0), - }, code_stats: Default::default(), optimization_fuel, print_fuel, jobserver: jobserver::client(), + lint_store: None, + registered_lints: false, driver_lint_caps, ctfe_backtrace, miri_unleashed_features: Lock::new(Default::default()), diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_session/src/version.rs index 1ad8620bfba..c0c088bcef7 100644 --- a/compiler/rustc_session/src/version.rs +++ b/compiler/rustc_session/src/version.rs @@ -9,7 +9,7 @@ pub struct RustcVersion { } impl RustcVersion { - pub const CURRENT: Self = current_rustc_version!(env!("CFG_RELEASE")); + pub const CURRENT: Self = current_rustc_version!(); } impl Display for RustcVersion { diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index dcf6b904077..ddd5ea5510a 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -10,9 +10,9 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![allow(rustc::usage_of_ty_tykind)] pub mod rustc_internal; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 7cfdbbbf703..5bb3c1a0d4c 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -6,11 +6,23 @@ // Prefer importing stable_mir over internal rustc constructs to make this file more readable. use crate::rustc_smir::Tables; use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy}; -use stable_mir::ty::{Const, GenericArgKind, GenericArgs, Region, Ty}; -use stable_mir::DefId; +use rustc_span::Symbol; +use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; +use stable_mir::ty::{ + Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, GenericArgKind, + GenericArgs, Region, TraitRef, Ty, +}; +use stable_mir::{AllocId, CrateItem, DefId}; use super::RustcInternal; +impl<'tcx> RustcInternal<'tcx> for CrateItem { + type T = rustc_span::def_id::DefId; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.0.internal(tables) + } +} + impl<'tcx> RustcInternal<'tcx> for DefId { type T = rustc_span::def_id::DefId; fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { @@ -38,8 +50,9 @@ impl<'tcx> RustcInternal<'tcx> for GenericArgKind { impl<'tcx> RustcInternal<'tcx> for Region { type T = rustc_ty::Region<'tcx>; - fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { - todo!() + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + // Cannot recover region. Use erased instead. + tables.tcx.lifetimes.re_erased } } @@ -65,3 +78,118 @@ impl<'tcx> RustcInternal<'tcx> for Const { tables.constants[self.id] } } + +impl<'tcx> RustcInternal<'tcx> for MonoItem { + type T = rustc_middle::mir::mono::MonoItem<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_middle::mir::mono as rustc_mono; + match self { + MonoItem::Fn(instance) => rustc_mono::MonoItem::Fn(instance.internal(tables)), + MonoItem::Static(def) => rustc_mono::MonoItem::Static(def.internal(tables)), + MonoItem::GlobalAsm(_) => { + unimplemented!() + } + } + } +} + +impl<'tcx> RustcInternal<'tcx> for Instance { + type T = rustc_ty::Instance<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.instances[self.def] + } +} + +impl<'tcx> RustcInternal<'tcx> for StaticDef { + type T = rustc_span::def_id::DefId; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.0.internal(tables) + } +} + +#[allow(rustc::usage_of_qualified_ty)] +impl<'tcx, T> RustcInternal<'tcx> for Binder<T> +where + T: RustcInternal<'tcx>, + T::T: rustc_ty::TypeVisitable<rustc_ty::TyCtxt<'tcx>>, +{ + type T = rustc_ty::Binder<'tcx, T::T>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + rustc_ty::Binder::bind_with_vars( + self.value.internal(tables), + tables.tcx.mk_bound_variable_kinds_from_iter( + self.bound_vars.iter().map(|bound| bound.internal(tables)), + ), + ) + } +} + +impl<'tcx> RustcInternal<'tcx> for BoundVariableKind { + type T = rustc_ty::BoundVariableKind; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + BoundVariableKind::Ty(kind) => rustc_ty::BoundVariableKind::Ty(match kind { + BoundTyKind::Anon => rustc_ty::BoundTyKind::Anon, + BoundTyKind::Param(def, symbol) => { + rustc_ty::BoundTyKind::Param(def.0.internal(tables), Symbol::intern(&symbol)) + } + }), + BoundVariableKind::Region(kind) => rustc_ty::BoundVariableKind::Region(match kind { + BoundRegionKind::BrAnon => rustc_ty::BoundRegionKind::BrAnon, + BoundRegionKind::BrNamed(def, symbol) => rustc_ty::BoundRegionKind::BrNamed( + def.0.internal(tables), + Symbol::intern(&symbol), + ), + BoundRegionKind::BrEnv => rustc_ty::BoundRegionKind::BrEnv, + }), + BoundVariableKind::Const => rustc_ty::BoundVariableKind::Const, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for TraitRef { + type T = rustc_ty::TraitRef<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + rustc_ty::TraitRef::new( + tables.tcx, + self.def_id.0.internal(tables), + self.args().internal(tables), + ) + } +} + +impl<'tcx> RustcInternal<'tcx> for AllocId { + type T = rustc_middle::mir::interpret::AllocId; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.alloc_ids[*self] + } +} + +impl<'tcx> RustcInternal<'tcx> for ClosureKind { + type T = rustc_ty::ClosureKind; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + ClosureKind::Fn => rustc_ty::ClosureKind::Fn, + ClosureKind::FnMut => rustc_ty::ClosureKind::FnMut, + ClosureKind::FnOnce => rustc_ty::ClosureKind::FnOnce, + } + } +} + +impl<'tcx, T> RustcInternal<'tcx> for &T +where + T: RustcInternal<'tcx>, +{ + type T = T::T; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + (*self).internal(tables) + } +} diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 4d2a518226d..fa75fd3076c 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -13,6 +13,7 @@ use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::Span; use scoped_tls::scoped_thread_local; use stable_mir::ty::IndexedVal; +use stable_mir::Error; use std::cell::Cell; use std::cell::RefCell; use std::fmt::Debug; @@ -20,12 +21,13 @@ use std::hash::Hash; use std::ops::Index; mod internal; +pub mod pretty; -pub fn stable<'tcx, S: Stable<'tcx>>(item: &S) -> S::T { +pub fn stable<'tcx, S: Stable<'tcx>>(item: S) -> S::T { with_tables(|tables| item.stable(tables)) } -pub fn internal<'tcx, S: RustcInternal<'tcx>>(item: &S) -> S::T { +pub fn internal<'tcx, S: RustcInternal<'tcx>>(item: S) -> S::T { with_tables(|tables| item.internal(tables)) } @@ -104,6 +106,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::RegionDef(self.create_def_id(did)) } + pub fn coroutine_witness_def(&mut self, did: DefId) -> stable_mir::ty::CoroutineWitnessDef { + stable_mir::ty::CoroutineWitnessDef(self.create_def_id(did)) + } + pub fn prov(&mut self, aid: AllocId) -> stable_mir::ty::Prov { stable_mir::ty::Prov(self.create_alloc_id(aid)) } @@ -140,12 +146,13 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum { // datastructures and stable MIR datastructures scoped_thread_local! (static TLV: Cell<*const ()>); -pub(crate) fn init<'tcx>(tables: &TablesWrapper<'tcx>, f: impl FnOnce()) { +pub(crate) fn init<'tcx, F, T>(tables: &TablesWrapper<'tcx>, f: F) -> T +where + F: FnOnce() -> T, +{ assert!(!TLV.is_set()); let ptr = tables as *const _ as *const (); - TLV.set(&Cell::new(ptr), || { - f(); - }); + TLV.set(&Cell::new(ptr), || f()) } /// Loads the current context and calls a function with it. @@ -161,7 +168,10 @@ pub(crate) fn with_tables<'tcx, R>(f: impl FnOnce(&mut Tables<'tcx>) -> R) -> R }) } -pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { +pub fn run<F, T>(tcx: TyCtxt<'_>, f: F) -> Result<T, Error> +where + F: FnOnce() -> T, +{ let tables = TablesWrapper(RefCell::new(Tables { tcx, def_ids: IndexMap::default(), @@ -171,7 +181,7 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { instances: IndexMap::default(), constants: IndexMap::default(), })); - stable_mir::run(&tables, || init(&tables, f)); + stable_mir::run(&tables, || init(&tables, f)) } #[macro_export] @@ -237,7 +247,8 @@ macro_rules! run { queries.global_ctxt().unwrap().enter(|tcx| { rustc_internal::run(tcx, || { self.result = Some((self.callback)(tcx)); - }); + }) + .unwrap(); if self.result.as_ref().is_some_and(|val| val.is_continue()) { Compilation::Continue } else { diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs new file mode 100644 index 00000000000..3ef2d28ea47 --- /dev/null +++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs @@ -0,0 +1,20 @@ +use std::io; + +use super::run; +use rustc_middle::ty::TyCtxt; + +pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> { + writeln!( + w, + "// WARNING: This is highly experimental output it's intended for stable-mir developers only." + )?; + writeln!( + w, + "// If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir." + )?; + let _ = run(tcx, || { + let items = stable_mir::all_local_items(); + let _ = items.iter().map(|item| -> io::Result<()> { item.dump(w) }).collect::<Vec<_>>(); + }); + Ok(()) +} diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 8ff3958da7b..7e74a1d92c7 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -19,10 +19,15 @@ impl<'tcx> BodyBuilder<'tcx> { BodyBuilder { tcx, instance } } + /// Build a stable monomorphic body for a given instance based on the MIR body. + /// + /// Note that we skip instantiation for static and constants. Trying to do so can cause ICE. + /// + /// We do monomorphize non-generic functions to eval unevaluated constants. pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { let mut body = self.tcx.instance_mir(self.instance.def).clone(); - let generics = self.tcx.generics_of(self.instance.def_id()); - if generics.requires_monomorphization(self.tcx) { + if self.tcx.def_kind(self.instance.def_id()).is_fn_like() || !self.instance.args.is_empty() + { self.visit_body(&mut body); } body.stable(tables) @@ -49,6 +54,20 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { *ty = self.monomorphize(*ty); } + fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { + let const_ = self.monomorphize(constant.const_); + let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), None) { + Ok(v) => v, + Err(mir::interpret::ErrorHandled::Reported(..)) => return, + Err(mir::interpret::ErrorHandled::TooGeneric(..)) => { + unreachable!("Failed to evaluate instance constant: {:?}", const_) + } + }; + let ty = constant.ty(); + constant.const_ = mir::Const::Val(val, ty); + self.super_constant(constant, location); + } + fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index d3bd87bb360..89dbf40c7b4 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,10 +7,10 @@ //! //! For now, we are developing everything inside `rustc`, thus, we keep this module private. -use crate::rustc_internal::{IndexMap, RustcInternal}; -use crate::rustc_smir::hir::def::DefKind; -use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region}; +use crate::rustc_internal::{internal, IndexMap, RustcInternal}; +use crate::rustc_smir::stable_mir::ty::{BoundRegion, Region}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::{alloc_range, AllocId}; use rustc_middle::mir::mono::MonoItem; @@ -20,10 +20,11 @@ use rustc_target::abi::FieldIdx; use stable_mir::mir::mono::InstanceDef; use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx}; use stable_mir::ty::{ - Const, ConstId, ConstantKind, FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, - Span, TyKind, UintTy, + AdtDef, AdtKind, ClosureDef, ClosureKind, Const, ConstId, ConstantKind, EarlyParamRegion, + FloatTy, FnDef, GenericArgs, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, + TyKind, UintTy, }; -use stable_mir::{self, opaque, Context, Filename}; +use stable_mir::{self, opaque, Context, CrateItem, Filename, ItemKind}; use std::cell::RefCell; use tracing::debug; @@ -85,9 +86,27 @@ impl<'tcx> Context for TablesWrapper<'tcx> { LineInfo { start_line: lines.1, start_col: lines.2, end_line: lines.3, end_col: lines.4 } } - fn def_kind(&self, def_id: stable_mir::DefId) -> stable_mir::DefKind { + fn item_kind(&self, item: CrateItem) -> ItemKind { + let tables = self.0.borrow(); + new_item_kind(tables.tcx.def_kind(tables[item.0])) + } + + fn adt_kind(&self, def: AdtDef) -> AdtKind { + let mut tables = self.0.borrow_mut(); + let ty = tables.tcx.type_of(def.0.internal(&mut *tables)).instantiate_identity().kind(); + let ty::TyKind::Adt(def, _) = ty else { + panic!("Expected an ADT definition, but found: {ty:?}") + }; + def.adt_kind().stable(&mut *tables) + } + + fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); - tables.tcx.def_kind(tables[def_id]).stable(&mut *tables) + tables.tcx.type_of(item.internal(&mut *tables)).instantiate_identity().stable(&mut *tables) + } + + fn const_literal(&self, cnst: &stable_mir::ty::Const) -> String { + internal(cnst).to_string() } fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span { @@ -198,10 +217,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn instance_body(&self, def: InstanceDef) -> Body { + fn instance_body(&self, def: InstanceDef) -> Option<Body> { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; - builder::BodyBuilder::new(tables.tcx, instance).build(&mut *tables) + tables + .has_body(instance) + .then(|| builder::BodyBuilder::new(tables.tcx, instance).build(&mut *tables)) } fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { @@ -249,6 +270,38 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Ok(None) | Err(_) => None, } } + + fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance { + let mut tables = self.0.borrow_mut(); + let internal_ty = ty.internal(&mut *tables); + let instance = Instance::resolve_drop_in_place(tables.tcx, internal_ty); + instance.stable(&mut *tables) + } + + fn resolve_for_fn_ptr( + &self, + def: FnDef, + args: &GenericArgs, + ) -> Option<stable_mir::mir::mono::Instance> { + let mut tables = self.0.borrow_mut(); + let def_id = def.0.internal(&mut *tables); + let args_ref = args.internal(&mut *tables); + Instance::resolve_for_fn_ptr(tables.tcx, ParamEnv::reveal_all(), def_id, args_ref) + .stable(&mut *tables) + } + + fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option<stable_mir::mir::mono::Instance> { + let mut tables = self.0.borrow_mut(); + let def_id = def.0.internal(&mut *tables); + let args_ref = args.internal(&mut *tables); + let closure_kind = kind.internal(&mut *tables); + Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind).stable(&mut *tables) + } } pub(crate) struct TablesWrapper<'tcx>(pub(crate) RefCell<Tables<'tcx>>); @@ -271,6 +324,17 @@ impl<'tcx> Tables<'tcx> { fn intern_const(&mut self, constant: mir::Const<'tcx>) -> ConstId { self.constants.create_or_fetch(constant) } + + fn has_body(&self, instance: Instance<'tcx>) -> bool { + let def_id = instance.def_id(); + self.tcx.is_mir_available(def_id) + || !matches!( + instance.def, + ty::InstanceDef::Virtual(..) + | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::Item(..) + ) + } } /// Build a stable mir crate from a given crate number. @@ -281,6 +345,40 @@ fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local } } +fn new_item_kind(kind: DefKind) -> ItemKind { + match kind { + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::Impl { .. } + | DefKind::Ctor(_, _) + | DefKind::GlobalAsm => { + unreachable!("Not a valid item kind: {kind:?}"); + } + DefKind::Closure | DefKind::Coroutine | DefKind::AssocFn | DefKind::Fn => ItemKind::Fn, + DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => { + ItemKind::Const + } + DefKind::Static(_) => ItemKind::Static, + } +} + /// Trait used to convert between an internal MIR type to a Stable MIR type. pub trait Stable<'tcx> { /// The stable representation of the type implementing Stable. @@ -310,6 +408,7 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { .map(|decl| stable_mir::mir::LocalDecl { ty: decl.ty.stable(tables), span: decl.source_info.span.stable(tables), + mutability: decl.mutability.stable(tables), }) .collect(), self.arg_count, @@ -682,10 +781,44 @@ impl<'tcx> Stable<'tcx> for mir::ConstOperand<'tcx> { impl<'tcx> Stable<'tcx> for mir::Place<'tcx> { type T = stable_mir::mir::Place; - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { stable_mir::mir::Place { local: self.local.as_usize(), - projection: format!("{:?}", self.projection), + projection: self.projection.iter().map(|e| e.stable(tables)).collect(), + } + } +} + +impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> { + type T = stable_mir::mir::ProjectionElem; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use mir::ProjectionElem::*; + match self { + Deref => stable_mir::mir::ProjectionElem::Deref, + Field(idx, ty) => { + stable_mir::mir::ProjectionElem::Field(idx.stable(tables), ty.stable(tables)) + } + Index(local) => stable_mir::mir::ProjectionElem::Index(local.stable(tables)), + ConstantIndex { offset, min_length, from_end } => { + stable_mir::mir::ProjectionElem::ConstantIndex { + offset: *offset, + min_length: *min_length, + from_end: *from_end, + } + } + Subslice { from, to, from_end } => stable_mir::mir::ProjectionElem::Subslice { + from: *from, + to: *to, + from_end: *from_end, + }, + // MIR includes an `Option<Symbol>` argument for `Downcast` that is the name of the + // variant, used for printing MIR. However this information should also be accessible + // via a lookup using the `VariantIdx`. The `Option<Symbol>` argument is therefore + // dropped when converting to Stable MIR. A brief justification for this decision can be + // found at https://github.com/rust-lang/rust/pull/117517#issuecomment-1811683486 + Downcast(_, idx) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables)), + OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables)), + Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables)), } } } @@ -693,8 +826,8 @@ impl<'tcx> Stable<'tcx> for mir::Place<'tcx> { impl<'tcx> Stable<'tcx> for mir::UserTypeProjection { type T = stable_mir::mir::UserTypeProjection; - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - UserTypeProjection { base: self.base.as_usize(), projection: format!("{:?}", self.projs) } + fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T { + UserTypeProjection { base: self.base.as_usize(), projection: opaque(&self.projs) } } } @@ -892,6 +1025,18 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { } } +impl<'tcx> Stable<'tcx> for ty::AdtKind { + type T = AdtKind; + + fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + ty::AdtKind::Struct => AdtKind::Struct, + ty::AdtKind::Union => AdtKind::Union, + ty::AdtKind::Enum => AdtKind::Enum, + } + } +} + impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource { type T = stable_mir::mir::CoroutineSource; fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { @@ -1028,8 +1173,6 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { type T = stable_mir::ty::GenericArgs; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - use stable_mir::ty::GenericArgs; - GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect()) } } @@ -1292,7 +1435,11 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Bound(debruijn_idx, bound_ty) => { TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables)) } - ty::Placeholder(..) | ty::CoroutineWitness(..) | ty::Infer(_) | ty::Error(_) => { + ty::CoroutineWitness(def_id, args) => TyKind::RigidTy(RigidTy::CoroutineWitness( + tables.coroutine_witness_def(*def_id), + args.stable(tables), + )), + ty::Placeholder(..) | ty::Infer(_) | ty::Error(_) => { unreachable!(); } } @@ -1448,7 +1595,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::TraitRef; - TraitRef { def_id: tables.trait_def(self.def_id), args: self.args.stable(tables) } + TraitRef::try_new(tables.trait_def(self.def_id), self.args.stable(tables)).unwrap() } } @@ -1691,7 +1838,7 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::RegionKind; match self { - ty::ReEarlyBound(early_reg) => RegionKind::ReEarlyBound(EarlyBoundRegion { + ty::ReEarlyParam(early_reg) => RegionKind::ReEarlyParam(EarlyParamRegion { def_id: tables.region_def(early_reg.def_id), index: early_reg.index, name: early_reg.name.to_string(), @@ -1724,15 +1871,6 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span { } } -impl<'tcx> Stable<'tcx> for DefKind { - type T = stable_mir::DefKind; - - fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - // FIXME: add a real implementation of stable DefKind - opaque(self) - } -} - impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { type T = stable_mir::mir::mono::Instance; @@ -1767,3 +1905,25 @@ impl<'tcx> Stable<'tcx> for MonoItem<'tcx> { } } } + +impl<'tcx, T> Stable<'tcx> for &T +where + T: Stable<'tcx>, +{ + type T = T::T; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + (*self).stable(tables) + } +} + +impl<'tcx, T> Stable<'tcx> for Option<T> +where + T: Stable<'tcx>, +{ + type T = Option<T::T>; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.as_ref().map(|value| value.stable(tables)) + } +} diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 36731d0fe74..988ff57254c 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -569,10 +569,6 @@ impl HygieneData { } } -pub fn clear_syntax_context_map() { - HygieneData::with(|data| data.syntax_context_map = FxHashMap::default()); -} - pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { HygieneData::with(|data| data.walk_chain(span, to)) } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 489c8d18926..6400ba488bb 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,11 +17,10 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![deny(rustc::diagnostic_outside_of_impl)] #![deny(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(array_windows)] #![feature(cfg_match)] #![feature(core_io_borrowed_buf)] @@ -33,6 +32,7 @@ #![feature(read_buf)] #![feature(round_char_boundary)] #![feature(rustc_attrs)] +#![feature(rustdoc_internals)] // tidy-alphabetical-end #[macro_use] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f287862cc23..2e3b5446405 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -124,6 +124,7 @@ symbols! { // There is currently no checking that all symbols are used; that would be // nice to have. Symbols { + Abi, AcqRel, Acquire, AddToDiagnostic, @@ -166,6 +167,7 @@ symbols! { CString, Capture, Center, + Cleanup, Clone, Command, ConstParamTy, @@ -215,6 +217,7 @@ symbols! { HashSet, Hasher, Implied, + InCleanup, IndexOutput, Input, Instant, @@ -258,6 +261,7 @@ symbols! { NonZeroU8, NonZeroUsize, None, + Normal, Ok, Option, Ord, @@ -1023,6 +1027,36 @@ symbols! { minnumf32, minnumf64, mips_target_feature, + mir_basic_block, + mir_call, + mir_cast_transmute, + mir_checked, + mir_copy_for_deref, + mir_debuginfo, + mir_deinit, + mir_discriminant, + mir_drop, + mir_field, + mir_goto, + mir_len, + mir_make_place, + mir_move, + mir_offset, + mir_retag, + mir_return, + mir_set_discriminant, + mir_static, + mir_static_mut, + mir_storage_dead, + mir_storage_live, + mir_unreachable, + mir_unwind_cleanup, + mir_unwind_continue, + mir_unwind_resume, + mir_unwind_terminate, + mir_unwind_terminate_reason, + mir_unwind_unreachable, + mir_variant, miri, misc, mmx_reg, @@ -1130,7 +1164,6 @@ symbols! { optin_builtin_traits, option, option_env, - option_payload_ptr, options, or, or_patterns, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 53925eeaaa0..0178ff53b24 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -4,7 +4,6 @@ use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; -use rustc_middle::util::common::record_time; use std::fmt::{self, Write}; use std::mem::{self, discriminant}; @@ -101,40 +100,38 @@ fn get_symbol_hash<'tcx>( tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - record_time(&tcx.sess.perf_stats.symbol_hash_time, || { - // the main symbol name is not necessarily unique; hash in the - // compiler's internal def-path, guaranteeing each symbol has a - // truly unique path - tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); - - // Include the main item-type. Note that, in this case, the - // assertions about `has_param` may not hold, but this item-type - // ought to be the same for every reference anyway. - assert!(!item_type.has_erasable_regions()); - hcx.while_hashing_spans(false, |hcx| { - item_type.hash_stable(hcx, &mut hasher); - - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.kind() { - item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); - } + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); + + // Include the main item-type. Note that, in this case, the + // assertions about `has_param` may not hold, but this item-type + // ought to be the same for every reference anyway. + assert!(!item_type.has_erasable_regions()); + hcx.while_hashing_spans(false, |hcx| { + item_type.hash_stable(hcx, &mut hasher); + + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::FnDef(..) = item_type.kind() { + item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); + } - // also include any type parameters (for generic items) - args.hash_stable(hcx, &mut hasher); + // also include any type parameters (for generic items) + args.hash_stable(hcx, &mut hasher); - if let Some(instantiating_crate) = instantiating_crate { - tcx.def_path_hash(instantiating_crate.as_def_id()) - .stable_crate_id() - .hash_stable(hcx, &mut hasher); - } + if let Some(instantiating_crate) = instantiating_crate { + tcx.def_path_hash(instantiating_crate.as_def_id()) + .stable_crate_id() + .hash_stable(hcx, &mut hasher); + } - // We want to avoid accidental collision between different types of instances. - // Especially, `VTableShim`s and `ReifyShim`s may overlap with their original - // instances without this. - discriminant(&instance.def).hash_stable(hcx, &mut hasher); - }); + // We want to avoid accidental collision between different types of instances. + // Especially, `VTableShim`s and `ReifyShim`s may overlap with their original + // instances without this. + discriminant(&instance.def).hash_stable(hcx, &mut hasher); }); // 64 bits should be enough to avoid collisions. diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 14dd8b4e56f..9f0982d0995 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -88,9 +88,9 @@ //! DefPaths which are much more robust in the face of changes to the code base. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(never_type)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 80dec80bf03..1501e7d0cf7 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -282,12 +282,12 @@ fn encode_region<'tcx>( s.push('E'); compress(dict, DictKey::Region(region), &mut s); } - // FIXME(@lcnr): Why is `ReEarlyBound` reachable here. - RegionKind::ReEarlyBound(..) | RegionKind::ReErased => { + // FIXME(@lcnr): Why is `ReEarlyParam` reachable here. + RegionKind::ReEarlyParam(..) | RegionKind::ReErased => { s.push_str("u6region"); compress(dict, DictKey::Region(region), &mut s); } - RegionKind::ReFree(..) + RegionKind::ReLateParam(..) | RegionKind::ReStatic | RegionKind::ReError(_) | RegionKind::ReVar(..) diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 5efd171b9dd..1aa24f6b84a 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -836,7 +836,6 @@ impl<'a, Ty> FnAbi<'a, Ty> { wasm::compute_c_abi_info(cx, self) } } - "asmjs" => wasm::compute_c_abi_info(cx, self), "bpf" => bpf::compute_abi_info(self), arch => { return Err(AdjustForForeignAbiError::Unsupported { diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 9c5ce889418..e17b81d4652 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -8,8 +8,8 @@ //! LLVM. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index d8dd4ae2286..9de8aa7c712 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1609,7 +1609,6 @@ supported_targets! { ("thumbv7a-pc-windows-msvc", thumbv7a_pc_windows_msvc), ("thumbv7a-uwp-windows-msvc", thumbv7a_uwp_windows_msvc), - ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), ("wasm32-unknown-unknown", wasm32_unknown_unknown), ("wasm32-wasi", wasm32_wasi), @@ -2244,10 +2243,6 @@ impl TargetOptions { add_link_args(&mut self.pre_link_args, flavor, args); } - fn add_post_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) { - add_link_args(&mut self.post_link_args, flavor, args); - } - fn update_from_cli(&mut self) { self.linker_flavor = LinkerFlavor::from_cli_json( self.linker_flavor_json, diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index a9792ca2795..d753aa8618e 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -22,6 +22,10 @@ trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entri trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` .label = empty on-clause here +trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to previous definition of `{$option_name}` + .other_label = `{$option_name}` is first declared here + .label = `{$option_name}` is already declared here + trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 5ba29f87855..de52fa6c447 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,9 +11,9 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -21,7 +21,6 @@ #![feature(let_chains)] #![feature(if_let_guard)] #![feature(never_type)] -#![feature(result_option_inspect)] #![feature(type_alias_impl_trait)] #![feature(min_specialization)] #![recursion_limit = "512"] // For rustdoc diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index f7031c5f493..739bbe929b3 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -11,18 +11,12 @@ //! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both //! may apply, then we can compute the "intersection" of both normalizes-to by //! performing them together. This is used specifically to resolve ambiguities. -use super::{EvalCtxt, SolverMode}; +use super::EvalCtxt; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; -/// We may need to invert the alias relation direction if dealing an alias on the RHS. -#[derive(Debug)] -enum Invert { - No, - Yes, -} - impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] pub(super) fn compute_alias_relate_goal( @@ -31,187 +25,130 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { let tcx = self.tcx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; - if lhs.is_infer() || rhs.is_infer() { - bug!( - "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" - ); - } - - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { - (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), - // RHS is not a projection, only way this is true is if LHS normalizes-to RHS - (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate( - param_env, - alias_lhs, - rhs, - direction, - Invert::No, - ), + let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; - // LHS is not a projection, only way this is true is if RHS normalizes-to LHS - (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate( - param_env, - alias_rhs, - lhs, - direction, - Invert::Yes, - ), + let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; - (Some(alias_lhs), Some(alias_rhs)) => { - debug!("both sides are aliases"); + let variance = match direction { + ty::AliasRelationDirection::Equate => ty::Variance::Invariant, + ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, + }; - let mut candidates = Vec::new(); - // LHS normalizes-to RHS - candidates.extend(self.assemble_normalizes_to_candidate( - param_env, - alias_lhs, - rhs, - direction, - Invert::No, - )); - // RHS normalizes-to RHS - candidates.extend(self.assemble_normalizes_to_candidate( - param_env, - alias_rhs, - lhs, - direction, - Invert::Yes, - )); - // Relate via args - candidates.extend( - self.assemble_subst_relate_candidate( - param_env, alias_lhs, alias_rhs, direction, - ), - ); - debug!(?candidates); + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { + (None, None) => { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - if let Some(merged) = self.try_merge_responses(&candidates) { - Ok(merged) + (Some(alias), None) => { + if rhs.is_infer() { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else if alias.is_opaque(tcx) { + self.define_opaque(param_env, alias, rhs) } else { - // When relating two aliases and we have ambiguity, if both - // aliases can be normalized to something, we prefer - // "bidirectionally normalizing" both of them within the same - // candidate. - // - // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/25>. - // - // As this is incomplete, we must not do so during coherence. - match self.solver_mode() { - SolverMode::Normal => { - if let Ok(bidirectional_normalizes_to_response) = self - .assemble_bidirectional_normalizes_to_candidate( - param_env, lhs, rhs, direction, - ) - { - Ok(bidirectional_normalizes_to_response) - } else { - self.flounder(&candidates) - } - } - SolverMode::Coherence => self.flounder(&candidates), - } + Err(NoSolution) } } + (None, Some(alias)) => { + if lhs.is_infer() { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else if alias.is_opaque(tcx) { + self.define_opaque(param_env, alias, lhs) + } else { + Err(NoSolution) + } + } + + (Some(alias_lhs), Some(alias_rhs)) => { + self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs) + } } } - #[instrument(level = "debug", skip(self), ret)] - fn assemble_normalizes_to_candidate( + /// Normalize the `term` to equate it later. This does not define opaque types. + #[instrument(level = "debug", skip(self, param_env), ret)] + fn try_normalize_term( &mut self, param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - other: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, - invert: Invert, - ) -> QueryResult<'tcx> { - self.probe_misc_candidate("normalizes-to").enter(|ecx| { - ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + term: ty::Term<'tcx>, + ) -> Result<Option<ty::Term<'tcx>>, NoSolution> { + match term.unpack() { + ty::TermKind::Ty(ty) => { + // We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`. + Ok(self + .try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty) + .map(Into::into)) + } + ty::TermKind::Const(_) => { + if let Some(alias) = term.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(term); + self.add_goal(Goal::new( + self.tcx(), + param_env, + ty::ProjectionPredicate { projection_ty: alias, term }, + )); + self.try_evaluate_added_goals()?; + Ok(Some(self.resolve_vars_if_possible(term))) + } else { + Ok(Some(term)) + } + } + } } - // Computes the normalizes-to branch, with side-effects. This must be performed - // in a probe in order to not taint the evaluation context. - fn normalizes_to_inner( + fn define_opaque( &mut self, param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - other: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, - invert: Invert, - ) -> Result<(), NoSolution> { - let other = match direction { - // This is purely an optimization. No need to instantiate a new - // infer var and equate the RHS to it. - ty::AliasRelationDirection::Equate => other, - - // Instantiate an infer var and subtype our RHS to it, so that we - // properly represent a subtype relation between the LHS and RHS - // of the goal. - ty::AliasRelationDirection::Subtype => { - let fresh = self.next_term_infer_of_kind(other); - let (sub, sup) = match invert { - Invert::No => (fresh, other), - Invert::Yes => (other, fresh), - }; - self.sub(param_env, sub, sup)?; - fresh - } - }; + opaque: ty::AliasTy<'tcx>, + term: ty::Term<'tcx>, + ) -> QueryResult<'tcx> { self.add_goal(Goal::new( self.tcx(), param_env, - ty::ProjectionPredicate { projection_ty: alias, term: other }, + ty::ProjectionPredicate { projection_ty: opaque, term }, )); - - Ok(()) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - fn assemble_subst_relate_candidate( + fn relate_rigid_alias_or_opaque( &mut self, param_env: ty::ParamEnv<'tcx>, - alias_lhs: ty::AliasTy<'tcx>, - alias_rhs: ty::AliasTy<'tcx>, - direction: ty::AliasRelationDirection, + lhs: ty::AliasTy<'tcx>, + variance: ty::Variance, + rhs: ty::AliasTy<'tcx>, ) -> QueryResult<'tcx> { - self.probe_misc_candidate("args relate").enter(|ecx| { - match direction { - ty::AliasRelationDirection::Equate => { - ecx.eq(param_env, alias_lhs, alias_rhs)?; - } - ty::AliasRelationDirection::Subtype => { - ecx.sub(param_env, alias_lhs, alias_rhs)?; - } - } + let tcx = self.tcx(); + let mut candidates = vec![]; + if lhs.is_opaque(tcx) { + candidates.extend( + self.probe_misc_candidate("define-lhs-opaque") + .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())), + ); + } - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } + if rhs.is_opaque(tcx) { + candidates.extend( + self.probe_misc_candidate("define-rhs-opaque") + .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())), + ); + } - fn assemble_bidirectional_normalizes_to_candidate( - &mut self, - param_env: ty::ParamEnv<'tcx>, - lhs: ty::Term<'tcx>, - rhs: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, - ) -> QueryResult<'tcx> { - self.probe_misc_candidate("bidir normalizes-to").enter(|ecx| { - ecx.normalizes_to_inner( - param_env, - lhs.to_alias_ty(ecx.tcx()).unwrap(), - rhs, - direction, - Invert::No, - )?; - ecx.normalizes_to_inner( - param_env, - rhs.to_alias_ty(ecx.tcx()).unwrap(), - lhs, - direction, - Invert::Yes, - )?; + candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| { + ecx.relate(param_env, lhs, variance, rhs)?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + })); + + if let Some(result) = self.try_merge_responses(&candidates) { + Ok(result) + } else { + self.flounder(&candidates) + } } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 27d2bdead83..13d7ebe1db0 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -352,7 +352,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return }; candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - if num_steps < ecx.local_overflow_limit() { + if tcx.recursion_limit().value_within_limit(num_steps) { let normalized_ty = ecx.next_ty_infer(); let normalizes_to_goal = goal.with( tcx, @@ -864,23 +864,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { let trait_ref = goal.predicate.trait_ref(tcx); - #[derive(Debug)] - enum FailureKind { - Overflow, - NoSolution(NoSolution), - } + struct Overflow; let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) { - Ok(Some(ty)) => Ok(ty), - Ok(None) => Err(FailureKind::Overflow), - Err(e) => Err(FailureKind::NoSolution(e)), + Some(ty) => Ok(ty), + None => Err(Overflow), }; match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) { - Err(FailureKind::Overflow) => { + Err(Overflow) => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) } - Err(FailureKind::NoSolution(NoSolution)) | Ok(Ok(())) => Err(NoSolution), + Ok(Ok(())) => Err(NoSolution), Ok(Err(_)) => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index 5f08bc0039a..004dc45263c 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -237,7 +237,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { CanonicalizeMode::Response { .. } => return r, }, - ty::ReFree(_) | ty::ReEarlyBound(_) => match self.canonicalize_mode { + ty::ReLateParam(_) | ty::ReEarlyParam(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => bug!("unexpected region in response: {r:?}"), }, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs new file mode 100644 index 00000000000..c47152c601c --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs @@ -0,0 +1,45 @@ +use super::EvalCtxt; +use crate::solve::inspect; +use rustc_middle::traits::query::NoSolution; + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(in crate::solve) fn commit_if_ok<T>( + &mut self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>, + ) -> Result<T, NoSolution> { + let mut nested_ecx = EvalCtxt { + infcx: self.infcx, + variables: self.variables, + var_values: self.var_values, + predefined_opaques_in_body: self.predefined_opaques_in_body, + max_input_universe: self.max_input_universe, + search_graph: self.search_graph, + nested_goals: self.nested_goals.clone(), + tainted: self.tainted, + inspect: self.inspect.new_probe(), + }; + + let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx)); + if result.is_ok() { + let EvalCtxt { + infcx: _, + variables: _, + var_values: _, + predefined_opaques_in_body: _, + max_input_universe: _, + search_graph: _, + nested_goals, + tainted, + inspect, + } = nested_ecx; + self.nested_goals = nested_goals; + self.tainted = tainted; + self.inspect.integrate_snapshot(inspect); + } else { + nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk); + self.inspect.finish_probe(nested_ecx.inspect); + } + + result + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 583eb9f96a9..23ce0a301ce 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -34,6 +34,7 @@ use super::{search_graph::SearchGraph, Goal}; pub use select::InferCtxtSelectExt; mod canonical; +mod commit_if_ok; mod probe; mod select; @@ -332,7 +333,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); - let encountered_overflow = self.search_graph.encountered_overflow(); let canonical_response = EvalCtxt::evaluate_canonical_goal( self.tcx(), self.search_graph, @@ -367,75 +367,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { bug!("an unchanged goal shouldn't have any side-effects on instantiation"); } - // Check that rerunning this query with its inference constraints applied - // doesn't result in new inference constraints and has the same result. + // FIXME: We previously had an assert here that checked that recomputing + // a goal after applying its constraints did not change its response. // - // If we have projection goals like `<T as Trait>::Assoc == u32` we recursively - // call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal - // could constrain `U` to `u32` which would cause this check to result in a - // solver cycle. - if cfg!(debug_assertions) - && has_changed - && !matches!( - goal_evaluation_kind, - GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes } - ) - && !self.search_graph.in_cycle() - { - // The nested evaluation has to happen with the original state - // of `encountered_overflow`. - let from_original_evaluation = - self.search_graph.reset_encountered_overflow(encountered_overflow); - self.check_evaluate_goal_stable_result(goal, canonical_goal, canonical_response); - // In case the evaluation was unstable, we manually make sure that this - // debug check does not influence the result of the parent goal. - self.search_graph.reset_encountered_overflow(from_original_evaluation); - } + // This assert was removed as it did not hold for goals constraining + // an inference variable to a recursive alias, e.g. in + // tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs. + // + // Once we have decided on how to handle trait-system-refactor-initiative#75, + // we should re-add an assert here. Ok((has_changed, certainty, nested_goals)) } - fn check_evaluate_goal_stable_result( - &mut self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - original_input: CanonicalInput<'tcx>, - original_result: CanonicalResponse<'tcx>, - ) { - let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let result = EvalCtxt::evaluate_canonical_goal( - self.tcx(), - self.search_graph, - canonical_goal, - // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal` - &mut ProofTreeBuilder::new_noop(), - ); - - macro_rules! fail { - ($msg:expr) => {{ - let msg = $msg; - warn!( - "unstable result: {msg}\n\ - original goal: {original_input:?},\n\ - original result: {original_result:?}\n\ - re-canonicalized goal: {canonical_goal:?}\n\ - second response: {result:?}" - ); - return; - }}; - } - - let Ok(new_canonical_response) = result else { fail!("second response was error") }; - // We only check for modulo regions as we convert all regions in - // the input to new existentials, even if they're expected to be - // `'static` or a placeholder region. - if !new_canonical_response.value.var_values.is_identity_modulo_regions() { - fail!("additional constraints from second response") - } - if original_result.value.certainty != new_canonical_response.value.certainty { - fail!("unstable certainty") - } - } - fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); @@ -750,6 +694,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn relate<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .relate(DefineOpaqueTypes::No, lhs, variance, rhs) + .map(|InferOk { value: (), obligations }| { + self.add_goals(obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to relate"); + NoSolution + }) + } + /// Equates two values returning the nested goals without adding them /// to the nested goals of the `EvalCtxt`. /// diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 69bfdd4688c..5752d49ed2c 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { visitor: &mut V, ) -> ControlFlow<V::BreakTy> { // HACK: An arbitrary cutoff to avoid dealing with overflow and cycles. - if self.goal.depth >= 10 { + if self.goal.depth <= 10 { let infcx = self.goal.infcx; infcx.probe(|_| { let mut instantiated_goals = vec![]; @@ -120,7 +120,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { for step in &probe.steps { match step { &inspect::ProbeStep::AddGoal(goal) => nested_goals.push(goal), - inspect::ProbeStep::EvaluateGoals(_) => (), inspect::ProbeStep::NestedProbe(ref probe) => { // Nested probes have to prove goals added in their parent // but do not leak them, so we truncate the added goals @@ -129,13 +128,17 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.candidates_recur(candidates, nested_goals, probe); nested_goals.truncate(num_goals); } + inspect::ProbeStep::EvaluateGoals(_) + | inspect::ProbeStep::CommitIfOkStart + | inspect::ProbeStep::CommitIfOkSuccess => (), } } match probe.kind { inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly - | inspect::ProbeKind::UpcastProjectionCompatibility => (), + | inspect::ProbeKind::UpcastProjectionCompatibility + | inspect::ProbeKind::CommitIfOk => (), // We add a candidate for the root evaluation if there // is only one way to prove a given goal, e.g. for `WellFormed`. // diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index 088455b38cb..0d46df44c91 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -219,6 +219,8 @@ enum WipProbeStep<'tcx> { AddGoal(inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), NestedProbe(WipProbe<'tcx>), + CommitIfOkStart, + CommitIfOkSuccess, } impl<'tcx> WipProbeStep<'tcx> { @@ -227,6 +229,8 @@ impl<'tcx> WipProbeStep<'tcx> { WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal), WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), + WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart, + WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess, } } } @@ -459,6 +463,29 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } + /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside + /// of the probe into the parent. + pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *probe.state.unwrap()) { + ( + DebugSolver::Probe(WipProbe { steps, .. }) + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipProbe { steps, .. }, + .. + }), + DebugSolver::Probe(probe), + ) => { + steps.push(WipProbeStep::CommitIfOkStart); + assert_eq!(probe.kind, None); + steps.extend(probe.steps); + steps.push(WipProbeStep::CommitIfOkSuccess); + } + _ => unreachable!(), + } + } + } + pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }) } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index dba5369fa0f..65d061ab3f4 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -16,13 +16,14 @@ //! about it on zulip. use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult, Response, }; -use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; @@ -297,25 +298,62 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn try_normalize_ty( &mut self, param_env: ty::ParamEnv<'tcx>, - mut ty: Ty<'tcx>, - ) -> Result<Option<Ty<'tcx>>, NoSolution> { - for _ in 0..self.local_overflow_limit() { - let ty::Alias(_, projection_ty) = *ty.kind() else { - return Ok(Some(ty)); - }; - - let normalized_ty = self.next_ty_infer(); + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty) + } + + fn try_normalize_ty_recur( + &mut self, + param_env: ty::ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + depth: usize, + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + if !self.tcx().recursion_limit().value_within_limit(depth) { + return None; + } + + let ty::Alias(kind, projection_ty) = *ty.kind() else { + return Some(ty); + }; + + // We do no always define opaque types eagerly to allow non-defining uses in the defining scope. + if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) { + if let Some(def_id) = projection_ty.def_id.as_local() { + if self + .unify_existing_opaque_tys( + param_env, + OpaqueTypeKey { def_id, args: projection_ty.args }, + self.next_ty_infer(), + ) + .is_empty() + { + return Some(ty); + } + } + } + + // FIXME(@lcnr): If the normalization of the alias adds an inference constraint which + // causes a previously added goal to fail, then we treat the alias as rigid. + // + // These feels like a potential issue, I should look into writing some tests here + // and then probably changing `commit_if_ok` to not inherit the parent goals. + match self.commit_if_ok(|this| { + let normalized_ty = this.next_ty_infer(); let normalizes_to_goal = Goal::new( - self.tcx(), + this.tcx(), param_env, ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, ); - self.add_goal(normalizes_to_goal); - self.try_evaluate_added_goals()?; - ty = self.resolve_vars_if_possible(normalized_ty); + this.add_goal(normalizes_to_goal); + this.try_evaluate_added_goals()?; + let ty = this.resolve_vars_if_possible(normalized_ty); + Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty)) + }) { + Ok(ty) => ty, + Err(NoSolution) => Some(ty), } - - Ok(None) } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs index 240141065dc..7fb550aa3e0 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs @@ -160,7 +160,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; - if !drcx.args_refs_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) { + if !drcx.args_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs b/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs index ebd129f32b9..1fde129c3a0 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs @@ -44,6 +44,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Prefer opaques registered already. let opaque_type_key = ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args }; + // FIXME: This also unifies the previous hidden type with the expected. + // + // If that fails, we insert `expected` as a new hidden type instead of + // eagerly emitting an error. let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected); if !matches.is_empty() { @@ -53,6 +57,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return self.flounder(&matches); } } + + let expected = match self.try_normalize_ty(goal.param_env, expected) { + Some(ty) => { + if ty.is_ty_var() { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } else { + ty + } + } + None => { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + } + }; + // Otherwise, define a new opaque type self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; self.add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 7ffa1d7d319..68f81a05536 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -110,39 +110,6 @@ impl<'tcx> SearchGraph<'tcx> { self.stack.is_empty() } - /// Whether we're currently in a cycle. This should only be used - /// for debug assertions. - pub(super) fn in_cycle(&self) -> bool { - if let Some(stack_depth) = self.stack.last_index() { - // Either the current goal on the stack is the root of a cycle - // or it depends on a goal with a lower depth. - self.stack[stack_depth].has_been_used - || self.stack[stack_depth].cycle_root_depth != stack_depth - } else { - false - } - } - - /// Fetches whether the current goal encountered overflow. - /// - /// This should only be used for the check in `evaluate_goal`. - pub(super) fn encountered_overflow(&self) -> bool { - if let Some(last) = self.stack.raw.last() { last.encountered_overflow } else { false } - } - - /// Resets `encountered_overflow` of the current goal. - /// - /// This should only be used for the check in `evaluate_goal`. - pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) -> bool { - if let Some(last) = self.stack.raw.last_mut() { - let prev = last.encountered_overflow; - last.encountered_overflow = encountered_overflow; - prev - } else { - false - } - } - /// Returns the remaining depth allowed for nested goals. /// /// This is generally simply one less than the current depth. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index a0e2ad6e202..0f30b49314b 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -43,9 +43,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; - if !drcx - .args_refs_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) - { + if !drcx.args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); } @@ -471,7 +469,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's destructured as a `dyn Trait`. let Some(b_ty) = - ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))? + ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) else { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); }; @@ -538,9 +536,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let b_ty = match ecx .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) { - Ok(Some(b_ty)) => b_ty, - Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], - Err(_) => return vec![], + Some(b_ty) => b_ty, + None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], }; let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index dcf5fd86929..7ca33ae41c0 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -20,6 +20,7 @@ use crate::traits::{ }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; @@ -100,7 +101,7 @@ pub fn overlapping_impls( let impl1_ref = tcx.impl_trait_ref(impl1_def_id); let impl2_ref = tcx.impl_trait_ref(impl2_def_id); let may_overlap = match (impl1_ref, impl2_ref) { - (Some(a), Some(b)) => drcx.args_refs_may_unify(a.skip_binder().args, b.skip_binder().args), + (Some(a), Some(b)) => drcx.args_may_unify(a.skip_binder().args, b.skip_binder().args), (None, None) => { let self_ty1 = tcx.type_of(impl1_def_id).skip_binder(); let self_ty2 = tcx.type_of(impl2_def_id).skip_binder(); @@ -1002,7 +1003,12 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { // and then prove the resulting predicate as a nested goal. let trait_ref = match predicate.kind().no_bound_vars() { Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref, - Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) => { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) + if matches!( + infcx.tcx.def_kind(proj.projection_ty.def_id), + DefKind::AssocTy | DefKind::AssocConst + ) => + { proj.projection_ty.trait_ref(infcx.tcx) } _ => return ControlFlow::Continue(()), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 5fbfedd3e83..da2d5dfbfb5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -187,11 +187,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id) && let ty::Tuple(args) = trait_ref.args.type_at(1).kind() { - let args = args - .iter() - .map(|ty| ty.to_string()) - .collect::<Vec<_>>() - .join(", "); + let args = args.iter().map(|ty| ty.to_string()).collect::<Vec<_>>().join(", "); flags.push((sym::Trait, Some(format!("{}({args})", kind.as_str())))); } else { flags.push((sym::Trait, Some(trait_ref.print_only_trait_path().to_string()))); @@ -325,7 +321,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString(Symbol); +pub struct OnUnimplementedFormatString(Symbol, Span); #[derive(Debug)] pub struct OnUnimplementedDirective { @@ -354,7 +350,7 @@ pub struct OnUnimplementedNote { pub enum AppendConstMessage { #[default] Default, - Custom(Symbol), + Custom(Symbol, Span), } #[derive(LintDiagnostic)] @@ -376,6 +372,35 @@ impl MalformedOnUnimplementedAttrLint { #[help] pub struct MissingOptionsForOnUnimplementedAttr; +#[derive(LintDiagnostic)] +#[diag(trait_selection_ignored_diagnostic_option)] +pub struct IgnoredDiagnosticOption { + pub option_name: &'static str, + #[label] + pub span: Span, + #[label(trait_selection_other_label)] + pub prev_span: Span, +} + +impl IgnoredDiagnosticOption { + fn maybe_emit_warning<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + new: Option<Span>, + old: Option<Span>, + option_name: &'static str, + ) { + if let (Some(new_item), Some(old_item)) = (new, old) { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + new_item, + IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + ); + } + } +} + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -388,8 +413,9 @@ impl<'tcx> OnUnimplementedDirective { let mut errored = None; let mut item_iter = items.iter(); - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) + let parse_value = |value_str, value_span| { + OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span) + .map(Some) }; let condition = if is_root { @@ -402,7 +428,7 @@ impl<'tcx> OnUnimplementedDirective { .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value - && let Err(guar) = parse_value(value) + && let Err(guar) = parse_value(value, cfg.span) { errored = Some(guar); } @@ -421,17 +447,17 @@ impl<'tcx> OnUnimplementedDirective { for item in item_iter { if item.has_name(sym::message) && message.is_none() { if let Some(message_) = item.value_str() { - message = parse_value(message_)?; + message = parse_value(message_, item.span())?; continue; } } else if item.has_name(sym::label) && label.is_none() { if let Some(label_) = item.value_str() { - label = parse_value(label_)?; + label = parse_value(label_, item.span())?; continue; } } else if item.has_name(sym::note) { if let Some(note_) = item.value_str() { - if let Some(note) = parse_value(note_)? { + if let Some(note) = parse_value(note_, item.span())? { notes.push(note); continue; } @@ -441,7 +467,7 @@ impl<'tcx> OnUnimplementedDirective { && !is_diagnostic_namespace_variant { if let Some(parent_label_) = item.value_str() { - parent_label = parse_value(parent_label_)?; + parent_label = parse_value(parent_label_, item.span())?; continue; } } else if item.has_name(sym::on) @@ -474,7 +500,7 @@ impl<'tcx> OnUnimplementedDirective { && !is_diagnostic_namespace_variant { if let Some(msg) = item.value_str() { - append_const_msg = Some(AppendConstMessage::Custom(msg)); + append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); continue; } else if item.is_word() { append_const_msg = Some(AppendConstMessage::Default); @@ -523,6 +549,54 @@ impl<'tcx> OnUnimplementedDirective { subcommands.extend(directive.subcommands); let mut notes = aggr.notes; notes.extend(directive.notes); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.message.as_ref().map(|f| f.1), + aggr.message.as_ref().map(|f| f.1), + "message", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.label.as_ref().map(|f| f.1), + aggr.label.as_ref().map(|f| f.1), + "label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.condition.as_ref().map(|i| i.span), + aggr.condition.as_ref().map(|i| i.span), + "condition", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.parent_label.as_ref().map(|f| f.1), + aggr.parent_label.as_ref().map(|f| f.1), + "parent_label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + aggr.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + "append_const_msg", + ); + Ok(Some(Self { condition: aggr.condition.or(directive.condition), subcommands, @@ -560,6 +634,7 @@ impl<'tcx> OnUnimplementedDirective { item_def_id, value, attr.span, + attr.span, )?), notes: Vec::new(), parent_label: None, @@ -636,7 +711,13 @@ impl<'tcx> OnUnimplementedDirective { let value = cfg.value.map(|v| { // `with_no_visible_paths` is also used when generating the options, // so we need to match it here. - ty::print::with_no_visible_paths!(OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)) + ty::print::with_no_visible_paths!( + OnUnimplementedFormatString(v, cfg.span).format( + tcx, + trait_ref, + &options_map + ) + ) }); options.contains(&(cfg.name, value)) @@ -680,8 +761,9 @@ impl<'tcx> OnUnimplementedFormatString { item_def_id: DefId, from: Symbol, err_sp: Span, + value_span: Span, ) -> Result<Self, ErrorGuaranteed> { - let result = OnUnimplementedFormatString(from); + let result = OnUnimplementedFormatString(from, value_span); result.verify(tcx, item_def_id, err_sp)?; Ok(result) } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 262bed8610c..145b0cd0092 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -739,9 +739,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { real_trait_pred = parent_trait_pred; } - // We `erase_late_bound_regions` here because `make_subregion` does not handle + // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle // `ReBound`, and we don't particularly care about the regions. - let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty()); + let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); if !self.can_eq(obligation.param_env, real_ty, arg_ty) { continue; } @@ -2287,8 +2287,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // represent regions that are part of the suspended // coroutine frame. Bound regions are preserved by // `erase_regions` and so we must also call - // `erase_late_bound_regions`. - let ty_erased = self.tcx.erase_late_bound_regions(ty); + // `instantiate_bound_regions_with_erased`. + let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty); let ty_erased = self.tcx.erase_regions(ty_erased); let eq = ty_erased == target_ty_erased; debug!(?ty_erased, ?target_ty_erased, ?eq); @@ -2723,11 +2723,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .collect::<Vec<_>>(); if !impls.is_empty() { let len = impls.len(); - let mut types = impls.iter() - .map(|t| with_no_trimmed_paths!(format!( - " {}", - tcx.type_of(*t).instantiate_identity(), - ))) + let mut types = impls + .iter() + .map(|t| { + with_no_trimmed_paths!(format!( + " {}", + tcx.type_of(*t).instantiate_identity(), + )) + }) .collect::<Vec<_>>(); let post = if types.len() > 9 { types.truncate(8); @@ -2769,30 +2772,47 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { )); } } - ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => { + ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_type, + elt_span, + elt_stmt_span, + } => { err.note( "the `Copy` trait is required because this value will be copied for each element of the array", ); let value_kind = match is_constable { IsConstable::Fn => Some("the result of the function call"), IsConstable::Ctor => Some("the result of the constructor"), - _ => None + _ => None, }; let sm = tcx.sess.source_map(); - if let Some(value_kind) = value_kind && - let Ok(snip) = sm.span_to_snippet(elt_span) + if let Some(value_kind) = value_kind + && let Ok(snip) = sm.span_to_snippet(elt_span) { let help_msg = format!( "consider creating a new `const` item and initializing it with {value_kind} \ - to be used in the repeat position"); + to be used in the repeat position" + ); let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); - err.multipart_suggestion(help_msg, vec![ - (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")), - (elt_span, "ARRAY_REPEAT_VALUE".to_string()) - ], Applicability::MachineApplicable); + err.multipart_suggestion( + help_msg, + vec![ + ( + elt_stmt_span.shrink_to_lo(), + format!( + "const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}" + ), + ), + (elt_span, "ARRAY_REPEAT_VALUE".to_string()), + ], + Applicability::MachineApplicable, + ); } - if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) { + if self.tcx.sess.is_nightly_build() + && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) + { err.help( "create an inline `const` block, see RFC #2920 \ <https://github.com/rust-lang/rfcs/pull/2920> for more information", @@ -2957,7 +2977,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { let what = match self.tcx.coroutine_kind(coroutine_def_id) { - None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield", + None + | Some(hir::CoroutineKind::Coroutine) + | Some(hir::CoroutineKind::Gen(_)) => "yield", Some(hir::CoroutineKind::Async(..)) => "await", }; err.note(format!( @@ -3352,7 +3374,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); let impls_future = self.type_implements_trait( future_trait, - [self.tcx.erase_late_bound_regions(self_ty)], + [self.tcx.instantiate_bound_regions_with_erased(self_ty)], obligation.param_env, ); if !impls_future.must_apply_modulo_regions() { @@ -3519,7 +3541,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { let inner_expr = expr.peel_blocks(); - let ty = typeck_results.expr_ty_adjusted_opt(inner_expr) + let ty = typeck_results + .expr_ty_adjusted_opt(inner_expr) .unwrap_or(Ty::new_misc_error(tcx)); let span = inner_expr.span; if Some(span) != err.span.primary_span() { @@ -3538,14 +3561,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { tcx.lang_items().fn_once_trait(), tcx.lang_items().fn_mut_trait(), tcx.lang_items().fn_trait(), - ].contains(&Some(pred.def_id())) + ] + .contains(&Some(pred.def_id())) { if let [stmt, ..] = block.stmts && let hir::StmtKind::Semi(value) = stmt.kind && let hir::ExprKind::Closure(hir::Closure { - body, - fn_decl_span, - .. + body, fn_decl_span, .. }) = value.kind && let body = hir.body(*body) && !matches!(body.value.kind, hir::ExprKind::Block(..)) @@ -3568,9 +3590,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "you might have meant to create the closure instead of a block", format!( "|{}| ", - (0..pred.trait_ref.args.len() - 1).map(|_| "_") + (0..pred.trait_ref.args.len() - 1) + .map(|_| "_") .collect::<Vec<_>>() - .join(", ")), + .join(", ") + ), Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index aa284cfc9c5..5239725f509 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -928,10 +928,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool { - if let ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id, - .. - } = obligation.cause.code() + if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = + obligation.cause.code() && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) && let arg = arg.peel_borrows() && let hir::ExprKind::Path(hir::QPath::Resolved( @@ -2731,7 +2729,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(format!("{cannot_do_this} in const contexts")) } // overridden post message - (true, Some(AppendConstMessage::Custom(custom_msg))) => { + (true, Some(AppendConstMessage::Custom(custom_msg, _))) => { Some(format!("{cannot_do_this}{custom_msg}")) } // fallback to generic message @@ -2752,7 +2750,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { use rustc_transmute::Answer; // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref)); + let trait_ref = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); let src_and_dst = rustc_transmute::Types { dst: trait_ref.args.type_at(0), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fff5510bbfb..2c004c65929 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -484,7 +484,7 @@ fn is_impossible_associated_item( t.super_visit_with(self) } fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ReEarlyBound(param) = r.kind() + if let ty::ReEarlyParam(param) = r.kind() && let param_def_id = self.generics.region_param(¶m, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 32bbd626d4e..1c2966bb3e5 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { }; let mut constraints = QueryRegionConstraints::default(); - let Ok(InferOk { value, obligations }) = self + let Ok(InferOk { value: mut bounds, obligations }) = self .instantiate_nll_query_response_and_region_obligations( &ObligationCause::dummy(), param_env, @@ -85,6 +85,10 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { }; assert_eq!(&obligations, &[]); + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + bounds.retain(|bound| !bound.has_placeholders()); + if !constraints.is_empty() { let span = self.tcx.def_span(body_id); @@ -114,7 +118,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { } }; - value + bounds } fn implied_bounds_tys( 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 f4b6d3bcfda..1529f736109 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -375,7 +375,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // consider a "quick reject". This avoids creating more types // and so forth that we need to. let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - if !drcx.args_refs_may_unify(obligation_args, impl_trait_ref.skip_binder().args) { + if !drcx.args_may_unify(obligation_args, impl_trait_ref.skip_binder().args) { return; } if self.reject_fn_ptr_impls( @@ -1051,7 +1051,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { // The regions of a type don't affect the size of the type let tcx = self.tcx(); - let self_ty = tcx.erase_late_bound_regions(obligation.predicate.self_ty()); + let self_ty = tcx.instantiate_bound_regions_with_erased(obligation.predicate.self_ty()); // We should erase regions from both the param-env and type, since both // may have infer regions. Specifically, after canonicalizing and instantiating, // early bound regions turn into region vars in both the new and old solver. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index abb4e71a9af..2ab3ecbd5a3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -327,8 +327,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // care about other regions. Erasing late-bound regions is equivalent // to instantiating the binder with placeholders then erasing those // placeholder regions. - let predicate = - self.tcx().erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate)); + let predicate = self + .tcx() + .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate)); let Some(assume) = rustc_transmute::Assume::from_const( self.infcx.tcx, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index fe5b625e483..9751cb60ed8 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -913,20 +913,15 @@ pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Vec<ty::Region<'tcx>> { - // Since we don't actually *know* the self type for an object, - // this "open(err)" serves as a kind of dummy standin -- basically - // a placeholder type. - let open_ty = Ty::new_fresh(tcx, 0); - let predicates = existential_predicates.iter().filter_map(|predicate| { if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { None } else { - Some(predicate.with_self_ty(tcx, open_ty)) + Some(predicate.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) } }); - required_region_bounds(tcx, open_ty, predicates) + required_region_bounds(tcx, tcx.types.trait_object_dummy_self, predicates) } /// Given a set of predicates that apply to an object type, returns diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 2563e3ed1a3..cb2a36cb998 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -4,18 +4,12 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Normalized, ObligationCause}; -use std::sync::atomic::Ordering; pub(crate) fn provide(p: &mut Providers) { *p = Providers { try_normalize_generic_arg_after_erasing_regions: |tcx, goal| { debug!("try_normalize_generic_arg_after_erasing_regions(goal={:#?}", goal); - tcx.sess - .perf_stats - .normalize_generic_arg_after_erasing_regions - .fetch_add(1, Ordering::Relaxed); - try_normalize_after_erasing_regions(tcx, goal) }, ..*p diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 01bb1ca70eb..b8c71bc96f8 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -10,7 +10,6 @@ use rustc_trait_selection::traits::query::{ use rustc_trait_selection::traits::{ self, FulfillmentErrorCode, ObligationCause, SelectionContext, }; -use std::sync::atomic::Ordering; pub(crate) fn provide(p: &mut Providers) { *p = Providers { @@ -27,7 +26,6 @@ fn normalize_projection_ty<'tcx>( ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { debug!("normalize_provider(goal={:#?})", goal); - tcx.sess.perf_stats.normalize_projection_ty.fetch_add(1, Ordering::Relaxed); tcx.infer_ctxt().enter_canonical_trait_query( &goal, |ocx, ParamEnvAnd { param_env, value: goal }| { @@ -78,7 +76,6 @@ fn normalize_weak_ty<'tcx>( ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { debug!("normalize_provider(goal={:#?})", goal); - tcx.sess.perf_stats.normalize_projection_ty.fetch_add(1, Ordering::Relaxed); tcx.infer_ctxt().enter_canonical_trait_query( &goal, |ocx, ParamEnvAnd { param_env, value: goal }| { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 3e140793b5a..ffeeae66858 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -43,7 +43,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { trait_fn_def_id, ) }) - .map(|def_id| *def_id), + .copied(), ), ) } @@ -69,7 +69,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { impl_fn_def_id, ) }) - .map(|def_id| *def_id) + .copied() })), ) } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 5c34df1ed50..24afd7dc357 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -53,9 +53,9 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' DefKind::AssocTy if let Some(data) = tcx.opt_rpitit_info(def_id.to_def_id()) => { match data { ty::ImplTraitInTraitData::Trait { fn_def_id, .. } => { - // We need to remap all of the late-bound lifetimes in theassumed wf types - // of the fn (which are represented as ReFree) to the early-bound lifetimes - // of the RPITIT (which are represented by ReEarlyBound owned by the opaque). + // We need to remap all of the late-bound lifetimes in the assumed wf types + // of the fn (which are represented as ReLateParam) to the early-bound lifetimes + // of the RPITIT (which are represented by ReEarlyParam owned by the opaque). // Luckily, this is very easy to do because we already have that mapping // stored in the HIR of this RPITIT. // @@ -65,19 +65,19 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' let mut mapping = FxHashMap::default(); let generics = tcx.generics_of(def_id); - // For each captured opaque lifetime, if it's late-bound (`ReFree` in this case, - // since it has been liberated), map it back to the early-bound lifetime of + // For each captured opaque lifetime, if it's late-bound (`ReLateParam` in this + // case, since it has been liberated), map it back to the early-bound lifetime of // the GAT. Since RPITITs also have all of the fn's generics, we slice only // the end of the list corresponding to the opaque's generics. for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] { let orig_lt = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); - if matches!(*orig_lt, ty::ReFree(..)) { + if matches!(*orig_lt, ty::ReLateParam(..)) { mapping.insert( orig_lt, - ty::Region::new_early_bound( + ty::Region::new_early_param( tcx, - ty::EarlyBoundRegion { + ty::EarlyParamRegion { def_id: param.def_id, index: param.index, name: param.name, @@ -90,7 +90,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' let remapped_wf_tys = tcx.fold_regions( tcx.assumed_wf_types(fn_def_id.expect_local()).to_vec(), |region, _| { - // If `region` is a `ReFree` that is captured by the + // If `region` is a `ReLateParam` that is captured by the // opaque, remap it to its corresponding the early- // bound region. if let Some(remapped_region) = mapping.get(®ion) { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 283862b5e1c..52f723eba80 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -740,11 +740,11 @@ fn coroutine_layout<'tcx>( }; let tag_layout = cx.tcx.mk_layout(LayoutS::scalar(cx, tag)); - let promoted_layouts = ineligible_locals - .iter() - .map(|local| subst_field(info.field_tys[local].ty)) - .map(|ty| Ty::new_maybe_uninit(tcx, ty)) - .map(|ty| Ok(cx.layout_of(ty)?.layout)); + let promoted_layouts = ineligible_locals.iter().map(|local| { + let field_ty = subst_field(info.field_tys[local].ty); + let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty); + Ok(cx.spanned_layout_of(uninit_ty, info.field_tys[local].source_info.span)?.layout) + }); let prefix_layouts = args .as_coroutine() .prefix_tys() diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index dabe25589a0..8de058f02c9 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -5,9 +5,9 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), allow(internal_features))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(iterator_try_collect)] diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 9242a1a751b..7ca2da42042 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -159,10 +159,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> { // Only check that the parent generics of the TAIT/RPIT are unique. // the args owned by the opaque are going to always be duplicate // lifetime params for RPITs, and empty for TAITs. - match self - .tcx - .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::Bound) - { + match self.tcx.uses_unique_generic_params( + &alias_ty.args[..parent_count], + CheckRegions::FromFunction, + ) { Ok(()) => { // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not // supported at all, so this is sound to do, but once we want to support them, you'll diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 2b262cfdebd..485bde735d8 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -203,11 +203,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> { "we shouldn't walk non-predicate binders with `impl Trait`...", ); } - ty::Region::new_bound( - self.tcx, - index.shifted_out_to_binder(self.depth), - bv, - ) + ty::Region::new_bound(self.tcx, index.shifted_out_to_binder(self.depth), bv) } else { re } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index d10927b0d93..af741a0a3a0 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -8,7 +8,7 @@ bitflags! { // required. /// Does this have `Param`? const HAS_TY_PARAM = 1 << 0; - /// Does this have `ReEarlyBound`? + /// Does this have `ReEarlyParam`? const HAS_RE_PARAM = 1 << 1; /// Does this have `ConstKind::Param`? const HAS_CT_PARAM = 1 << 2; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 7f75e5b35a2..da504c54fdd 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -47,9 +47,9 @@ pub trait Interner: Sized { // Kinds of regions type Region: Clone + DebugWithInfcx<Self> + Hash + Ord; - type EarlyBoundRegion: Clone + Debug + Hash + Ord; + type EarlyParamRegion: Clone + Debug + Hash + Ord; type BoundRegion: Clone + Debug + Hash + Ord; - type FreeRegion: Clone + Debug + Hash + Ord; + type LateParamRegion: Clone + Debug + Hash + Ord; type InferRegion: Clone + DebugWithInfcx<Self> + Hash + Ord; type PlaceholderRegion: Clone + Debug + Hash + Ord; diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index fc9eaf63ac8..5d3ce49bc8f 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -22,8 +22,8 @@ use self::RegionKind::*; /// ```text /// static ----------+-----...------+ (greatest) /// | | | -/// early-bound and | | -/// free regions | | +/// param regions | | +/// | | | /// | | | /// | | | /// empty(root) placeholder(U1) | @@ -88,8 +88,8 @@ use self::RegionKind::*; /// To do this, we replace the bound regions with placeholder markers, /// which don't satisfy any relation not explicitly provided. /// -/// There are two kinds of placeholder regions in rustc: `ReFree` and -/// `RePlaceholder`. When checking an item's body, `ReFree` is supposed +/// There are two kinds of placeholder regions in rustc: `ReLateParam` and +/// `RePlaceholder`. When checking an item's body, `ReLateParam` is supposed /// to be used. These also support explicit bounds: both the internally-stored /// *scope*, which the region is assumed to outlive, as well as other /// relations stored in the `FreeRegionMap`. Note that these relations @@ -123,26 +123,35 @@ use self::RegionKind::*; )] #[derive(TyEncodable, TyDecodable)] pub enum RegionKind<I: Interner> { - /// Region bound in a type or fn declaration which will be - /// substituted 'early' -- that is, at the same time when type - /// parameters are substituted. - ReEarlyBound(I::EarlyBoundRegion), + /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`. + /// + /// There are some important differences between region and type parameters. + /// Not all region parameters in the source are represented via `ReEarlyParam`: + /// late-bound function parameters are instead lowered to a `ReBound`. Late-bound + /// regions get eagerly replaced with `ReLateParam` which behaves in the same way as + /// `ReEarlyParam`. Region parameters are also sometimes implicit, + /// e.g. in `impl Trait for &()`. + ReEarlyParam(I::EarlyParamRegion), /// A higher-ranked region. These represent either late-bound function parameters /// or bound variables from a `for<'a>`-binder. /// /// While inside of a function, e.g. during typeck, the late-bound function parameters - /// can be converted to `ReFree` by calling `tcx.liberate_late_bound_regions`. + /// can be converted to `ReLateParam` by calling `tcx.liberate_late_bound_regions`. /// /// Bound regions inside of types **must not** be erased, as they impact trait /// selection and the `TypeId` of that type. `for<'a> fn(&'a ())` and /// `fn(&'static ())` are different types and have to be treated as such. ReBound(DebruijnIndex, I::BoundRegion), - /// When checking a function body, the types of all arguments and so forth - /// that refer to bound region parameters are modified to refer to free - /// region parameters. - ReFree(I::FreeRegion), + /// Late-bound function parameters are represented using a `ReBound`. When + /// inside of a function, we convert these bound variables to placeholder + /// parameters via `tcx.liberate_late_bound_regions`. They are then treated + /// the same way as `ReEarlyParam` while inside of the function. + /// + /// See <https://rustc-dev-guide.rust-lang.org/early-late-bound-summary.html> for + /// more info about early and late bound lifetime parameters. + ReLateParam(I::LateParamRegion), /// Static data that has an "infinite" lifetime. Top in the region lattice. ReStatic, @@ -150,8 +159,11 @@ pub enum RegionKind<I: Interner> { /// A region variable. Should not exist outside of type inference. ReVar(I::InferRegion), - /// A placeholder region -- basically, the higher-ranked version of `ReFree`. + /// A placeholder region -- the higher-ranked version of `ReLateParam`. /// Should not exist outside of type inference. + /// + /// Used when instantiating a `forall` binder via + /// `infcx.instantiate_binder_with_placeholders`. RePlaceholder(I::PlaceholderRegion), /// Erased region, used by trait selection, in MIR and during codegen. @@ -166,9 +178,9 @@ pub enum RegionKind<I: Interner> { #[inline] const fn regionkind_discriminant<I: Interner>(value: &RegionKind<I>) -> usize { match value { - ReEarlyBound(_) => 0, + ReEarlyParam(_) => 0, ReBound(_, _) => 1, - ReFree(_) => 2, + ReLateParam(_) => 2, ReStatic => 3, ReVar(_) => 4, RePlaceholder(_) => 5, @@ -180,9 +192,9 @@ const fn regionkind_discriminant<I: Interner>(value: &RegionKind<I>) -> usize { // This is manually implemented because a derive would require `I: Copy` impl<I: Interner> Copy for RegionKind<I> where - I::EarlyBoundRegion: Copy, + I::EarlyParamRegion: Copy, I::BoundRegion: Copy, - I::FreeRegion: Copy, + I::LateParamRegion: Copy, I::InferRegion: Copy, I::PlaceholderRegion: Copy, I::ErrorGuaranteed: Copy, @@ -195,9 +207,9 @@ impl<I: Interner> PartialEq for RegionKind<I> { fn eq(&self, other: &RegionKind<I>) -> bool { regionkind_discriminant(self) == regionkind_discriminant(other) && match (self, other) { - (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r == b_r, + (ReEarlyParam(a_r), ReEarlyParam(b_r)) => a_r == b_r, (ReBound(a_d, a_r), ReBound(b_d, b_r)) => a_d == b_d && a_r == b_r, - (ReFree(a_r), ReFree(b_r)) => a_r == b_r, + (ReLateParam(a_r), ReLateParam(b_r)) => a_r == b_r, (ReStatic, ReStatic) => true, (ReVar(a_r), ReVar(b_r)) => a_r == b_r, (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r, @@ -223,13 +235,13 @@ impl<I: Interner> DebugWithInfcx<I> for RegionKind<I> { f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result { match this.data { - ReEarlyBound(data) => write!(f, "ReEarlyBound({data:?})"), + ReEarlyParam(data) => write!(f, "ReEarlyParam({data:?})"), ReBound(binder_id, bound_region) => { write!(f, "ReBound({binder_id:?}, {bound_region:?})") } - ReFree(fr) => write!(f, "{fr:?}"), + ReLateParam(fr) => write!(f, "{fr:?}"), ReStatic => f.write_str("ReStatic"), @@ -252,9 +264,9 @@ impl<I: Interner> fmt::Debug for RegionKind<I> { // This is not a derived impl because a derive would require `I: HashStable` impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for RegionKind<I> where - I::EarlyBoundRegion: HashStable<CTX>, + I::EarlyParamRegion: HashStable<CTX>, I::BoundRegion: HashStable<CTX>, - I::FreeRegion: HashStable<CTX>, + I::LateParamRegion: HashStable<CTX>, I::InferRegion: HashStable<CTX>, I::PlaceholderRegion: HashStable<CTX>, { @@ -269,10 +281,10 @@ where d.hash_stable(hcx, hasher); r.hash_stable(hcx, hasher); } - ReEarlyBound(r) => { + ReEarlyParam(r) => { r.hash_stable(hcx, hasher); } - ReFree(r) => { + ReLateParam(r) => { r.hash_stable(hcx, hasher); } RePlaceholder(r) => { diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index f316671b278..79102dcce35 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -19,9 +19,9 @@ use crate::mir::mono::InstanceDef; use crate::mir::Body; -use std::cell::Cell; use std::fmt; use std::fmt::Debug; +use std::{cell::Cell, io}; use self::ty::{ GenericPredicates, Generics, ImplDef, ImplTrait, IndexedVal, LineInfo, Span, TraitDecl, @@ -36,9 +36,12 @@ pub mod mir; pub mod ty; pub mod visitor; +use crate::mir::pretty::function_name; +use crate::mir::Mutability; +use crate::ty::{AdtDef, AdtKind, ClosureDef, ClosureKind}; pub use error::*; use mir::mono::Instance; -use ty::{FnDef, GenericArgs}; +use ty::{Const, FnDef, GenericArgs}; /// Use String for now but we should replace it. pub type Symbol = String; @@ -47,7 +50,7 @@ pub type Symbol = String; pub type CrateNum = usize; /// A unique identification number for each item accessible for the current compilation unit. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct DefId(usize); impl Debug for DefId { @@ -99,7 +102,13 @@ pub struct Crate { pub is_local: bool, } -pub type DefKind = Opaque; +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum ItemKind { + Fn, + Static, + Const, +} + pub type Filename = Opaque; /// Holds information about an item in the crate. @@ -119,13 +128,22 @@ impl CrateItem { with(|cx| cx.name_of_def_id(self.0)) } - pub fn kind(&self) -> DefKind { - with(|cx| cx.def_kind(self.0)) + pub fn kind(&self) -> ItemKind { + with(|cx| cx.item_kind(*self)) } pub fn requires_monomorphization(&self) -> bool { with(|cx| cx.requires_monomorphization(self.0)) } + + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } + + pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> { + writeln!(w, "{}", function_name(*self))?; + self.body().dump(w) + } } /// Return the function where execution starts if the current @@ -204,7 +222,16 @@ pub trait Context { fn get_lines(&self, span: &Span) -> LineInfo; /// Returns the `kind` of given `DefId` - fn def_kind(&self, def_id: DefId) -> DefKind; + fn item_kind(&self, item: CrateItem) -> ItemKind; + + /// Returns the kind of a given algebraic data type + fn adt_kind(&self, def: AdtDef) -> AdtKind; + + /// Returns the type of given crate item. + fn def_ty(&self, item: DefId) -> Ty; + + /// Returns literal value of a const as a string. + fn const_literal(&self, cnst: &Const) -> String; /// `Span` of an item fn span_of_an_item(&self, def_id: DefId) -> Span; @@ -214,7 +241,7 @@ pub trait Context { /// Get the body of an Instance. /// FIXME: Monomorphize the body. - fn instance_body(&self, instance: InstanceDef) -> Body; + fn instance_body(&self, instance: InstanceDef) -> Option<Body>; /// Get the instance type with generic substitutions applied and lifetimes erased. fn instance_ty(&self, instance: InstanceDef) -> Ty; @@ -234,18 +261,36 @@ pub trait Context { /// Resolve an instance from the given function definition and generic arguments. fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option<Instance>; + + /// Resolve an instance for drop_in_place for the given type. + fn resolve_drop_in_place(&self, ty: Ty) -> Instance; + + /// Resolve instance for a function pointer. + fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option<Instance>; + + /// Resolve instance for a closure with the requested type. + fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option<Instance>; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt // datastructures and stable MIR datastructures scoped_thread_local! (static TLV: Cell<*const ()>); -pub fn run(context: &dyn Context, f: impl FnOnce()) { - assert!(!TLV.is_set()); - let ptr: *const () = &context as *const &_ as _; - TLV.set(&Cell::new(ptr), || { - f(); - }); +pub fn run<F, T>(context: &dyn Context, f: F) -> Result<T, Error> +where + F: FnOnce() -> T, +{ + if TLV.is_set() { + Err(Error::from("StableMIR already running")) + } else { + let ptr: *const () = &context as *const &_ as _; + TLV.set(&Cell::new(ptr), || Ok(f())) + } } /// Loads the current context and calls a function with it. @@ -260,7 +305,7 @@ pub fn with<R>(f: impl FnOnce(&dyn Context) -> R) -> R { } /// A type that provides internal information but that can still be used for debug purpose. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Opaque(String); impl std::fmt::Display for Opaque { diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs index 2e1714b49c1..2cbe6eb4ad1 100644 --- a/compiler/stable_mir/src/mir.rs +++ b/compiler/stable_mir/src/mir.rs @@ -1,5 +1,6 @@ mod body; pub mod mono; +pub mod pretty; pub mod visit; pub use body::*; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 06933783685..fa58a7ffe15 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,7 +1,8 @@ +use crate::mir::pretty::{function_body, pretty_statement}; use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, Ty}; use crate::Opaque; use crate::Span; - +use std::io; /// The SMIR representation of a single function. #[derive(Clone, Debug)] pub struct Body { @@ -56,6 +57,28 @@ impl Body { pub fn locals(&self) -> &[LocalDecl] { &self.locals } + + pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> { + writeln!(w, "{}", function_body(self))?; + self.blocks + .iter() + .enumerate() + .map(|(index, block)| -> io::Result<()> { + writeln!(w, " bb{}: {{", index)?; + let _ = block + .statements + .iter() + .map(|statement| -> io::Result<()> { + writeln!(w, "{}", pretty_statement(&statement.kind))?; + Ok(()) + }) + .collect::<Vec<_>>(); + writeln!(w, " }}").unwrap(); + Ok(()) + }) + .collect::<Result<Vec<_>, _>>()?; + Ok(()) + } } type LocalDecls = Vec<LocalDecl>; @@ -64,6 +87,7 @@ type LocalDecls = Vec<LocalDecl>; pub struct LocalDecl { pub ty: Ty, pub span: Span, + pub mutability: Mutability, } #[derive(Clone, Debug)] @@ -398,22 +422,128 @@ pub enum Operand { pub struct Place { pub local: Local, /// projection out of a place (access a field, deref a pointer, etc) - pub projection: String, + pub projection: Vec<ProjectionElem>, +} + +// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This +// is so it can be used for both Places (for which the projection elements are of type +// ProjectionElem<Local, Ty>) and user-provided type annotations (for which the projection elements +// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use +// ProjectionElem for Places. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ProjectionElem { + /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. + Deref, + + /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is + /// referenced by source-order index rather than the name of the field. The fields type is also + /// given. + Field(FieldIdx, Ty), + + /// Index into a slice/array. The value of the index is computed at runtime using the `V` + /// argument. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. + Index(Local), + + /// Index into a slice/array given by offsets. + /// + /// These indices are generated by slice patterns. Easiest to explain by example: + /// + /// ```ignore (illustrative) + /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, + /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, + /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, + /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, + /// ``` + ConstantIndex { + /// index or -index (in Python terms), depending on from_end + offset: u64, + /// The thing being indexed must be at least this long. For arrays this + /// is always the exact length. + min_length: u64, + /// Counting backwards from end? This is always false when indexing an + /// array. + from_end: bool, + }, + + /// Projects a slice from the base place. + /// + /// These indices are generated by slice patterns. If `from_end` is true, this represents + /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`. + Subslice { + from: u64, + to: u64, + /// Whether `to` counts from the start or end of the array/slice. + from_end: bool, + }, + + /// "Downcast" to a variant of an enum or a coroutine. + Downcast(VariantIdx), + + /// Like an explicit cast from an opaque type to a concrete type, but without + /// requiring an intermediate variable. + OpaqueCast(Ty), + + /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where + /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping + /// explicit during optimizations and codegen. + /// + /// This projection doesn't impact the runtime behavior of the program except for potentially changing + /// some type metadata of the interpreter or codegen backend. + Subtype(Ty), } #[derive(Clone, Debug, Eq, PartialEq)] pub struct UserTypeProjection { pub base: UserTypeAnnotationIndex, - pub projection: String, + + pub projection: Opaque, } pub type Local = usize; pub const RETURN_LOCAL: Local = 0; +/// The source-order index of a field in a variant. +/// +/// For example, in the following types, +/// ```ignore(illustrative) +/// enum Demo1 { +/// Variant0 { a: bool, b: i32 }, +/// Variant1 { c: u8, d: u64 }, +/// } +/// struct Demo2 { e: u8, f: u16, g: u8 } +/// ``` +/// `a`'s `FieldIdx` is `0`, +/// `b`'s `FieldIdx` is `1`, +/// `c`'s `FieldIdx` is `0`, and +/// `g`'s `FieldIdx` is `2`. type FieldIdx = usize; /// The source-order index of a variant in a type. +/// +/// For example, in the following types, +/// ```ignore(illustrative) +/// enum Demo1 { +/// Variant0 { a: bool, b: i32 }, +/// Variant1 { c: u8, d: u64 }, +/// } +/// struct Demo2 { e: u8, f: u16, g: u8 } +/// ``` +/// `a` is in the variant with the `VariantIdx` of `0`, +/// `c` is in the variant with the `VariantIdx` of `1`, and +/// `g` is in the variant with the `VariantIdx` of `0`. pub type VariantIdx = usize; type UserTypeAnnotationIndex = usize; @@ -536,6 +666,10 @@ impl Constant { } impl Place { + // FIXME(klinvill): This function is expected to resolve down the chain of projections to get + // the type referenced at the end of it. E.g. calling `ty()` on `*(_1.f)` should end up + // returning the type referenced by `f`. The information needed to do this may not currently be + // present in Stable MIR since at least an implementation for AdtDef is probably needed. pub fn ty(&self, locals: &[LocalDecl]) -> Ty { let _start_ty = locals[self.local].ty; todo!("Implement projection") diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 8f533349848..8562bfd3905 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -1,16 +1,16 @@ use crate::mir::Body; -use crate::ty::{FnDef, GenericArgs, IndexedVal, Ty}; -use crate::{with, CrateItem, DefId, Error, Opaque}; +use crate::ty::{ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; +use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque}; use std::fmt::Debug; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum MonoItem { Fn(Instance), Static(StaticDef), GlobalAsm(Opaque), } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Instance { /// The type of instance. pub kind: InstanceKind, @@ -19,7 +19,7 @@ pub struct Instance { pub def: InstanceDef, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InstanceKind { /// A user defined item. Item, @@ -33,7 +33,7 @@ pub enum InstanceKind { impl Instance { /// Get the body of an Instance. The body will be eagerly monomorphized. - pub fn body(&self) -> Body { + pub fn body(&self) -> Option<Body> { with(|context| context.instance_body(self.def)) } @@ -54,6 +54,33 @@ impl Instance { }) }) } + + /// Resolve the drop in place for a given type. + pub fn resolve_drop_in_place(ty: Ty) -> Instance { + with(|cx| cx.resolve_drop_in_place(ty)) + } + + /// Resolve an instance for a given function pointer. + pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> { + with(|context| { + context.resolve_for_fn_ptr(def, args).ok_or_else(|| { + crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) + }) + }) + } + + /// Resolve a closure with the expected kind. + pub fn resolve_closure( + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Result<Instance, crate::Error> { + with(|context| { + context.resolve_closure(def, args, kind).ok_or_else(|| { + crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) + }) + }) + } } /// Try to convert a crate item into an instance. @@ -86,12 +113,36 @@ impl TryFrom<Instance> for CrateItem { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +impl From<Instance> for MonoItem { + fn from(value: Instance) -> Self { + MonoItem::Fn(value) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct InstanceDef(usize); -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct StaticDef(pub DefId); +impl TryFrom<CrateItem> for StaticDef { + type Error = crate::Error; + + fn try_from(value: CrateItem) -> Result<Self, Self::Error> { + if matches!(value.kind(), ItemKind::Static | ItemKind::Const) { + Ok(StaticDef(value.0)) + } else { + Err(Error::new(format!("Expected a static item, but found: {value:?}"))) + } + } +} + +impl StaticDef { + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } +} + impl IndexedVal for InstanceDef { fn to_val(index: usize) -> Self { InstanceDef(index) diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs new file mode 100644 index 00000000000..e52c3360ce4 --- /dev/null +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -0,0 +1,258 @@ +use crate::mir::{Operand, Rvalue, StatementKind}; +use crate::ty::{DynKind, FloatTy, IntTy, RigidTy, TyKind, UintTy}; +use crate::{with, Body, CrateItem, Mutability}; + +pub fn function_name(item: CrateItem) -> String { + let mut pretty_name = String::new(); + let body = item.body(); + pretty_name.push_str("fn "); + pretty_name.push_str(item.name().as_str()); + if body.arg_locals().is_empty() { + pretty_name.push_str("()"); + } else { + pretty_name.push_str("("); + } + body.arg_locals().iter().enumerate().for_each(|(index, local)| { + pretty_name.push_str(format!("_{}: ", index).as_str()); + pretty_name.push_str(&pretty_ty(local.ty.kind())); + }); + if !body.arg_locals().is_empty() { + pretty_name.push_str(")"); + } + let return_local = body.ret_local(); + pretty_name.push_str(" -> "); + pretty_name.push_str(&pretty_ty(return_local.ty.kind())); + pretty_name.push_str(" {"); + pretty_name +} + +pub fn function_body(body: &Body) -> String { + let mut pretty_body = String::new(); + body.inner_locals().iter().enumerate().for_each(|(index, local)| { + pretty_body.push_str(" "); + pretty_body.push_str(format!("let {}", ret_mutability(&local.mutability)).as_str()); + pretty_body.push_str(format!("_{}: ", index).as_str()); + pretty_body.push_str(format!("{}", pretty_ty(local.ty.kind())).as_str()); + pretty_body.push_str(";\n"); + }); + pretty_body.push_str("}"); + pretty_body +} + +pub fn ret_mutability(mutability: &Mutability) -> String { + match mutability { + Mutability::Not => "".to_string(), + Mutability::Mut => "mut ".to_string(), + } +} + +pub fn pretty_statement(statement: &StatementKind) -> String { + let mut pretty = String::new(); + match statement { + StatementKind::Assign(place, rval) => { + pretty.push_str(format!(" _{} = ", place.local).as_str()); + pretty.push_str(format!("{}", &pretty_rvalue(rval)).as_str()); + } + StatementKind::FakeRead(_, _) => todo!(), + StatementKind::SetDiscriminant { .. } => todo!(), + StatementKind::Deinit(_) => todo!(), + StatementKind::StorageLive(_) => todo!(), + StatementKind::StorageDead(_) => todo!(), + StatementKind::Retag(_, _) => todo!(), + StatementKind::PlaceMention(_) => todo!(), + StatementKind::AscribeUserType { .. } => todo!(), + StatementKind::Coverage(_) => todo!(), + StatementKind::Intrinsic(_) => todo!(), + StatementKind::ConstEvalCounter => (), + StatementKind::Nop => (), + } + pretty +} + +pub fn pretty_operand(operand: &Operand) -> String { + let mut pretty = String::new(); + match operand { + Operand::Copy(copy) => { + pretty.push_str(""); + pretty.push_str(format!("{}", copy.local).as_str()); + } + Operand::Move(mv) => { + pretty.push_str("move "); + pretty.push_str(format!("_{}", mv.local).as_str()); + } + Operand::Constant(cnst) => { + pretty.push_str("const "); + pretty.push_str(with(|cx| cx.const_literal(&cnst.literal)).as_str()); + } + } + pretty +} + +pub fn pretty_rvalue(rval: &Rvalue) -> String { + let mut pretty = String::new(); + match rval { + Rvalue::AddressOf(muta, addr) => { + pretty.push_str("&raw "); + pretty.push_str(&ret_mutability(&muta)); + pretty.push_str(format!("(*_{})", addr.local).as_str()); + } + Rvalue::Aggregate(aggregatekind, operands) => { + pretty.push_str(format!("{:#?}", aggregatekind).as_str()); + pretty.push_str("("); + operands.iter().enumerate().for_each(|(i, op)| { + pretty.push_str(&pretty_operand(op)); + if i != operands.len() - 1 { + pretty.push_str(", "); + } + }); + pretty.push_str(")"); + } + Rvalue::BinaryOp(bin, op, op2) => { + pretty.push_str(&pretty_operand(op)); + pretty.push_str(" "); + pretty.push_str(format!("{:#?}", bin).as_str()); + pretty.push_str(" "); + pretty.push_str(&pretty_operand(op2)); + } + Rvalue::Cast(_, op, ty) => { + pretty.push_str(&pretty_operand(op)); + pretty.push_str(" as "); + pretty.push_str(&pretty_ty(ty.kind())); + } + Rvalue::CheckedBinaryOp(bin, op1, op2) => { + pretty.push_str(&pretty_operand(op1)); + pretty.push_str(" "); + pretty.push_str(format!("{:#?}", bin).as_str()); + pretty.push_str(" "); + pretty.push_str(&pretty_operand(op2)); + } + Rvalue::CopyForDeref(deref) => { + pretty.push_str("CopyForDeref"); + pretty.push_str(format!("{}", deref.local).as_str()); + } + Rvalue::Discriminant(place) => { + pretty.push_str("discriminant"); + pretty.push_str(format!("{}", place.local).as_str()); + } + Rvalue::Len(len) => { + pretty.push_str("len"); + pretty.push_str(format!("{}", len.local).as_str()); + } + Rvalue::Ref(_, borrowkind, place) => { + pretty.push_str("ref"); + pretty.push_str(format!("{:#?}", borrowkind).as_str()); + pretty.push_str(format!("{}", place.local).as_str()); + } + Rvalue::Repeat(op, cnst) => { + pretty.push_str(&pretty_operand(op)); + pretty.push_str(" "); + pretty.push_str(&pretty_ty(cnst.ty().kind())); + } + Rvalue::ShallowInitBox(_, _) => todo!(), + Rvalue::ThreadLocalRef(item) => { + pretty.push_str("thread_local_ref"); + pretty.push_str(format!("{:#?}", item).as_str()); + } + Rvalue::NullaryOp(nul, ty) => { + pretty.push_str(format!("{:#?}", nul).as_str()); + pretty.push_str(&&pretty_ty(ty.kind())); + pretty.push_str(" "); + } + Rvalue::UnaryOp(un, op) => { + pretty.push_str(&pretty_operand(op)); + pretty.push_str(" "); + pretty.push_str(format!("{:#?}", un).as_str()); + } + Rvalue::Use(op) => pretty.push_str(&pretty_operand(op)), + } + pretty +} + +pub fn pretty_ty(ty: TyKind) -> String { + let mut pretty = String::new(); + pretty.push_str(""); + match ty { + TyKind::RigidTy(rigid_ty) => match rigid_ty { + RigidTy::Bool => "bool".to_string(), + RigidTy::Char => "char".to_string(), + RigidTy::Int(i) => match i { + IntTy::Isize => "isize".to_string(), + IntTy::I8 => "i8".to_string(), + IntTy::I16 => "i16".to_string(), + IntTy::I32 => "i32".to_string(), + IntTy::I64 => "i64".to_string(), + IntTy::I128 => "i128".to_string(), + }, + RigidTy::Uint(u) => match u { + UintTy::Usize => "usize".to_string(), + UintTy::U8 => "u8".to_string(), + UintTy::U16 => "u16".to_string(), + UintTy::U32 => "u32".to_string(), + UintTy::U64 => "u64".to_string(), + UintTy::U128 => "u128".to_string(), + }, + RigidTy::Float(f) => match f { + FloatTy::F32 => "f32".to_string(), + FloatTy::F64 => "f64".to_string(), + }, + RigidTy::Adt(def, _) => { + format!("{:#?}", with(|cx| cx.def_ty(def.0))) + } + RigidTy::Str => "str".to_string(), + RigidTy::Array(ty, len) => { + format!("[{}; {}]", pretty_ty(ty.kind()), with(|cx| cx.const_literal(&len))) + } + RigidTy::Slice(ty) => { + format!("[{}]", pretty_ty(ty.kind())) + } + RigidTy::RawPtr(ty, mutability) => { + pretty.push_str("*"); + match mutability { + Mutability::Not => pretty.push_str("const "), + Mutability::Mut => pretty.push_str("mut "), + } + pretty.push_str(&pretty_ty(ty.kind())); + pretty + } + RigidTy::Ref(_, ty, _) => pretty_ty(ty.kind()), + RigidTy::FnDef(_, _) => format!("{:#?}", rigid_ty), + RigidTy::FnPtr(_) => format!("{:#?}", rigid_ty), + RigidTy::Closure(_, _) => format!("{:#?}", rigid_ty), + RigidTy::Coroutine(_, _, _) => format!("{:#?}", rigid_ty), + RigidTy::Dynamic(data, region, repr) => { + // FIXME: Fix binder printing, it looks ugly now + pretty.push_str("("); + match repr { + DynKind::Dyn => pretty.push_str("dyn "), + DynKind::DynStar => pretty.push_str("dyn* "), + } + pretty.push_str(format!("{:#?}", data).as_str()); + pretty.push_str(format!(" + {:#?} )", region).as_str()); + pretty + } + RigidTy::Never => "!".to_string(), + RigidTy::Tuple(tuple) => { + if tuple.is_empty() { + "()".to_string() + } else { + let mut tuple_str = String::new(); + tuple_str.push_str("("); + tuple.iter().enumerate().for_each(|(i, ty)| { + tuple_str.push_str(&pretty_ty(ty.kind())); + if i != tuple.len() - 1 { + tuple_str.push_str(", "); + } + }); + tuple_str.push_str(")"); + tuple_str + } + } + _ => format!("{:#?}", rigid_ty), + }, + TyKind::Alias(_, _) => format!("{:#?}", ty), + TyKind::Param(param_ty) => { + format!("{:#?}", param_ty.name) + } + TyKind::Bound(_, _) => format!("{:#?}", ty), + } +} diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index 806dced71ff..40bedd67352 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -76,6 +76,15 @@ pub trait MirVisitor { self.super_place(place, ptx, location) } + fn visit_projection_elem( + &mut self, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + self.super_projection_elem(elem, ptx, location); + } + fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) { let _ = (local, ptx, location); } @@ -133,7 +142,7 @@ pub trait MirVisitor { } let local_start = arg_count + 1; - for (idx, arg) in body.arg_locals().iter().enumerate() { + for (idx, arg) in body.inner_locals().iter().enumerate() { self.visit_local_decl(idx + local_start, arg) } } @@ -148,7 +157,7 @@ pub trait MirVisitor { fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) { let _ = local; - let LocalDecl { ty, span } = decl; + let LocalDecl { ty, span, .. } = decl; self.visit_ty(ty, Location(*span)); } @@ -264,7 +273,29 @@ pub trait MirVisitor { fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { let _ = location; let _ = ptx; - visit_opaque(&Opaque(place.projection.clone())); + self.visit_local(&place.local, ptx, location); + + for elem in &place.projection { + self.visit_projection_elem(elem, ptx, location); + } + } + + fn super_projection_elem( + &mut self, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Deref => {} + ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), + ProjectionElem::Index(local) => self.visit_local(local, ptx, location), + ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} + ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} + ProjectionElem::Downcast(_idx) => {} + ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), + ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), + } } fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { @@ -386,7 +417,7 @@ pub trait MirVisitor { fn visit_opaque(_: &Opaque) {} /// The location of a statement / terminator in the code and the CFG. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Location(Span); impl Location { diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index bd3feef6018..fa932c5d81a 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -4,7 +4,7 @@ use super::{ with, AllocId, DefId, Symbol, }; use crate::{Filename, Opaque}; -use std::fmt::{self, Debug, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Ty(pub usize); @@ -61,7 +61,7 @@ pub struct Region { #[derive(Clone, Debug, Eq, PartialEq)] pub enum RegionKind { - ReEarlyBound(EarlyBoundRegion), + ReEarlyParam(EarlyParamRegion), ReBound(DebruijnIndex, BoundRegion), ReStatic, RePlaceholder(Placeholder<BoundRegion>), @@ -71,7 +71,7 @@ pub enum RegionKind { pub(crate) type DebruijnIndex = u32; #[derive(Clone, Debug, Eq, PartialEq)] -pub struct EarlyBoundRegion { +pub struct EarlyParamRegion { pub def_id: RegionDef, pub index: u32, pub name: Symbol, @@ -135,6 +135,46 @@ pub enum TyKind { Bound(usize, BoundTy), } +impl TyKind { + pub fn rigid(&self) -> Option<&RigidTy> { + if let TyKind::RigidTy(inner) = self { Some(inner) } else { None } + } + + pub fn is_unit(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Tuple(data)) if data.len() == 0) + } + + pub fn is_trait(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) + } + + pub fn is_enum(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Enum) + } + + pub fn is_struct(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Struct) + } + + pub fn is_union(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Union) + } + + pub fn trait_principal(&self) -> Option<Binder<ExistentialTraitRef>> { + if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { + if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = + predicates.first() + { + Some(Binder { value: trait_ref.clone(), bound_vars: bound_vars.clone() }) + } else { + None + } + } else { + None + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum RigidTy { Bool, @@ -156,6 +196,7 @@ pub enum RigidTy { Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind), Never, Tuple(Vec<Ty>), + CoroutineWitness(CoroutineWitnessDef, GenericArgs), } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -217,6 +258,43 @@ pub struct BrNamedDef(pub DefId); #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct AdtDef(pub DefId); +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub enum AdtKind { + Enum, + Union, + Struct, +} + +impl AdtDef { + pub fn kind(&self) -> AdtKind { + with(|cx| cx.adt_kind(*self)) + } +} + +impl Display for AdtKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + AdtKind::Enum => "enum", + AdtKind::Union => "union", + AdtKind::Struct => "struct", + }) + } +} + +impl AdtKind { + pub fn is_enum(&self) -> bool { + matches!(self, AdtKind::Enum) + } + + pub fn is_struct(&self) -> bool { + matches!(self, AdtKind::Struct) + } + + pub fn is_union(&self) -> bool { + matches!(self, AdtKind::Union) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct AliasDef(pub DefId); @@ -235,6 +313,9 @@ pub struct ImplDef(pub DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct RegionDef(pub DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct CoroutineWitnessDef(pub DefId); + /// A list of generic arguments. #[derive(Clone, Debug, Eq, PartialEq)] pub struct GenericArgs(pub Vec<GenericArgKind>); @@ -351,6 +432,30 @@ pub struct Binder<T> { pub bound_vars: Vec<BoundVariableKind>, } +impl<T> Binder<T> { + pub fn skip_binder(self) -> T { + self.value + } + + pub fn map_bound_ref<F, U>(&self, f: F) -> Binder<U> + where + F: FnOnce(&T) -> U, + { + let Binder { value, bound_vars } = self; + let new_value = f(value); + Binder { value: new_value, bound_vars: bound_vars.clone() } + } + + pub fn map_bound<F, U>(self, f: F) -> Binder<U> + where + F: FnOnce(T) -> U, + { + let Binder { value, bound_vars } = self; + let new_value = f(value); + Binder { value: new_value, bound_vars } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct EarlyBinder<T> { pub value: T, @@ -389,12 +494,27 @@ pub enum ExistentialPredicate { AutoTrait(TraitDef), } +/// An existential reference to a trait where `Self` is not included. +/// +/// The `generic_args` will include any other known argument. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExistentialTraitRef { pub def_id: TraitDef, pub generic_args: GenericArgs, } +impl Binder<ExistentialTraitRef> { + pub fn with_self_ty(&self, self_ty: Ty) -> Binder<TraitRef> { + self.map_bound_ref(|trait_ref| trait_ref.with_self_ty(self_ty)) + } +} + +impl ExistentialTraitRef { + pub fn with_self_ty(&self, self_ty: Ty) -> TraitRef { + TraitRef::new(self.def_id, self_ty, &self.generic_args) + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExistentialProjection { pub def_id: TraitDef, @@ -500,10 +620,39 @@ impl TraitDecl { pub type ImplTrait = EarlyBinder<TraitRef>; +/// A complete reference to a trait, i.e., one where `Self` is known. #[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitRef { pub def_id: TraitDef, - pub args: GenericArgs, + /// The generic arguments for this definition. + /// The first element must always be type, and it represents `Self`. + args: GenericArgs, +} + +impl TraitRef { + pub fn new(def_id: TraitDef, self_ty: Ty, gen_args: &GenericArgs) -> TraitRef { + let mut args = vec![GenericArgKind::Type(self_ty)]; + args.extend_from_slice(&gen_args.0); + TraitRef { def_id, args: GenericArgs(args) } + } + + pub fn try_new(def_id: TraitDef, args: GenericArgs) -> Result<TraitRef, ()> { + match &args.0[..] { + [GenericArgKind::Type(_), ..] => Ok(TraitRef { def_id, args }), + _ => Err(()), + } + } + + pub fn args(&self) -> &GenericArgs { + &self.args + } + + pub fn self_ty(&self) -> Ty { + let GenericArgKind::Type(self_ty) = self.args.0[0] else { + panic!("Self must be a type, but found: {:?}", self.args.0[0]) + }; + self_ty + } } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs index 05e0b9b4d78..65e42879d61 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -149,6 +149,7 @@ impl Visitable for RigidTy { RigidTy::FnPtr(sig) => sig.visit(visitor), RigidTy::Closure(_, args) => args.visit(visitor), RigidTy::Coroutine(_, args, _) => args.visit(visitor), + RigidTy::CoroutineWitness(_, args) => args.visit(visitor), RigidTy::Dynamic(pred, r, _) => { pred.visit(visitor)?; r.visit(visitor) diff --git a/config.example.toml b/config.example.toml index e5df28a49af..170856bd97d 100644 --- a/config.example.toml +++ b/config.example.toml @@ -42,18 +42,15 @@ change-id = 116881 # Unless you're developing for a target where Rust CI doesn't build a compiler # toolchain or changing LLVM locally, you probably want to leave this enabled. # -# Set this to `"if-available"` if you are not sure whether you're on a tier 1 -# target. All tier 1 targets are currently supported; -# -# We also currently only support this when building LLVM for the build triple. -# -# Set this to `"if-unchanged"` to only download if the llvm-project have not -# been modified. (If there are no changes or if built from tarball source, -# the logic is the same as "if-available") +# Set this to `"if-unchanged"` to download only if the llvm-project has not +# been modified. You can also use this if you are unsure whether you're on a +# tier 1 target. All tier 1 targets are currently supported. + +# Currently, we only support this when building LLVM for the build triple. # # Note that many of the LLVM options are not currently supported for # downloading. Currently only the "assertions" option can be toggled. -#download-ci-llvm = if rust.channel == "dev" { "if-available" } else { false } +#download-ci-llvm = if rust.channel == "dev" { "if-unchanged" } else { false } # Indicates whether the LLVM build is a Release or Debug build #optimize = true diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f07c76232eb..4c014283210 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -78,8 +78,8 @@ not(no_sync), target_has_atomic = "ptr" ))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![no_std] #![needs_allocator] // Lints: diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index b2db2fdfd18..d51f5a548b5 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -285,9 +285,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> { // Also note the implementation of `Self: TrustedRandomAccess` requires // that `T: Copy` so reading elements from the buffer doesn't invalidate // them for `Drop`. - unsafe { - if T::IS_ZST { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } - } + unsafe { if T::IS_ZST { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } } } } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 9c8d7bbd999..34213637a32 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -206,7 +206,7 @@ where #[inline] fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> { - <&Self>::try_from(slice).map(|r| *r) + <&Self>::try_from(slice).copied() } } diff --git a/library/core/src/char/decode.rs b/library/core/src/char/decode.rs index d76f983d87c..23319fbe5dd 100644 --- a/library/core/src/char/decode.rs +++ b/library/core/src/char/decode.rs @@ -106,7 +106,7 @@ impl<I: Iterator<Item = u16>> Iterator for DecodeUtf16<I> { } } -#[stable(feature = "decode_utf16_fused_iterator", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "decode_utf16_fused_iterator", since = "1.75.0")] impl<I: Iterator<Item = u16> + FusedIterator> FusedIterator for DecodeUtf16<I> {} impl DecodeUtf16Error { diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index 24bb9ad1ad1..60b5df752ca 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -21,12 +21,16 @@ pub(crate) fn escape_ascii_into(output: &mut [ascii::Char; 4], byte: u8) -> Rang b'\\' => backslash(ascii::Char::ReverseSolidus), b'\'' => backslash(ascii::Char::Apostrophe), b'\"' => backslash(ascii::Char::QuotationMark), - _ => if let Some(a) = byte.as_ascii() && !byte.is_ascii_control() { - ([a, ascii::Char::Null, ascii::Char::Null, ascii::Char::Null], 1) - } else { - let hi = HEX_DIGITS[usize::from(byte >> 4)]; - let lo = HEX_DIGITS[usize::from(byte & 0xf)]; - ([ascii::Char::ReverseSolidus, ascii::Char::SmallX, hi, lo], 4) + _ => { + if let Some(a) = byte.as_ascii() + && !byte.is_ascii_control() + { + ([a, ascii::Char::Null, ascii::Char::Null, ascii::Char::Null], 1) + } else { + let hi = HEX_DIGITS[usize::from(byte >> 4)]; + let lo = HEX_DIGITS[usize::from(byte & 0xf)]; + ([ascii::Char::ReverseSolidus, ascii::Char::SmallX, hi, lo], 4) + } } }; *output = data; diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 6908c824f44..7340ad90da5 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -241,7 +241,6 @@ impl fmt::Debug for c_void { ), all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), target_family = "wasm", - target_arch = "asmjs", target_os = "uefi", windows, ))] @@ -270,7 +269,6 @@ pub struct VaListImpl<'f> { ), all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), target_family = "wasm", - target_arch = "asmjs", target_os = "uefi", windows, ))] @@ -395,7 +393,6 @@ pub struct VaList<'a, 'f: 'a> { any(target_os = "macos", target_os = "ios", target_os = "tvos") ), target_family = "wasm", - target_arch = "asmjs", target_os = "uefi", windows, ))] @@ -413,7 +410,6 @@ pub struct VaList<'a, 'f: 'a> { not(any(target_os = "macos", target_os = "ios", target_os = "tvos")) ), not(target_family = "wasm"), - not(target_arch = "asmjs"), not(target_os = "uefi"), not(windows), ))] @@ -431,7 +427,6 @@ pub struct VaList<'a, 'f: 'a> { ), all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), target_family = "wasm", - target_arch = "asmjs", target_os = "uefi", windows, ))] @@ -461,7 +456,6 @@ impl<'f> VaListImpl<'f> { not(any(target_os = "macos", target_os = "ios", target_os = "tvos")) ), not(target_family = "wasm"), - not(target_arch = "asmjs"), not(target_os = "uefi"), not(windows), ))] diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 4f42f73ebba..778515f8616 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -309,7 +309,6 @@ macro_rules! impl_Exp { n /= 10; exponent += 1; } - let (added_precision, subtracted_precision) = match f.precision() { Some(fmt_prec) => { // number of decimal digits minus 1 @@ -331,9 +330,15 @@ macro_rules! impl_Exp { let rem = n % 10; n /= 10; exponent += 1; - // round up last digit - if rem >= 5 { + // round up last digit, round to even on a tie + if rem > 5 || (rem == 5 && (n % 2 != 0 || subtracted_precision > 1 )) { n += 1; + // if the digit is rounded to the next power + // instead adjust the exponent + if n.ilog10() > (n - 1).ilog10() { + n /= 10; + exponent += 1; + } } } (n, exponent, exponent, added_precision) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index f855b2ad483..f25ca9e2b18 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1072,7 +1072,7 @@ extern "rust-intrinsic" { /// zero-initialization: This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. - #[rustc_const_stable(feature = "const_assert_type2", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_assert_type2", since = "1.75.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn assert_zero_valid<T>(); @@ -1080,7 +1080,7 @@ extern "rust-intrinsic" { /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. - #[rustc_const_stable(feature = "const_assert_type2", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_assert_type2", since = "1.75.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn assert_mem_uninitialized_valid<T>(); @@ -2279,7 +2279,7 @@ extern "rust-intrinsic" { /// any safety invariants. /// /// The stabilized version of this intrinsic is [`core::mem::discriminant`]. - #[rustc_const_stable(feature = "const_discriminant", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_discriminant", since = "1.75.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant; @@ -2487,12 +2487,6 @@ extern "rust-intrinsic" { where G: FnOnce<ARG, Output = RET>, F: FnOnce<ARG, Output = RET>; - - /// This method creates a pointer to any `Some` value. If the argument is - /// `None`, an invalid within-bounds pointer (that is still acceptable for - /// constructing an empty slice) is returned. - #[rustc_nounwind] - pub fn option_payload_ptr<T>(arg: *const Option<T>) -> *const T; } // Some functions are defined here because they accidentally got made diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index b26a17ec30e..0d8a306ace5 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -110,15 +110,15 @@ //! let popped; //! //! { -//! Call(_unused = Vec::push(v, value), pop) +//! Call(_unused = Vec::push(v, value), pop, UnwindContinue()) //! } //! //! pop = { -//! Call(popped = Vec::pop(v), drop) +//! Call(popped = Vec::pop(v), drop, UnwindContinue()) //! } //! //! drop = { -//! Drop(popped, ret) +//! Drop(popped, ret, UnwindContinue()) //! } //! //! ret = { @@ -238,10 +238,6 @@ //! //! #### Terminators //! -//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there -//! are no resume and abort terminators, and terminators that might unwind do not have any way to -//! indicate the unwind block. -//! //! - [`Goto`], [`Return`], [`Unreachable`] and [`Drop`](Drop()) have associated functions. //! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block` //! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the @@ -260,7 +256,26 @@ /// Type representing basic blocks. /// /// All terminators will have this type as a return type. It helps achieve some type safety. -pub struct BasicBlock; +#[rustc_diagnostic_item = "mir_basic_block"] +pub enum BasicBlock { + /// A non-cleanup basic block. + Normal, + /// A basic block that lies on an unwind path. + Cleanup, +} + +/// The reason we are terminating the process during unwinding. +#[rustc_diagnostic_item = "mir_unwind_terminate_reason"] +pub enum UnwindTerminateReason { + /// Unwinding is just not possible given the ABI of this function. + Abi, + /// We were already cleaning up for an ongoing unwind, and a *second*, *nested* unwind was + /// triggered by the drop glue. + InCleanup, +} + +pub use UnwindTerminateReason::Abi as ReasonAbi; +pub use UnwindTerminateReason::InCleanup as ReasonInCleanup; macro_rules! define { ($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => { @@ -271,11 +286,41 @@ macro_rules! define { } } +// Unwind actions +define!( + "mir_unwind_continue", + /// An unwind action that continues unwinding. + fn UnwindContinue() +); +define!( + "mir_unwind_unreachable", + /// An unwind action that triggers undefined behaviour. + fn UnwindUnreachable() -> BasicBlock +); +define!( + "mir_unwind_terminate", + /// An unwind action that terminates the execution. + /// + /// `UnwindTerminate` can also be used as a terminator. + fn UnwindTerminate(reason: UnwindTerminateReason) +); +define!( + "mir_unwind_cleanup", + /// An unwind action that continues execution in a given basic blok. + fn UnwindCleanup(goto: BasicBlock) +); + +// Terminators define!("mir_return", fn Return() -> BasicBlock); define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock); define!("mir_unreachable", fn Unreachable() -> BasicBlock); -define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock)); -define!("mir_call", fn Call(call: (), goto: BasicBlock)); +define!("mir_drop", fn Drop<T, U>(place: T, goto: BasicBlock, unwind_action: U)); +define!("mir_call", fn Call<U>(call: (), goto: BasicBlock, unwind_action: U)); +define!("mir_unwind_resume", + /// A terminator that resumes the unwinding. + fn UnwindResume() +); + define!("mir_storage_live", fn StorageLive<T>(local: T)); define!("mir_storage_dead", fn StorageDead<T>(local: T)); define!("mir_deinit", fn Deinit<T>(place: T)); @@ -382,16 +427,15 @@ pub macro mir { } $( - $block_name:ident = { + $block_name:ident $(($block_cleanup:ident))? = { $($block:tt)* } )* ) => {{ // First, we declare all basic blocks. - $( - let $block_name: ::core::intrinsics::mir::BasicBlock; - )* - + __internal_declare_basic_blocks!($( + $block_name $(($block_cleanup))? + )*); { // Now all locals #[allow(non_snake_case)] @@ -585,3 +629,17 @@ pub macro __internal_remove_let { } }, } + +/// Helper macro that declares the basic blocks. +#[doc(hidden)] +pub macro __internal_declare_basic_blocks { + () => {}, + ($name:ident (cleanup) $($rest:tt)*) => { + let $name = ::core::intrinsics::mir::BasicBlock::Cleanup; + __internal_declare_basic_blocks!($($rest)*) + }, + ($name:ident $($rest:tt)*) => { + let $name = ::core::intrinsics::mir::BasicBlock::Normal; + __internal_declare_basic_blocks!($($rest)*) + }, +} diff --git a/library/core/src/iter/adapters/copied.rs b/library/core/src/iter/adapters/copied.rs index 8f6b2904eae..7a2c9d839b7 100644 --- a/library/core/src/iter/adapters/copied.rs +++ b/library/core/src/iter/adapters/copied.rs @@ -193,7 +193,7 @@ where T: Copy, { default fn spec_next_chunk(&mut self) -> Result<[T; N], array::IntoIter<T, N>> { - array::iter_next_chunk(&mut self.map(|e| *e)) + array::iter_next_chunk(&mut self.copied()) } } diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index 693479977db..32308c84d71 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -97,9 +97,11 @@ where // SAFETY: Loop conditions ensure the index is in bounds. unsafe { - let opt_payload_at = core::intrinsics::option_payload_ptr(&val); + let opt_payload_at: *const MaybeUninit<B> = (&val as *const Option<B>) + .byte_add(core::mem::offset_of!(Option<B>, Some.0)) + .cast(); let dst = guard.array.as_mut_ptr().add(idx); - crate::ptr::copy_nonoverlapping(opt_payload_at.cast(), dst, 1); + crate::ptr::copy_nonoverlapping(opt_payload_at, dst, 1); crate::mem::forget(val); }; diff --git a/library/core/src/iter/adapters/step_by.rs b/library/core/src/iter/adapters/step_by.rs index 7f58f7d1775..9e83584e3f3 100644 --- a/library/core/src/iter/adapters/step_by.rs +++ b/library/core/src/iter/adapters/step_by.rs @@ -83,11 +83,7 @@ where // last element. Used in the `DoubleEndedIterator` implementation. fn next_back_index(&self) -> usize { let rem = self.iter.len() % (self.step + 1); - if self.first_take { - if rem == 0 { self.step } else { rem - 1 } - } else { - rem - } + if self.first_take { if rem == 0 { self.step } else { rem - 1 } } else { rem } } } diff --git a/library/core/src/iter/sources/from_coroutine.rs b/library/core/src/iter/sources/from_coroutine.rs index 16fbca9b65e..bf413b24d41 100644 --- a/library/core/src/iter/sources/from_coroutine.rs +++ b/library/core/src/iter/sources/from_coroutine.rs @@ -11,8 +11,7 @@ use crate::pin::Pin; /// # Examples /// /// ``` -/// #![cfg_attr(bootstrap, feature(generators))] -/// #![cfg_attr(not(bootstrap), feature(coroutines))] +/// #![feature(coroutines)] /// #![feature(iter_from_coroutine)] /// /// let it = std::iter::from_coroutine(|| { diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 6adea444214..8e2c887a161 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -69,7 +69,7 @@ fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {} message = "`{Self}` is not an iterator" )] #[doc(notable_trait)] -#[cfg_attr(not(bootstrap), lang = "iterator")] +#[lang = "iterator"] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] pub trait Iterator { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 5a6d242a729..921a0fb6a9f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -69,7 +69,7 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![doc(rust_logo)] #![doc(cfg_hide( not(test), any(not(feature = "miri-test-libstd"), test, doctest), @@ -178,6 +178,8 @@ #![feature(is_ascii_octdigit)] #![feature(isqrt)] #![feature(maybe_uninit_uninit_array)] +#![feature(offset_of)] +#![feature(offset_of_enum)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] @@ -219,6 +221,7 @@ #![feature(doc_cfg)] #![feature(doc_cfg_hide)] #![feature(doc_notable_trait)] +#![feature(effects)] #![feature(exhaustive_patterns)] #![feature(extern_types)] #![feature(fundamental)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 8a4070ebd96..53e9a32e305 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -398,7 +398,7 @@ impl<T> MaybeUninit<T> { #[rustc_allow_const_fn_unstable(const_mut_refs)] #[rustc_allow_const_fn_unstable(const_ptr_write)] #[rustc_allow_const_fn_unstable(const_maybe_uninit_as_mut_ptr)] - #[rustc_const_stable(feature = "const_maybe_uninit_zeroed", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_maybe_uninit_zeroed", since = "1.75.0")] pub const fn zeroed() -> MaybeUninit<T> { let mut u = MaybeUninit::<T>::uninit(); // SAFETY: `u.as_mut_ptr()` points to allocated memory. @@ -691,10 +691,7 @@ impl<T> MaybeUninit<T> { /// // they both get dropped! /// ``` #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] - #[rustc_const_stable( - feature = "const_maybe_uninit_assume_init_read", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_maybe_uninit_assume_init_read", since = "1.75.0")] #[inline(always)] #[track_caller] pub const unsafe fn assume_init_read(&self) -> T { diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index c964596dd5f..bf10ada0176 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -647,7 +647,7 @@ pub const fn needs_drop<T: ?Sized>() -> bool { #[allow(deprecated)] #[rustc_diagnostic_item = "mem_zeroed"] #[track_caller] -#[rustc_const_stable(feature = "const_mem_zeroed", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_mem_zeroed", since = "1.75.0")] pub const unsafe fn zeroed<T>() -> T { // SAFETY: the caller must guarantee that an all-zero value is valid for `T`. unsafe { @@ -1132,10 +1132,12 @@ impl<T> fmt::Debug for Discriminant<T> { /// /// [Reference]: ../../reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations /// -/// The value of a [`Discriminant<T>`] is independent of any *lifetimes* in `T`. As such, reading -/// or writing a `Discriminant<Foo<'a>>` as a `Discriminant<Foo<'b>>` (whether via [`transmute`] or -/// otherwise) is always sound. Note that this is **not** true for other kinds of generic -/// parameters; `Discriminant<Foo<A>>` and `Discriminant<Foo<B>>` might be incompatible. +/// The value of a [`Discriminant<T>`] is independent of any *free lifetimes* in `T`. As such, +/// reading or writing a `Discriminant<Foo<'a>>` as a `Discriminant<Foo<'b>>` (whether via +/// [`transmute`] or otherwise) is always sound. Note that this is **not** true for other kinds +/// of generic parameters and for higher-ranked lifetimes; `Discriminant<Foo<A>>` and +/// `Discriminant<Foo<B>>` as well as `Discriminant<Bar<dyn for<'a> Trait<'a>>>` and +/// `Discriminant<Bar<dyn Trait<'static>>>` may be incompatible. /// /// # Examples /// @@ -1208,7 +1210,7 @@ impl<T> fmt::Debug for Discriminant<T> { /// // assert_eq!(0, unsafe { std::mem::transmute::<_, u8>(std::mem::discriminant(&unit_like)) }); /// ``` #[stable(feature = "discriminant_value", since = "1.21.0")] -#[rustc_const_stable(feature = "const_discriminant", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_discriminant", since = "1.75.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_discriminant")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const fn discriminant<T>(v: &T) -> Discriminant<T> { @@ -1358,7 +1360,7 @@ impl<T> SizedTypeProperties for T {} /// /// ``` /// #![feature(offset_of)] -/// # #![cfg_attr(not(bootstrap), feature(offset_of_enum))] +/// # #![feature(offset_of_enum)] /// /// use std::mem; /// #[repr(C)] @@ -1388,12 +1390,9 @@ impl<T> SizedTypeProperties for T {} /// B { one: u8, two: u16 }, /// } /// -/// # #[cfg(not(bootstrap))] /// assert_eq!(mem::offset_of!(Enum, A.0), 1); -/// # #[cfg(not(bootstrap))] /// assert_eq!(mem::offset_of!(Enum, B.two), 2); /// -/// # #[cfg(not(bootstrap))] /// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); /// ``` #[unstable(feature = "offset_of", issue = "106655")] diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index b7eca9b168a..77f85215d71 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -425,8 +425,8 @@ impl IpAddr { #[inline] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[stable(feature = "ip_to_canonical", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ip_to_canonical", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_to_canonical", since = "1.75.0")] + #[rustc_const_stable(feature = "ip_to_canonical", since = "1.75.0")] pub const fn to_canonical(&self) -> IpAddr { match self { IpAddr::V4(_) => *self, @@ -1757,7 +1757,7 @@ impl Ipv6Addr { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")] - #[rustc_const_stable(feature = "const_ipv6_to_ipv4_mapped", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_ipv6_to_ipv4_mapped", since = "1.75.0")] pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> { match self.octets() { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { @@ -1825,8 +1825,8 @@ impl Ipv6Addr { #[inline] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[stable(feature = "ip_to_canonical", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ip_to_canonical", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_to_canonical", since = "1.75.0")] + #[rustc_const_stable(feature = "ip_to_canonical", since = "1.75.0")] pub const fn to_canonical(&self) -> IpAddr { if let Some(mapped) = self.to_ipv4_mapped() { return IpAddr::V4(mapped); @@ -2128,7 +2128,7 @@ impl From<[u16; 8]> for IpAddr { } } -#[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "ip_bitops", since = "1.75.0")] impl Not for Ipv4Addr { type Output = Ipv4Addr; @@ -2141,7 +2141,7 @@ impl Not for Ipv4Addr { } } -#[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "ip_bitops", since = "1.75.0")] impl Not for &'_ Ipv4Addr { type Output = Ipv4Addr; @@ -2151,7 +2151,7 @@ impl Not for &'_ Ipv4Addr { } } -#[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "ip_bitops", since = "1.75.0")] impl Not for Ipv6Addr { type Output = Ipv6Addr; @@ -2164,7 +2164,7 @@ impl Not for Ipv6Addr { } } -#[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "ip_bitops", since = "1.75.0")] impl Not for &'_ Ipv6Addr { type Output = Ipv6Addr; @@ -2246,13 +2246,13 @@ macro_rules! bitop_impls { } bitop_impls! { - #[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_bitops", since = "1.75.0")] impl (BitAnd, BitAndAssign) for Ipv4Addr = (bitand, bitand_assign); - #[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_bitops", since = "1.75.0")] impl (BitOr, BitOrAssign) for Ipv4Addr = (bitor, bitor_assign); - #[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_bitops", since = "1.75.0")] impl (BitAnd, BitAndAssign) for Ipv6Addr = (bitand, bitand_assign); - #[stable(feature = "ip_bitops", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ip_bitops", since = "1.75.0")] impl (BitOr, BitOrAssign) for Ipv6Addr = (bitor, bitor_assign); } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index f60626b00dc..709eba2ffc9 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1424,9 +1424,17 @@ impl f32 { /// ]; /// /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); - /// # assert!(bois.into_iter().map(|b| b.weight) - /// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) - /// # .all(|(a, b)| a.to_bits() == b.to_bits())) + /// + /// // `f32::NAN` could be positive or negative, which will affect the sort order. + /// if f32::NAN.is_sign_negative() { + /// assert!(bois.into_iter().map(|b| b.weight) + /// .zip([f32::NAN, -5.0, 0.1, 10.0, 99.0, f32::INFINITY].iter()) + /// .all(|(a, b)| a.to_bits() == b.to_bits())) + /// } else { + /// assert!(bois.into_iter().map(|b| b.weight) + /// .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) + /// .all(|(a, b)| a.to_bits() == b.to_bits())) + /// } /// ``` #[stable(feature = "total_cmp", since = "1.62.0")] #[must_use] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 0a87021d8c1..73fa61574cc 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1422,9 +1422,17 @@ impl f64 { /// ]; /// /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); - /// # assert!(bois.into_iter().map(|b| b.weight) - /// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter()) - /// # .all(|(a, b)| a.to_bits() == b.to_bits())) + /// + /// // `f64::NAN` could be positive or negative, which will affect the sort order. + /// if f64::NAN.is_sign_negative() { + /// assert!(bois.into_iter().map(|b| b.weight) + /// .zip([f64::NAN, -5.0, 0.1, 10.0, 99.0, f64::INFINITY].iter()) + /// .all(|(a, b)| a.to_bits() == b.to_bits())) + /// } else { + /// assert!(bois.into_iter().map(|b| b.weight) + /// .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter()) + /// .all(|(a, b)| a.to_bits() == b.to_bits())) + /// } /// ``` #[stable(feature = "total_cmp", since = "1.62.0")] #[must_use] diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index cd5ca988f7e..80e2903992e 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -7,8 +7,7 @@ use crate::pin::Pin; /// possible return values of a coroutine. Currently this corresponds to either /// a suspension point (`Yielded`) or a termination point (`Complete`). #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -#[cfg_attr(bootstrap, lang = "generator_state")] -#[cfg_attr(not(bootstrap), lang = "coroutine_state")] +#[lang = "coroutine_state"] #[unstable(feature = "coroutine_trait", issue = "43122")] pub enum CoroutineState<Y, R> { /// The coroutine suspended with a value. @@ -40,8 +39,7 @@ pub enum CoroutineState<Y, R> { /// closure-like: /// /// ```rust -/// #![cfg_attr(bootstrap, feature(generators))] -/// #![cfg_attr(not(bootstrap), feature(coroutines))] +/// #![feature(coroutines)] /// #![feature(coroutine_trait)] /// /// use std::ops::{Coroutine, CoroutineState}; @@ -68,8 +66,7 @@ pub enum CoroutineState<Y, R> { /// /// [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 /// [unstable book]: ../../unstable-book/language-features/coroutines.html -#[cfg_attr(bootstrap, lang = "generator")] -#[cfg_attr(not(bootstrap), lang = "coroutine")] +#[lang = "coroutine"] #[unstable(feature = "coroutine_trait", issue = "43122")] #[fundamental] pub trait Coroutine<R = ()> { diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 51e304dd7c2..3a3d3fcf1da 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -56,7 +56,7 @@ use crate::marker::Tuple; #[lang = "fn"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_paren_sugar] -#[cfg_attr(not(bootstrap), rustc_on_unimplemented( +#[rustc_on_unimplemented( on( Args = "()", note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" @@ -69,7 +69,7 @@ use crate::marker::Tuple; ), message = "expected a `{Trait}` closure, found `{Self}`", label = "expected an `{Trait}` closure, found `{Self}`" -))] +)] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] // FIXME(effects) #[const_trait] @@ -143,7 +143,7 @@ pub trait Fn<Args: Tuple>: FnMut<Args> { #[lang = "fn_mut"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_paren_sugar] -#[cfg_attr(not(bootstrap), rustc_on_unimplemented( +#[rustc_on_unimplemented( on( Args = "()", note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" @@ -156,7 +156,7 @@ pub trait Fn<Args: Tuple>: FnMut<Args> { ), message = "expected a `{Trait}` closure, found `{Self}`", label = "expected an `{Trait}` closure, found `{Self}`" -))] +)] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] // FIXME(effects) #[const_trait] @@ -222,7 +222,7 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> { #[lang = "fn_once"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_paren_sugar] -#[cfg_attr(not(bootstrap), rustc_on_unimplemented( +#[rustc_on_unimplemented( on( Args = "()", note = "wrap the `{Self}` in a closure with no arguments: `|| {{ /* code */ }}`" @@ -235,7 +235,7 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> { ), message = "expected a `{Trait}` closure, found `{Self}`", label = "expected an `{Trait}` closure, found `{Self}`" -))] +)] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] // FIXME(effects) #[const_trait] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index acf3dfbdf4c..bfd6aee4a23 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -119,7 +119,7 @@ //! # Representation //! //! Rust guarantees to optimize the following types `T` such that -//! [`Option<T>`] has the same size and alignment as `T`. In some +//! [`Option<T>`] has the same size, alignment, and [function call ABI] as `T`. In some //! of these cases, Rust further guarantees that //! `transmute::<_, Option<T>>([0u8; size_of::<T>()])` is sound and //! produces `Option::<T>::None`. These cases are identified by the @@ -127,7 +127,7 @@ //! //! | `T` | `transmute::<_, Option<T>>([0u8; size_of::<T>()])` sound? | //! |---------------------------------------------------------------------|----------------------------------------------------------------------| -//! | [`Box<U>`] | when `U: Sized` | +//! | [`Box<U>`] (specifically, only `Box<U, Global>`) | when `U: Sized` | //! | `&U` | when `U: Sized` | //! | `&mut U` | when `U: Sized` | //! | `fn`, `extern "C" fn`[^extern_fn] | always | @@ -135,11 +135,12 @@ //! | [`ptr::NonNull<U>`] | when `U: Sized` | //! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type | //! -//! [^extern_fn]: this remains true for any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) +//! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) //! //! [`Box<U>`]: ../../std/boxed/struct.Box.html //! [`num::NonZero*`]: crate::num //! [`ptr::NonNull<U>`]: crate::ptr::NonNull +//! [function call ABI]: ../primitive.fn.html#abi-compatibility //! //! This is called the "null pointer optimization" or NPO. //! @@ -765,7 +766,7 @@ impl<T> Option<T> { /// ``` #[inline] #[must_use] - #[stable(feature = "option_as_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "option_as_slice", since = "1.75.0")] pub fn as_slice(&self) -> &[T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to @@ -779,7 +780,7 @@ impl<T> Option<T> { // `None` case it's just padding). unsafe { slice::from_raw_parts( - crate::intrinsics::option_payload_ptr(crate::ptr::from_ref(self)), + (self as *const Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), usize::from(self.is_some()), ) } @@ -819,7 +820,7 @@ impl<T> Option<T> { /// ``` #[inline] #[must_use] - #[stable(feature = "option_as_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "option_as_slice", since = "1.75.0")] pub fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to @@ -835,8 +836,7 @@ impl<T> Option<T> { // the `None` case it's just padding). unsafe { slice::from_raw_parts_mut( - crate::intrinsics::option_payload_ptr(crate::ptr::from_mut(self).cast_const()) - .cast_mut(), + (self as *mut Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), usize::from(self.is_some()), ) } @@ -1079,8 +1079,6 @@ impl<T> Option<T> { /// # Examples /// /// ``` - /// #![feature(result_option_inspect)] - /// /// let v = vec![1, 2, 3, 4, 5]; /// /// // prints "got: 4" @@ -1090,11 +1088,8 @@ impl<T> Option<T> { /// let x: Option<&usize> = v.get(5).inspect(|x| println!("got: {x}")); /// ``` #[inline] - #[unstable(feature = "result_option_inspect", issue = "91345")] - pub fn inspect<F>(self, f: F) -> Self - where - F: FnOnce(&T), - { + #[stable(feature = "result_option_inspect", since = "CURRENT_RUSTC_VERSION")] + pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self { if let Some(ref x) = self { f(x); } diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index a00fd322b3c..b7cd10b5b19 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -47,7 +47,7 @@ pub macro panic_2015 { #[allow_internal_unstable(core_panic, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] #[rustc_macro_transparency = "semitransparent"] -#[cfg(any(bootstrap, feature = "panic_immediate_abort"))] +#[cfg(feature = "panic_immediate_abort")] pub macro panic_2021 { () => ( $crate::panicking::panic("explicit panic") @@ -75,7 +75,7 @@ pub macro panic_2021 { )] #[rustc_diagnostic_item = "core_panic_2021_macro"] #[rustc_macro_transparency = "semitransparent"] -#[cfg(not(any(bootstrap, feature = "panic_immediate_abort")))] +#[cfg(not(feature = "panic_immediate_abort"))] pub macro panic_2021 { () => ({ // Create a function so that the argument for `track_caller` diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 39a5e8d9fe2..fa6e5fe5d17 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -170,9 +170,8 @@ pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! { #[inline] #[track_caller] #[rustc_do_not_const_check] // hooked by const-eval -#[cfg_attr(bootstrap, lang = "panic_display")] // enforce a &&str argument in const-check and hook this by const-eval -#[cfg_attr(not(bootstrap), rustc_const_panic_str)] +#[rustc_const_panic_str] #[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn panic_display<T: fmt::Display>(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index bca97d4ee36..c3e774e1dab 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1088,8 +1088,7 @@ impl<P, U> DispatchFromDyn<Pin<U>> for Pin<P> where P: DispatchFromDyn<U> {} /// ### With `Coroutine`s /// /// ```rust -/// #![cfg_attr(bootstrap, feature(generators))] -/// #![cfg_attr(not(bootstrap), feature(coroutines))] +/// #![feature(coroutines)] /// #![feature(coroutine_trait)] /// use core::{ /// ops::{Coroutine, CoroutineState}, diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 87e49210874..a7e20407cec 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1493,7 +1493,7 @@ mod prim_ref {} /// /// ### Casting to and from integers /// -/// You cast function pointers directly to integers: +/// You can cast function pointers directly to integers: /// /// ```rust /// let fnptr: fn(i32) -> i32 = |x| x+2; @@ -1519,6 +1519,114 @@ mod prim_ref {} /// Note that all of this is not portable to platforms where function pointers and data pointers /// have different sizes. /// +/// ### ABI compatibility +/// +/// Generally, when a function is declared with one signature and called via a function pointer with +/// a different signature, the two signatures must be *ABI-compatible* or else calling the function +/// via that function pointer is Undefined Behavior. ABI compatibility is a lot stricter than merely +/// having the same memory layout; for example, even if `i32` and `f32` have the same size and +/// alignment, they might be passed in different registers and hence not be ABI-compatible. +/// +/// ABI compatibility as a concern only arises in code that alters the type of function pointers, +/// code that imports functions via `extern` blocks, and in code that combines `#[target_feature]` +/// with `extern fn`. Altering the type of function pointers is wildly unsafe (as in, a lot more +/// unsafe than even [`transmute_copy`][mem::transmute_copy]), and should only occur in the most +/// exceptional circumstances. Most Rust code just imports functions via `use`. `#[target_feature]` +/// is also used rarely. So, most likely you do not have to worry about ABI compatibility. +/// +/// But assuming such circumstances, what are the rules? For this section, we are only considering +/// the ABI of direct Rust-to-Rust calls, not linking in general -- once functions are imported via +/// `extern` blocks, there are more things to consider that we do not go into here. +/// +/// For two signatures to be considered *ABI-compatible*, they must use a compatible ABI string, +/// must take the same number of arguments, the individual argument types and the return types must +/// be ABI-compatible, and the target feature requirements must be met (see the subsection below for +/// the last point). The ABI string is declared via `extern "ABI" fn(...) -> ...`; note that +/// `fn name(...) -> ...` implicitly uses the `"Rust"` ABI string and `extern fn name(...) -> ...` +/// implicitly uses the `"C"` ABI string. +/// +/// The ABI strings are guaranteed to be compatible if they are the same, or if the caller ABI +/// string is `$X-unwind` and the callee ABI string is `$X`, where `$X` is one of the following: +/// "C", "aapcs", "fastcall", "stdcall", "system", "sysv64", "thiscall", "vectorcall", "win64". +/// +/// The following types are guaranteed to be ABI-compatible: +/// +/// - `*const T`, `*mut T`, `&T`, `&mut T`, `Box<T>` (specifically, only `Box<T, Global>`), and +/// `NonNull<T>` are all ABI-compatible with each other for all `T`. They are also ABI-compatible +/// with each other for _different_ `T` if they have the same metadata type (`<T as +/// Pointee>::Metadata`). +/// - `usize` is ABI-compatible with the `uN` integer type of the same size, and likewise `isize` is +/// ABI-compatible with the `iN` integer type of the same size. +/// - Any two `fn` (function pointer) types are ABI-compatible with each other if they have the same +/// ABI string or the ABI string only differs in a trailing `-unwind`, independent of the rest of +/// their signature. (This means you can pass `fn()` to a function expecting `fn(i32)`, and the +/// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer +/// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just +/// almost certainly UB to later call that function pointer.) +/// - Any two types with size 0 and alignment 1 are ABI-compatible. +/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the +/// unique field that doesn't have size 0 and alignment 1 (if there is such a field). +/// - `i32` is ABI-compatible with `NonZeroI32`, and similar for all other integer types with their +/// matching `NonZero*` type. +/// - If `T` is guaranteed to be subject to the [null pointer +/// optimization](option/index.html#representation), then `T` and `Option<T>` are ABI-compatible. +/// +/// Furthermore, ABI compatibility satisfies the following general properties: +/// +/// - Every type is ABI-compatible with itself. +/// - If `T1` and `T2` are ABI-compatible, then two `repr(C)` types that only differ because one +/// field type was changed from `T1` to `T2` are ABI-compatible. +/// - If `T1` and `T2` are ABI-compatible and `T2` and `T3` are ABI-compatible, then so are `T1` and +/// `T3` (i.e., ABI-compatibility is transitive). +/// - If `T1` and `T2` are ABI-compatible, then so are `T2` and `T1` (i.e., ABI-compatibility is +/// symmetric). +/// +/// More signatures can be ABI-compatible on specific targets, but that should not be relied upon +/// since it is not portable and not a stable guarantee. +/// +/// Noteworthy cases of types *not* being ABI-compatible in general are: +/// * `bool` vs `u8`, and `i32` vs `u32`: on some targets, the calling conventions for these types +/// differ in terms of what they guarantee for the remaining bits in the register that are not +/// used by the value. +/// * `i32` vs `f32` are not compatible either, as has already been mentioned above. +/// * `struct Foo(u32)` and `u32` are not compatible (without `repr(transparent)`) since structs are +/// aggregate types and often passed in a different way than primitives like `i32`. +/// +/// Note that these rules describe when two completely known types are ABI-compatible. When +/// considering ABI compatibility of a type declared in another crate (including the standard +/// library), consider that any type that has a private field or the `#[non_exhaustive]` attribute +/// may change its layout as a non-breaking update unless documented otherwise -- so for instance, +/// even if such a type is a 1-ZST or `repr(transparent)` right now, this might change with any +/// library version bump. +/// +/// If the declared signature and the signature of the function pointer are ABI-compatible, then the +/// function call behaves as if every argument was [`transmute`d][mem::transmute] from the +/// type in the function pointer to the type at the function declaration, and the return value is +/// [`transmute`d][mem::transmute] from the type in the declaration to the type in the +/// pointer. All the usual caveats and concerns around transmutation apply; for instance, if the +/// function expects a `NonNullI32` and the function pointer uses the ABI-compatible type +/// `Option<NonNullI32>`, and the value used for the argument is `None`, then this call is Undefined +/// Behavior since transmuting `None::<NonNullI32>` to `NonNullI32` violates the non-null +/// requirement. +/// +/// #### Requirements concerning target features +/// +/// Under some conditions, the signature used by the caller and the callee can be ABI-incompatible +/// even if the exact same ABI string and types are being used. As an example, the +/// `std::arch::x86_64::__m256` type has a different `extern "C"` ABI when the `avx` feature is +/// enabled vs when it is not enabled. +/// +/// Therefore, to ensure ABI compatibility when code using different target features is combined +/// (such as via `#[target_feature]`), we further require that one of the following conditions is +/// met: +/// +/// - The function uses the `"Rust"` ABI string (which is the default without `extern`). +/// - Caller and callee are using the exact same set of target features. For the callee we consider +/// the features enabled (via `#[target_feature]` and `-C target-feature`/`-C target-cpu`) at the +/// declaration site; for the caller we consider the features enabled at the call site. +/// - Neither any argument nor the return value involves a SIMD type (`#[repr(simd)]`) that is not +/// behind a pointer indirection (i.e., `*mut __m256` is fine, but `(i32, __m256)` is not). +/// /// ### Trait implementations /// /// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index bbf7199fffa..e578d2257f6 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -42,6 +42,7 @@ impl Alignment { /// This provides the same numerical value as [`mem::align_of`], /// but in an `Alignment` instead of a `usize`. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] pub const fn of<T>() -> Self { // SAFETY: rustc ensures that type alignment is always a power of two. @@ -53,6 +54,7 @@ impl Alignment { /// /// Note that `0` is not a power of two, nor a valid alignment. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] pub const fn new(align: usize) -> Option<Self> { if align.is_power_of_two() { @@ -98,6 +100,7 @@ impl Alignment { /// Returns the alignment as a [`NonZeroUsize`] #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] pub const fn as_nonzero(self) -> NonZeroUsize { // SAFETY: All the discriminants are non-zero. @@ -118,10 +121,42 @@ impl Alignment { /// assert_eq!(Alignment::new(1024).unwrap().log2(), 10); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] - pub fn log2(self) -> u32 { + pub const fn log2(self) -> u32 { self.as_nonzero().trailing_zeros() } + + /// Returns a bit mask that can be used to match this alignment. + /// + /// This is equivalent to `!(self.as_usize() - 1)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_alignment_type)] + /// #![feature(ptr_mask)] + /// use std::ptr::{Alignment, NonNull}; + /// + /// #[repr(align(1))] struct Align1(u8); + /// #[repr(align(2))] struct Align2(u16); + /// #[repr(align(4))] struct Align4(u32); + /// let one = <NonNull<Align1>>::dangling().as_ptr(); + /// let two = <NonNull<Align2>>::dangling().as_ptr(); + /// let four = <NonNull<Align4>>::dangling().as_ptr(); + /// + /// assert_eq!(four.mask(Alignment::of::<Align1>().mask()), four); + /// assert_eq!(four.mask(Alignment::of::<Align2>().mask()), four); + /// assert_eq!(four.mask(Alignment::of::<Align4>().mask()), four); + /// assert_ne!(one.mask(Alignment::of::<Align4>().mask()), one); + /// ``` + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] + #[inline] + pub const fn mask(self) -> usize { + // SAFETY: The alignment is always nonzero, and therefore decrementing won't overflow. + !(unsafe { self.as_usize().unchecked_sub(1) }) + } } #[unstable(feature = "ptr_alignment_type", issue = "102070")] @@ -193,6 +228,14 @@ impl hash::Hash for Alignment { } } +/// Returns [`Alignment::MIN`], which is valid for any type. +#[unstable(feature = "ptr_alignment_type", issue = "102070")] +impl Default for Alignment { + fn default() -> Alignment { + Alignment::MIN + } +} + #[cfg(target_pointer_width = "16")] type AlignmentEnum = AlignmentEnum16; #[cfg(target_pointer_width = "32")] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 97f936fbd9e..36685f756d0 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -480,8 +480,8 @@ impl<T: ?Sized> *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset(self, count: isize) -> Self { @@ -561,8 +561,8 @@ impl<T: ?Sized> *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_offset(self, count: isize) -> Self { self.cast::<u8>().wrapping_offset(count).with_metadata_of(self) @@ -728,8 +728,8 @@ impl<T: ?Sized> *const T { /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from<U: ?Sized>(self, origin: *const U) -> isize { @@ -955,8 +955,8 @@ impl<T: ?Sized> *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_add(self, count: usize) -> Self { @@ -1049,8 +1049,8 @@ impl<T: ?Sized> *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_sub(self, count: usize) -> Self { @@ -1130,8 +1130,8 @@ impl<T: ?Sized> *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_add(self, count: usize) -> Self { self.cast::<u8>().wrapping_add(count).with_metadata_of(self) @@ -1209,8 +1209,8 @@ impl<T: ?Sized> *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_sub(self, count: usize) -> Self { self.cast::<u8>().wrapping_sub(count).with_metadata_of(self) diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 64695d63f0c..bc362fb627f 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -495,8 +495,8 @@ impl<T: ?Sized> *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset(self, count: isize) -> Self { @@ -575,8 +575,8 @@ impl<T: ?Sized> *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_offset(self, count: isize) -> Self { self.cast::<u8>().wrapping_offset(count).with_metadata_of(self) @@ -900,8 +900,8 @@ impl<T: ?Sized> *mut T { /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from<U: ?Sized>(self, origin: *const U) -> isize { @@ -1056,8 +1056,8 @@ impl<T: ?Sized> *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_add(self, count: usize) -> Self { @@ -1150,8 +1150,8 @@ impl<T: ?Sized> *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_sub(self, count: usize) -> Self { @@ -1231,8 +1231,8 @@ impl<T: ?Sized> *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_add(self, count: usize) -> Self { self.cast::<u8>().wrapping_add(count).with_metadata_of(self) @@ -1310,8 +1310,8 @@ impl<T: ?Sized> *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pointer_byte_offsets", since = "1.75.0")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_sub(self, count: usize) -> Self { self.cast::<u8>().wrapping_sub(count).with_metadata_of(self) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 50127b27f8b..90c346e98bc 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -835,8 +835,6 @@ impl<T, E> Result<T, E> { /// # Examples /// /// ``` - /// #![feature(result_option_inspect)] - /// /// let x: u8 = "4" /// .parse::<u8>() /// .inspect(|x| println!("original: {x}")) @@ -844,7 +842,7 @@ impl<T, E> Result<T, E> { /// .expect("failed to parse number"); /// ``` #[inline] - #[unstable(feature = "result_option_inspect", issue = "91345")] + #[stable(feature = "result_option_inspect", since = "CURRENT_RUSTC_VERSION")] pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self { if let Ok(ref t) = self { f(t); @@ -858,8 +856,6 @@ impl<T, E> Result<T, E> { /// # Examples /// /// ``` - /// #![feature(result_option_inspect)] - /// /// use std::{fs, io}; /// /// fn read() -> io::Result<String> { @@ -868,7 +864,7 @@ impl<T, E> Result<T, E> { /// } /// ``` #[inline] - #[unstable(feature = "result_option_inspect", issue = "91345")] + #[stable(feature = "result_option_inspect", since = "CURRENT_RUSTC_VERSION")] pub fn inspect_err<F: FnOnce(&E)>(self, f: F) -> Self { if let Err(ref e) = self { f(e); diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index d5d6d60acf6..701e61e6615 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -806,6 +806,8 @@ unsafe impl<'a, const N: usize> ReverseSearcher<'a> for CharArraySearcher<'a, N> searcher_methods!(reverse); } +impl<'a, const N: usize> DoubleEndedSearcher<'a> for CharArraySearcher<'a, N> {} + /// Searches for chars that are equal to any of the [`char`]s in the array. /// /// # Examples @@ -826,6 +828,8 @@ unsafe impl<'a, 'b, const N: usize> ReverseSearcher<'a> for CharArrayRefSearcher searcher_methods!(reverse); } +impl<'a, 'b, const N: usize> DoubleEndedSearcher<'a> for CharArrayRefSearcher<'a, 'b, N> {} + ///////////////////////////////////////////////////////////////////////////// // Impl for &[char] ///////////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index f83f60857a2..5f1f41e6865 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -444,7 +444,7 @@ impl AtomicBool { /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses - #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_from_ptr", since = "1.75.0")] #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")] pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool { // SAFETY: guaranteed by the caller @@ -1242,7 +1242,7 @@ impl<T> AtomicPtr<T> { /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses - #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_from_ptr", since = "1.75.0")] #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")] pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr<T> { // SAFETY: guaranteed by the caller @@ -2218,7 +2218,7 @@ macro_rules! atomic_int { /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses - #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_from_ptr", since = "1.75.0")] #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")] pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type { // SAFETY: guaranteed by the caller diff --git a/library/core/tests/fmt/num.rs b/library/core/tests/fmt/num.rs index b9ede65c9ff..bc387a46ea7 100644 --- a/library/core/tests/fmt/num.rs +++ b/library/core/tests/fmt/num.rs @@ -128,28 +128,43 @@ fn test_format_int_exp_precision() { let big_int: u32 = 314_159_265; assert_eq!(format!("{big_int:.1e}"), format!("{:.1e}", f64::from(big_int))); - //test adding precision + // test adding precision assert_eq!(format!("{:.10e}", i8::MIN), "-1.2800000000e2"); assert_eq!(format!("{:.10e}", i16::MIN), "-3.2768000000e4"); assert_eq!(format!("{:.10e}", i32::MIN), "-2.1474836480e9"); assert_eq!(format!("{:.20e}", i64::MIN), "-9.22337203685477580800e18"); assert_eq!(format!("{:.40e}", i128::MIN), "-1.7014118346046923173168730371588410572800e38"); - //test rounding + // test rounding assert_eq!(format!("{:.1e}", i8::MIN), "-1.3e2"); assert_eq!(format!("{:.1e}", i16::MIN), "-3.3e4"); assert_eq!(format!("{:.1e}", i32::MIN), "-2.1e9"); assert_eq!(format!("{:.1e}", i64::MIN), "-9.2e18"); assert_eq!(format!("{:.1e}", i128::MIN), "-1.7e38"); - //test huge precision + // test huge precision assert_eq!(format!("{:.1000e}", 1), format!("1.{}e0", "0".repeat(1000))); //test zero precision assert_eq!(format!("{:.0e}", 1), format!("1e0",)); assert_eq!(format!("{:.0e}", 35), format!("4e1",)); - //test padding with precision (and sign) + // test padding with precision (and sign) assert_eq!(format!("{:+10.3e}", 1), " +1.000e0"); + + // test precision remains correct when rounding to next power + #[cfg(miri)] // can't cover all of `i16` in Miri + let range = [i16::MIN, -1, 1, i16::MAX]; + #[cfg(not(miri))] + let range = i16::MIN..=i16::MAX; + for i in range { + for p in 0..=5 { + assert_eq!( + format!("{i:.p$e}"), + format!("{:.p$e}", f32::from(i)), + "integer {i} at precision {p}" + ); + } + } } #[test] diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index 5defeb50d40..5dc012bab4a 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -568,3 +568,11 @@ fn zip_unzip_roundtrip() { let a = z.unzip(); assert_eq!(a, (x, y)); } + +#[test] +fn as_slice() { + assert_eq!(Some(42).as_slice(), &[42]); + assert_eq!(Some(43).as_mut_slice(), &[43]); + assert_eq!(None::<i32>.as_slice(), &[]); + assert_eq!(None::<i32>.as_mut_slice(), &[]); +} diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 991fdb1256d..d3f1fba9369 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -17,8 +17,8 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] // This library is copied into rust-analyzer to allow loading rustc compiled proc macros. // Please avoid unstable features where possible to minimize the amount of changes necessary // to make it compile with rust-analyzer on stable. diff --git a/library/std/build.rs b/library/std/build.rs index ad0a82eab8c..11ba29766c1 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -25,7 +25,6 @@ fn main() { || target.contains("vxworks") || target.contains("wasm32") || target.contains("wasm64") - || target.contains("asmjs") || target.contains("espidf") || target.contains("solid") || target.contains("nintendo-3ds") diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 61c39133617..4310e108303 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -189,7 +189,7 @@ pub struct OpenOptions(fs_imp::OpenOptions); /// Representation of the various timestamps on a file. #[derive(Copy, Clone, Debug, Default)] -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub struct FileTimes(fs_imp::FileTimes); /// Representation of the various permissions on a file. @@ -688,7 +688,7 @@ impl File { /// Ok(()) /// } /// ``` - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] #[doc(alias = "futimens")] #[doc(alias = "futimes")] #[doc(alias = "SetFileTime")] @@ -699,7 +699,7 @@ impl File { /// Changes the modification time of the underlying file. /// /// This is an alias for `set_times(FileTimes::new().set_modified(time))`. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] #[inline] pub fn set_modified(&self, time: SystemTime) -> io::Result<()> { self.set_times(FileTimes::new().set_modified(time)) @@ -1413,20 +1413,20 @@ impl FileTimes { /// Create a new `FileTimes` with no times set. /// /// Using the resulting `FileTimes` in [`File::set_times`] will not modify any timestamps. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] pub fn new() -> Self { Self::default() } /// Set the last access time of a file. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] pub fn set_accessed(mut self, t: SystemTime) -> Self { self.0.set_accessed(t.into_inner()); self } /// Set the last modified time of a file. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] pub fn set_modified(mut self, t: SystemTime) -> Self { self.0.set_modified(t.into_inner()); self @@ -1440,7 +1440,7 @@ impl AsInnerMut<fs_imp::FileTimes> for FileTimes { } // For implementing OS extension traits in `std::os` -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl Sealed for FileTimes {} impl Permissions { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 55aafc3db33..6c7494a6a6f 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -507,6 +507,16 @@ impl<R: ?Sized + Seek> Seek for BufReader<R> { ) }) } + + /// Seeks relative to the current position. + /// + /// If the new position lies within the buffer, the buffer will not be + /// flushed, allowing for more efficient seeks. This method does not return + /// the location of the underlying reader, so the caller must track this + /// information themselves if it is required. + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + self.seek_relative(offset) + } } impl<T: ?Sized> SizeHint for BufReader<T> { diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 256b043a609..d8c8d933eb4 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -476,7 +476,7 @@ impl<A: Allocator> Read for VecDeque<u8, A> { } /// BufRead is implemented for `VecDeque<u8>` by reading bytes from the front of the `VecDeque`. -#[stable(feature = "vecdeque_buf_read", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] impl<A: Allocator> BufRead for VecDeque<u8, A> { /// Returns the contents of the "front" slice as returned by /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7d70a0bac24..ff5b4e76d44 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1957,6 +1957,36 @@ pub trait Seek { fn stream_position(&mut self) -> Result<u64> { self.seek(SeekFrom::Current(0)) } + + /// Seeks relative to the current position. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but + /// doesn't return the new position which can allow some implementations + /// such as [`BufReader`] to perform more efficient seeks. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_seek_relative)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// f.seek_relative(10)?; + /// assert_eq!(f.stream_position()?, 10); + /// Ok(()) + /// } + /// ``` + /// + /// [`BufReader`]: crate::io::BufReader + #[unstable(feature = "seek_seek_relative", issue = "117374")] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } } /// Enumeration of possible methods to seek within an I/O object. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f716c0fe922..d06012c14dc 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -227,7 +227,7 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![doc(rust_logo)] #![doc(cfg_hide( not(test), not(any(test, bootstrap)), diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs index 0d2a7189032..e5df4de0b7f 100644 --- a/library/std/src/os/ios/fs.rs +++ b/library/std/src/os/ios/fs.rs @@ -144,14 +144,14 @@ impl MetadataExt for Metadata { } /// OS-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs index 12c0293285a..8fb6e99ecfa 100644 --- a/library/std/src/os/l4re/raw.rs +++ b/library/std/src/os/l4re/raw.rs @@ -31,7 +31,6 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; target_arch = "powerpc", target_arch = "sparc", target_arch = "arm", - target_arch = "asmjs", target_arch = "wasm32" ))] mod arch { diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index a568f9b26ba..c29dd62bc06 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -31,7 +31,6 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; target_arch = "powerpc", target_arch = "sparc", target_arch = "arm", - target_arch = "asmjs", target_arch = "wasm32" ))] mod arch { diff --git a/library/std/src/os/macos/fs.rs b/library/std/src/os/macos/fs.rs index 098b0733723..573426d1a86 100644 --- a/library/std/src/os/macos/fs.rs +++ b/library/std/src/os/macos/fs.rs @@ -150,14 +150,14 @@ impl MetadataExt for Metadata { } /// OS-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/os/watchos/fs.rs b/library/std/src/os/watchos/fs.rs index 2838501817c..ee215dd5984 100644 --- a/library/std/src/os/watchos/fs.rs +++ b/library/std/src/os/watchos/fs.rs @@ -144,14 +144,14 @@ impl MetadataExt for Metadata { } /// OS-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 3b591a35dd7..1b013d1c154 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -528,14 +528,14 @@ impl FileTypeExt for fs::FileType { } /// Windows-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] pub trait FileTimesExt: Sealed { /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "file_set_times", since = "1.75.0")] fn set_created(self, t: SystemTime) -> Self; } -#[stable(feature = "file_set_times", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "file_set_times", since = "1.75.0")] impl FileTimesExt for fs::FileTimes { fn set_created(mut self, t: SystemTime) -> Self { self.as_inner_mut().set_created(t.into_inner()); diff --git a/library/std/src/process.rs b/library/std/src/process.rs index ad29eeb6a0b..af6bef1a76e 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1961,7 +1961,7 @@ impl ExitCode { } /// The default value is [`ExitCode::SUCCESS`] -#[stable(feature = "process_exitcode_default", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "process_exitcode_default", since = "1.75.0")] impl Default for ExitCode { fn default() -> Self { ExitCode::SUCCESS diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index ac7c800ff6f..5d8967bfbe6 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -532,7 +532,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { } #[stable(feature = "std_debug", since = "1.16.0")] -impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } @@ -546,7 +546,7 @@ impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> { } #[stable(feature = "std_debug", since = "1.16.0")] -impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index d58aa6c27b8..b7357460f39 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -14,7 +14,6 @@ use crate::ptr; target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc", - target_arch = "asmjs", target_arch = "wasm32", target_arch = "hexagon", all(target_arch = "riscv32", not(target_os = "espidf")), diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 3bb492fa98b..3d4ba509829 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -174,17 +174,6 @@ pub mod os { pub const EXE_EXTENSION: &str = "elf"; } -#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "emscripten"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ".js"; - pub const EXE_EXTENSION: &str = "js"; -} - #[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 40eb910fdc3..e3455cfef33 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1300,13 +1300,17 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))] - let to_timespec = |time: Option<SystemTime>| { - match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")), - Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time")), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - } + let to_timespec = |time: Option<SystemTime>| match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time" + )), + Some(_) => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time" + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] { diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index dc3c037c0cb..077698a462c 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -274,15 +274,19 @@ pub fn current_exe() -> io::Result<PathBuf> { return path.canonicalize(); } // Search PWD to infer current_exe. - if let Some(pstr) = path.to_str() && pstr.contains("/") { + if let Some(pstr) = path.to_str() + && pstr.contains("/") + { return getcwd().map(|cwd| cwd.join(path))?.canonicalize(); } // Search PATH to infer current_exe. if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) { for search_path in split_paths(&p) { let pb = search_path.join(&path); - if pb.is_file() && let Ok(metadata) = crate::fs::metadata(&pb) && - metadata.permissions().mode() & 0o111 != 0 { + if pb.is_file() + && let Ok(metadata) = crate::fs::metadata(&pb) + && metadata.permissions().mode() & 0o111 != 0 + { return pb.canonicalize(); } } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 437aae3ae7f..e8238665452 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -477,12 +477,13 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - let to_timestamp = |time: Option<SystemTime>| { - match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")), - None => Ok(0), - } + let to_timestamp = |time: Option<SystemTime>| match time { + Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), + Some(_) => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time" + )), + None => Ok(0), }; self.fd.filestat_set_times( to_timestamp(times.accessed)?, diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index 8c0e07b3566..f7f9f96ba5d 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -104,7 +104,9 @@ pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> { // The meaning of verbatim paths can change when they use a different // separator. - if let Some(parser) = parser.strip_prefix(r"?\") && !parser.prefix_bytes().iter().any(|&x| x == b'/') { + if let Some(parser) = parser.strip_prefix(r"?\") + && !parser.prefix_bytes().iter().any(|&x| x == b'/') + { // \\?\ if let Some(parser) = parser.strip_prefix(r"UNC\") { // \\?\UNC\server\share diff --git a/library/std/src/sys_common/once/futex.rs b/library/std/src/sys_common/once/futex.rs index 42db5fad4b4..609085dcd47 100644 --- a/library/std/src/sys_common/once/futex.rs +++ b/library/std/src/sys_common/once/futex.rs @@ -128,7 +128,8 @@ impl Once { RUNNING | QUEUED => { // Set the state to QUEUED if it is not already. if state == RUNNING - && let Err(new) = self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire) + && let Err(new) = + self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire) { state = new; continue; diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 4097eb5549e..4b9ddd5aba1 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -548,10 +548,6 @@ impl Builder { let main = Box::new(main); // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the // lifetime change is justified. - #[cfg(bootstrap)] - let main = - unsafe { mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(main) }; - #[cfg(not(bootstrap))] let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) }; Ok(JoinInner { diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index f92ffa94e99..2fa5a8e5e38 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -16,8 +16,8 @@ #![unstable(feature = "test", issue = "50297")] #![doc(test(attr(deny(warnings))))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(internal_output_capture)] #![feature(staged_api)] #![feature(process_exitcode_internals)] diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index b98b13119e8..b27b524b873 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -17,4 +17,4 @@ lto = "off" [llvm] # Will download LLVM from CI if available on your platform. -download-ci-llvm = "if-available" +download-ci-llvm = "if-unchanged" diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml index f362c4111f1..087544397f5 100644 --- a/src/bootstrap/defaults/config.library.toml +++ b/src/bootstrap/defaults/config.library.toml @@ -13,4 +13,4 @@ lto = "off" [llvm] # Will download LLVM from CI if available on your platform. -download-ci-llvm = "if-available" +download-ci-llvm = "if-unchanged" diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml index 79424f28d27..6e6c3660027 100644 --- a/src/bootstrap/defaults/config.tools.toml +++ b/src/bootstrap/defaults/config.tools.toml @@ -21,4 +21,4 @@ compiler-docs = true [llvm] # Will download LLVM from CI if available on your platform. -download-ci-llvm = "if-available" +download-ci-llvm = "if-unchanged" diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs index dbbce6fe220..b61659cad35 100644 --- a/src/bootstrap/src/bin/rustdoc.rs +++ b/src/bootstrap/src/bin/rustdoc.rs @@ -70,9 +70,7 @@ fn main() { cmd.arg("--cfg=bootstrap"); } cmd.arg("-Zunstable-options"); - // #[cfg(bootstrap)] - cmd.arg("--check-cfg=values(bootstrap)"); - // cmd.arg("--check-cfg=cfg(bootstrap)"); + cmd.arg("--check-cfg=cfg(bootstrap)"); if verbose > 1 { eprintln!( diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 121925b56a0..ecaaf91aec1 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -376,12 +376,12 @@ impl Step for RustAnalyzer { let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; - builder.ensure(Std::new(target)); + builder.ensure(Rustc::new(target, builder)); let mut cargo = prepare_tool_cargo( builder, compiler, - Mode::ToolStd, + Mode::ToolRustc, target, cargo_subcommand(builder.kind), "src/tools/rust-analyzer", @@ -414,7 +414,7 @@ impl Step for RustAnalyzer { /// Cargo's output path in a given stage, compiled by a particular /// compiler for the specified target. fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::ToolStd, target).join(".rust-analyzer-check.stamp") + builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rust-analyzer-check.stamp") } } } @@ -463,10 +463,6 @@ macro_rules! tool_check_step { cargo.arg("--all-targets"); } - // Enable internal lints for clippy and rustdoc - // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` - // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 - cargo.rustflag("-Zunstable-options"); let _guard = builder.msg_check(&concat!(stringify!($name), " artifacts").to_lowercase(), target); run_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 7021a954358..c253dd2c6aa 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -592,7 +592,9 @@ impl Step for StdLink { .join("stage0/lib/rustlib") .join(&host) .join("codegen-backends"); - builder.cp_r(&stage0_codegen_backends, &sysroot_codegen_backends); + if stage0_codegen_backends.exists() { + builder.cp_r(&stage0_codegen_backends, &sysroot_codegen_backends); + } } } } @@ -885,7 +887,9 @@ impl Step for Rustc { } else if let Some(path) = &builder.config.rust_profile_use { if compiler.stage == 1 { cargo.rustflag(&format!("-Cprofile-use={path}")); - cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); + if builder.is_verbose() { + cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); + } true } else { false @@ -1001,6 +1005,13 @@ pub fn rustc_cargo_env( .env("CFG_RELEASE_CHANNEL", &builder.config.channel) .env("CFG_VERSION", builder.rust_version()); + // Some tools like Cargo detect their own git information in build scripts. When omit-git-hash + // is enabled in config.toml, we pass this environment variable to tell build scripts to avoid + // detecting git information on their own. + if builder.config.omit_git_hash { + cargo.env("CFG_OMIT_GIT_HASH", "1"); + } + if let Some(backend) = builder.config.default_codegen_backend() { cargo.env("CFG_DEFAULT_CODEGEN_BACKEND", backend); } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 950845b736f..c485481b9a1 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1630,14 +1630,11 @@ impl Step for Extended { prepare("rust-analysis"); prepare("clippy"); prepare("rust-analyzer"); - for tool in &["rust-docs", "rust-demangler", "miri"] { + for tool in &["rust-docs", "rust-demangler", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } } - if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("cranelift")) { - prepare("rustc-codegen-cranelift"); - } // create an 'uninstall' package builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); pkgbuild("uninstall"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index d2aa89dee28..254fbc72a8c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -369,7 +369,7 @@ impl Step for RustAnalyzer { // We don't need to build the whole Rust Analyzer for the proc-macro-srv test suite, // but we do need the standard library to be present. - builder.ensure(compile::Std::new(compiler, host)); + builder.ensure(compile::Rustc::new(compiler, host)); let workspace_path = "src/tools/rust-analyzer"; // until the whole RA test suite runs on `i686`, we only run @@ -378,7 +378,7 @@ impl Step for RustAnalyzer { let mut cargo = tool::prepare_tool_cargo( builder, compiler, - Mode::ToolStd, + Mode::ToolRustc, host, "test", crate_path, diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index d1bc05e51d7..37cd96e7c63 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -203,6 +203,16 @@ pub fn prepare_tool_cargo( if !features.is_empty() { cargo.arg("--features").arg(&features.join(", ")); } + + // Enable internal lints for clippy and rustdoc + // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` + // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 + // + // NOTE: We unconditionally set this here to avoid recompiling tools between `x check $tool` + // and `x test $tool` executions. + // See https://github.com/rust-lang/rust/issues/116538 + cargo.rustflag("-Zunstable-options"); + cargo } @@ -603,8 +613,7 @@ pub struct RustAnalyzer { } impl RustAnalyzer { - pub const ALLOW_FEATURES: &'static str = - "proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink"; + pub const ALLOW_FEATURES: &'static str = "rustc_private,proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink"; } impl Step for RustAnalyzer { @@ -636,7 +645,7 @@ impl Step for RustAnalyzer { compiler: self.compiler, target: self.target, tool: "rust-analyzer", - mode: Mode::ToolStd, + mode: Mode::ToolRustc, path: "src/tools/rust-analyzer", extra_features: vec!["rust-analyzer/in-rust-tree".to_owned()], is_optional_tool: false, diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index cd276674dee..142116f7276 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1404,9 +1404,6 @@ impl<'a> Builder<'a> { rustflags.arg("-Zunstable-options"); } - // #[cfg(bootstrap)] - let use_new_check_cfg_syntax = self.local_rebuild; - // Enable compile-time checking of `cfg` names, values and Cargo `features`. // // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like @@ -1414,17 +1411,9 @@ impl<'a> Builder<'a> { // features but cargo isn't involved in the #[path] process and so cannot pass the // complete list of features, so for that reason we don't enable checking of // features for std crates. - if use_new_check_cfg_syntax { - cargo.arg("-Zcheck-cfg"); - if mode == Mode::Std { - rustflags.arg("--check-cfg=cfg(feature,values(any()))"); - } - } else { - cargo.arg(if mode != Mode::Std { - "-Zcheck-cfg=names,values,output,features" - } else { - "-Zcheck-cfg=names,values,output" - }); + cargo.arg("-Zcheck-cfg"); + if mode == Mode::Std { + rustflags.arg("--check-cfg=cfg(feature,values(any()))"); } // Add extra cfg not defined in/by rustc @@ -1445,12 +1434,8 @@ impl<'a> Builder<'a> { .collect::<String>(), None => String::new(), }; - if use_new_check_cfg_syntax { - let values = values.strip_prefix(",").unwrap_or(&values); // remove the first `,` - rustflags.arg(&format!("--check-cfg=cfg({name},values({values}))")); - } else { - rustflags.arg(&format!("--check-cfg=values({name}{values})")); - } + let values = values.strip_prefix(",").unwrap_or(&values); // remove the first `,` + rustflags.arg(&format!("--check-cfg=cfg({name},values({values}))")); } } @@ -1466,11 +1451,7 @@ impl<'a> Builder<'a> { // We also declare that the flag is expected, which we need to do to not // get warnings about it being unexpected. hostflags.arg("-Zunstable-options"); - if use_new_check_cfg_syntax { - hostflags.arg("--check-cfg=cfg(bootstrap)"); - } else { - hostflags.arg("--check-cfg=values(bootstrap)"); - } + hostflags.arg("--check-cfg=cfg(bootstrap)"); // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0a9175aa3ea..fa8b0b20cec 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2113,6 +2113,8 @@ impl Config { match download_ci_llvm { None => self.channel == "dev" && llvm::is_ci_llvm_available(&self, asserts), Some(StringOrBool::Bool(b)) => b, + // FIXME: "if-available" is deprecated. Remove this block later (around mid 2024) + // to not break builds between the recent-to-old checkouts. Some(StringOrBool::String(s)) if s == "if-available" => { llvm::is_ci_llvm_available(&self, asserts) } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 33b8f1a7ce7..a57b09e2ef6 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -98,7 +98,7 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[ /* Extra values not defined in the built-in targets yet, but used in std */ (Some(Mode::Std), "target_env", Some(&["libnx"])), // (Some(Mode::Std), "target_os", Some(&[])), - (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])), + (Some(Mode::Std), "target_arch", Some(&["spirv", "nvptx", "xtensa"])), /* Extra names used by dependencies */ // FIXME: Used by serde_json, but we should not be triggering on external dependencies. (Some(Mode::Rustc), "no_btreemap_remove_entry", None), diff --git a/src/bootstrap/src/tests/config.rs b/src/bootstrap/src/tests/config.rs index 59bd52a94dc..a8106ca8ff9 100644 --- a/src/bootstrap/src/tests/config.rs +++ b/src/bootstrap/src/tests/config.rs @@ -24,19 +24,19 @@ fn download_ci_llvm() { } let parse_llvm = |s| parse(s).llvm_from_ci; - let if_available = parse_llvm("llvm.download-ci-llvm = \"if-available\""); + let if_unchanged = parse_llvm("llvm.download-ci-llvm = \"if-unchanged\""); assert!(parse_llvm("llvm.download-ci-llvm = true")); assert!(!parse_llvm("llvm.download-ci-llvm = false")); - assert_eq!(parse_llvm(""), if_available); - assert_eq!(parse_llvm("rust.channel = \"dev\""), if_available); + assert_eq!(parse_llvm(""), if_unchanged); + assert_eq!(parse_llvm("rust.channel = \"dev\""), if_unchanged); assert!(!parse_llvm("rust.channel = \"stable\"")); assert!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\"")); assert!(parse_llvm( - "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-available\"" + "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\"" )); assert!(!parse_llvm( - "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-available\"" + "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-unchanged\"" )); } diff --git a/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile b/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile deleted file mode 100644 index 07dcb9ea928..00000000000 --- a/src/ci/docker/host-x86_64/disabled/asmjs/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - xz-utils \ - bzip2 - -COPY scripts/emscripten.sh /scripts/ -RUN bash /scripts/emscripten.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/emsdk-portable -ENV PATH=$PATH:/emsdk-portable/upstream/emscripten/ -ENV PATH=$PATH:/emsdk-portable/node/12.9.1_64bit/bin/ -ENV BINARYEN_ROOT=/emsdk-portable/upstream/ - -ENV TARGETS=asmjs-unknown-emscripten - -# Use -O1 optimizations in the link step to reduce time spent optimizing JS. -ENV EMCC_CFLAGS=-O1 - -# Emscripten installation is user-specific -ENV NO_CHANGE_USER=1 - -ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS - -# This is almost identical to the wasm32-unknown-emscripten target, so -# running with assertions again is not useful -ENV NO_DEBUG_ASSERTIONS=1 -ENV NO_LLVM_ASSERTIONS=1 -ENV NO_OVERFLOW_CHECKS=1 diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 3372baed999..341e2de223a 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -75,8 +75,7 @@ ENV RUN_MAKE_TARGETS=$RUN_MAKE_TARGETS,thumbv7m-none-eabi ENV RUN_MAKE_TARGETS=$RUN_MAKE_TARGETS,thumbv7em-none-eabi ENV RUN_MAKE_TARGETS=$RUN_MAKE_TARGETS,thumbv7em-none-eabihf -ENV TARGETS=asmjs-unknown-emscripten -ENV TARGETS=$TARGETS,wasm32-unknown-emscripten +ENV TARGETS=wasm32-unknown-emscripten ENV TARGETS=$TARGETS,arm-unknown-linux-musleabi ENV TARGETS=$TARGETS,arm-unknown-linux-musleabihf ENV TARGETS=$TARGETS,armv5te-unknown-linux-gnueabi diff --git a/src/ci/run.sh b/src/ci/run.sh index ce0dd6018af..fa786c6c7e7 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -145,7 +145,7 @@ else # LLVM continuously on at least some builders to ensure it works, though. # (And PGO is its own can of worms). if [ "$NO_DOWNLOAD_CI_LLVM" = "" ]; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.download-ci-llvm=if-available" + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.download-ci-llvm=if-unchanged" else # When building for CI we want to use the static C++ Standard library # included with LLVM, since a dynamic libstdcpp may not be available. diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index cbb4d5765ca..77164ed4117 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -58,7 +58,7 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe" # Disable downloading CI LLVM on this builder; - # setting up clang-cl just above conflicts with the default if-available option. + # setting up clang-cl just above conflicts with the default if-unchanged option. ciCommandSetEnv NO_DOWNLOAD_CI_LLVM 1 fi diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 907e9c59f31..c1e79248003 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -148,7 +148,6 @@ target | std | notes `armv7a-none-eabi` | * | Bare ARMv7-A `armv7r-none-eabi` | * | Bare ARMv7-R `armv7r-none-eabihf` | * | Bare ARMv7-R, hardfloat -`asmjs-unknown-emscripten` | ✓ | asm.js via Emscripten `i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE [^x86_32-floats-x87] `i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87] `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, MUSL [^x86_32-floats-x87] diff --git a/src/doc/unstable-book/src/compiler-flags/llvm-module-flag.md b/src/doc/unstable-book/src/compiler-flags/llvm-module-flag.md new file mode 100644 index 00000000000..454ad0a9a6d --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/llvm-module-flag.md @@ -0,0 +1,12 @@ +# `llvm-module-flag` + +--------------------- + +This flag allows adding a key/value to the `!llvm.module.flags` metadata in the +LLVM-IR for a compiled Rust module. The syntax is + +`-Z llvm_module_flag=<name>:<type>:<value>:<behavior>` + +Currently only u32 values are supported but the type is required to be specified +for forward compatibility. The `behavior` element must match one of the named +LLVM [metadata behaviors](https://llvm.org/docs/LangRef.html#module-flags-metadata) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index eb946e82f39..007c5e113b7 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -723,7 +723,7 @@ where fn region_name(region: Region<'_>) -> Option<Symbol> { match *region { - ty::ReEarlyBound(r) => Some(r.name), + ty::ReEarlyParam(r) => Some(r.name), _ => None, } } @@ -743,7 +743,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for RegionReplacer<'a, 'tcx> { match *r { // These are the regions that can be seen in the AST. ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned().unwrap_or(r), - ty::ReEarlyBound(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, + ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, r => bug!("unexpected region: {r:?}"), } } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index ab5aec12fe7..0445daf0d4d 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -164,8 +164,8 @@ impl Cfg { /// Renders the configuration for human display, as a short HTML description. pub(crate) fn render_short_html(&self) -> String { let mut msg = Display(self, Format::ShortHtml).to_string(); - if self.should_capitalize_first_letter() && - let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) + if self.should_capitalize_first_letter() + && let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) { msg[i..i + 1].make_ascii_uppercase(); } @@ -516,7 +516,6 @@ impl<'a> fmt::Display for Display<'a> { (sym::target_arch, Some(arch)) => match arch.as_str() { "aarch64" => "AArch64", "arm" => "ARM", - "asmjs" => "JavaScript", "loongarch64" => "LoongArch LA64", "m68k" => "M68k", "csky" => "CSKY", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 974ba1e3bd9..f7532185d71 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -374,15 +374,17 @@ pub(crate) fn build_impl( // Only inline impl if the implemented trait is // reachable in rustdoc generated documentation - if !did.is_local() && let Some(traitref) = associated_trait { + if !did.is_local() + && let Some(traitref) = associated_trait + { let did = traitref.def_id; if !cx.cache.effective_visibilities.is_directly_public(tcx, did) { return; } - if let Some(stab) = tcx.lookup_stability(did) && - stab.is_unstable() && - stab.feature == sym::rustc_private + if let Some(stab) = tcx.lookup_stability(did) + && stab.is_unstable() + && stab.feature == sym::rustc_private { return; } @@ -514,7 +516,10 @@ pub(crate) fn build_impl( } while let Some(ty) = stack.pop() { - if let Some(did) = ty.def_id(&cx.cache) && !document_hidden && tcx.is_doc_hidden(did) { + if let Some(did) = ty.def_id(&cx.cache) + && !document_hidden + && tcx.is_doc_hidden(did) + { return; } if let Some(generics) = ty.generics() { @@ -580,7 +585,8 @@ fn build_module_items( let res = item.res.expect_non_local(); if let Some(def_id) = res.opt_def_id() && let Some(allowed_def_ids) = allowed_def_ids - && !allowed_def_ids.contains(&def_id) { + && !allowed_def_ids.contains(&def_id) + { continue; } if let Some(def_id) = res.mod_def_id() { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d33e41dc2b3..dc9098d3ade 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -54,7 +54,9 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< let mut inserted = FxHashSet::default(); items.extend(doc.foreigns.iter().map(|(item, renamed)| { let item = clean_maybe_renamed_foreign_item(cx, item, *renamed); - if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) { + if let Some(name) = item.name + && (cx.render_options.document_hidden || !item.is_doc_hidden()) + { inserted.insert((item.type_(), name)); } item @@ -85,7 +87,9 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< } let v = clean_maybe_renamed_item(cx, item, *renamed, *import_id); for item in &v { - if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) { + if let Some(name) = item.name + && (cx.render_options.document_hidden || !item.is_doc_hidden()) + { inserted.insert((item.type_(), name)); } } @@ -287,9 +291,9 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life ty::ReStatic => Some(Lifetime::statik()), _ if !region.has_name() => None, ty::ReBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) => Some(Lifetime(name)), - ty::ReEarlyBound(ref data) => Some(Lifetime(data.name)), + ty::ReEarlyParam(ref data) => Some(Lifetime(data.name)), ty::ReBound(..) - | ty::ReFree(..) + | ty::ReLateParam(..) | ty::ReVar(..) | ty::ReError(_) | ty::RePlaceholder(..) @@ -817,12 +821,7 @@ fn clean_ty_generics<'tcx>( { let pred = clean_predicate(*pred, cx)?; - bounds.extend( - pred.get_bounds() - .into_iter() - .flatten() - .cloned() - ); + bounds.extend(pred.get_bounds().into_iter().flatten().cloned()); if let Some(proj) = projection && let lhs = clean_projection(proj.map_bound(|p| p.projection_ty), cx, None) @@ -989,10 +988,8 @@ fn clean_proc_macro<'tcx>( cx: &mut DocContext<'tcx>, ) -> ItemKind { let attrs = cx.tcx.hir().attrs(item.hir_id()); - if kind == MacroKind::Derive && - let Some(derive_name) = attrs - .lists(sym::proc_macro_derive) - .find_map(|mi| mi.ident()) + if kind == MacroKind::Derive + && let Some(derive_name) = attrs.lists(sym::proc_macro_derive).find_map(|mi| mi.ident()) { *name = derive_name.name; } @@ -1154,7 +1151,9 @@ fn clean_fn_decl_with_args<'tcx>( hir::FnRetTy::Return(typ) => clean_ty(typ, cx), hir::FnRetTy::DefaultReturn(..) => Type::Tuple(Vec::new()), }; - if let Some(header) = header && header.is_async() { + if let Some(header) = header + && header.is_async() + { output = output.sugared_async_return_type(); } FnDecl { inputs: args, output, c_variadic: decl.c_variadic } @@ -1606,14 +1605,16 @@ fn first_non_private<'tcx>( continue; } - if let Some(def_id) = child.res.opt_def_id() && target_def_id == def_id { + if let Some(def_id) = child.res.opt_def_id() + && target_def_id == def_id + { let mut last_path_res = None; 'reexps: for reexp in child.reexport_chain.iter() { - if let Some(use_def_id) = reexp.id() && - let Some(local_use_def_id) = use_def_id.as_local() && - let Some(hir::Node::Item(item)) = hir.find_by_def_id(local_use_def_id) && - !item.ident.name.is_empty() && - let hir::ItemKind::Use(path, _) = item.kind + if let Some(use_def_id) = reexp.id() + && let Some(local_use_def_id) = use_def_id.as_local() + && let Some(hir::Node::Item(item)) = hir.find_by_def_id(local_use_def_id) + && !item.ident.name.is_empty() + && let hir::ItemKind::Use(path, _) = item.kind { for res in &path.res { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { @@ -1624,7 +1625,8 @@ fn first_non_private<'tcx>( // We never check for "cx.render_options.document_private" // because if a re-export is not fully public, it's never // documented. - cx.tcx.local_visibility(local_use_def_id).is_public() { + cx.tcx.local_visibility(local_use_def_id).is_public() + { break 'reexps; } last_path_res = Some((path, res)); @@ -1639,7 +1641,12 @@ fn first_non_private<'tcx>( // 1. We found a public reexport. // 2. We didn't find a public reexport so it's the "end type" path. if let Some((new_path, _)) = last_path_res { - return Some(first_non_private_clean_path(cx, path, new_path.segments, new_path.span)); + return Some(first_non_private_clean_path( + cx, + path, + new_path.segments, + new_path.span, + )); } // If `last_path_res` is `None`, it can mean two things: // @@ -1928,13 +1935,13 @@ fn clean_trait_object_lifetime_bound<'tcx>( // latter contrary to `clean_middle_region`. match *region { ty::ReStatic => Some(Lifetime::statik()), - ty::ReEarlyBound(region) if region.name != kw::Empty => Some(Lifetime(region.name)), + ty::ReEarlyParam(region) if region.name != kw::Empty => Some(Lifetime(region.name)), ty::ReBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) if name != kw::Empty => { Some(Lifetime(name)) } - ty::ReEarlyBound(_) + ty::ReEarlyParam(_) | ty::ReBound(..) - | ty::ReFree(_) + | ty::ReLateParam(_) | ty::ReVar(_) | ty::RePlaceholder(_) | ty::ReErased @@ -2304,7 +2311,9 @@ fn clean_middle_opaque_bounds<'tcx>( _ => return None, }; - if let Some(sized) = cx.tcx.lang_items().sized_trait() && trait_ref.def_id() == sized { + if let Some(sized) = cx.tcx.lang_items().sized_trait() + && trait_ref.def_id() == sized + { has_sized = true; return None; } @@ -2680,28 +2689,27 @@ fn add_without_unwanted_attributes<'hir>( let mut attr = attr.clone(); match attr.kind { ast::AttrKind::Normal(ref mut normal) => { - if let [ident] = &*normal.item.path.segments && - let ident = ident.ident.name && - ident == sym::doc + if let [ident] = &*normal.item.path.segments + && let ident = ident.ident.name + && ident == sym::doc { match normal.item.args { ast::AttrArgs::Delimited(ref mut args) => { - let tokens = - filter_tokens_from_list(&args.tokens, |token| { - !matches!( - token, - TokenTree::Token( - Token { - kind: TokenKind::Ident( - sym::hidden | sym::inline | sym::no_inline, - _, - ), - .. - }, - _, - ), - ) - }); + let tokens = filter_tokens_from_list(&args.tokens, |token| { + !matches!( + token, + TokenTree::Token( + Token { + kind: TokenKind::Ident( + sym::hidden | sym::inline | sym::no_inline, + _, + ), + .. + }, + _, + ), + ) + }); args.tokens = TokenStream::new(tokens); attrs.push((Cow::Owned(attr), import_parent)); } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 627f15e67ac..c35fb9ec788 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -146,7 +146,8 @@ pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics) }) = generics.params.iter_mut().find(|param| ¶m.name == arg) { param_bounds.extend(bounds.drain(..)); - } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred + } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = + &mut pred && let Some(GenericParamDef { kind: GenericParamDefKind::Lifetime { outlives: param_bounds }, .. diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 88ee4e3a2ab..871738cdc07 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1269,8 +1269,8 @@ impl GenericBound { pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { use rustc_hir::TraitBoundModifier as TBM; - if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self && - Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() + if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self + && Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() { return true; } @@ -1623,7 +1623,7 @@ impl Type { /// functions. pub(crate) fn sugared_async_return_type(self) -> Type { if let Type::ImplTrait(mut v) = self - && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _ )) = v.pop() + && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _)) = v.pop() && let Some(segment) = trait_.segments.pop() && let GenericArgs::AngleBracketed { mut bindings, .. } = segment.args && let Some(binding) = bindings.pop() diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 9ff00c1946f..eb47ff94d2e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -443,8 +443,8 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { return true; } - if let hir::ExprKind::Unary(hir::UnOp::Neg, expr) = &expr.kind && - let hir::ExprKind::Lit(_) = &expr.kind + if let hir::ExprKind::Unary(hir::UnOp::Neg, expr) = &expr.kind + && let hir::ExprKind::Lit(_) = &expr.kind { return true; } @@ -643,17 +643,16 @@ pub(crate) fn inherits_doc_hidden( ) -> bool { let hir = tcx.hir(); while let Some(id) = tcx.opt_local_parent(def_id) { - if let Some(stop_at) = stop_at && id == stop_at { + if let Some(stop_at) = stop_at + && id == stop_at + { return false; } def_id = id; if tcx.is_doc_hidden(def_id.to_def_id()) { return true; - } else if let Some(node) = hir.find_by_def_id(def_id) && - matches!( - node, - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }), - ) + } else if let Some(node) = hir.find_by_def_id(def_id) + && matches!(node, hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }),) { // `impl` blocks stand a bit on their own: unless they have `#[doc(hidden)]` directly // on them, they don't inherit it from the parent context. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 2412865801d..e551e37f181 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -597,15 +597,15 @@ pub(crate) fn make_test( loop { match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => { - if !found_main && - let ast::ItemKind::Fn(..) = item.kind && - item.ident.name == sym::main + if !found_main + && let ast::ItemKind::Fn(..) = item.kind + && item.ident.name == sym::main { found_main = true; } - if !found_extern_crate && - let ast::ItemKind::ExternCrate(original) = item.kind + if !found_extern_crate + && let ast::ItemKind::ExternCrate(original) = item.kind { // This code will never be reached if `crate_name` is none because // `found_extern_crate` is initialized to `true` if it is none. @@ -957,10 +957,10 @@ impl Collector { fn get_filename(&self) -> FileName { if let Some(ref source_map) = self.source_map { let filename = source_map.span_to_filename(self.position); - if let FileName::Real(ref filename) = filename && - let Ok(cur_dir) = env::current_dir() && - let Some(local_path) = filename.local_path() && - let Ok(path) = local_path.strip_prefix(&cur_dir) + if let FileName::Real(ref filename) = filename + && let Ok(cur_dir) = env::current_dir() + && let Some(local_path) = filename.local_path() + && let Ok(path) = local_path.strip_prefix(&cur_dir) { return path.to_owned().into(); } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index abff77253ea..d63bbe5896e 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -230,8 +230,8 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // If the impl is from a masked crate or references something from a // masked crate then remove it completely. - if let clean::ImplItem(ref i) = *item.kind && - (self.cache.masked_crates.contains(&item.item_id.krate()) + if let clean::ImplItem(ref i) = *item.kind + && (self.cache.masked_crates.contains(&item.item_id.krate()) || i.trait_ .as_ref() .map_or(false, |t| is_from_private_dep(self.tcx, self.cache, t.def_id())) @@ -249,9 +249,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = *item.kind && - let Some(trait_) = &i.trait_ && - !i.kind.is_blanket() + if let clean::ImplItem(ref i) = *item.kind + && let Some(trait_) = &i.trait_ + && !i.kind.is_blanket() { self.cache .implementors @@ -264,8 +264,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { if let Some(s) = item.name.or_else(|| { if item.is_stripped() { None - } else if let clean::ImportItem(ref i) = *item.kind && - let clean::ImportKind::Simple(s) = i.kind { + } else if let clean::ImportItem(ref i) = *item.kind + && let clean::ImportKind::Simple(s) = i.kind + { Some(s) } else { None @@ -357,7 +358,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { desc, parent, parent_idx: None, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = self.cache.parent_stack.last() { + impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = + self.cache.parent_stack.last() + { item_id.as_def_id() } else { None @@ -493,9 +496,11 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { clean::Type::Path { ref path } | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => { dids.insert(path.def_id()); - if let Some(generics) = path.generics() && - let ty::Adt(adt, _) = self.tcx.type_of(path.def_id()).instantiate_identity().kind() && - adt.is_fundamental() { + if let Some(generics) = path.generics() + && let ty::Adt(adt, _) = + self.tcx.type_of(path.def_id()).instantiate_identity().kind() + && adt.is_fundamental() + { for ty in generics { if let Some(did) = ty.def_id(self.cache) { dids.insert(did); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 29fd880af50..06bfd4652d6 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1166,13 +1166,17 @@ fn fmt_type<'cx>( // we need to surround them with angle brackets in some cases (e.g. `<dyn …>::P`). if f.alternate() { - if let Some(trait_) = trait_ && should_show_cast { + if let Some(trait_) = trait_ + && should_show_cast + { write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{:#}::", self_type.print(cx))? } } else { - if let Some(trait_) = trait_ && should_show_cast { + if let Some(trait_) = trait_ + && should_show_cast + { write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{}::", self_type.print(cx))? @@ -1268,16 +1272,23 @@ impl clean::Impl { write!(f, " for ")?; } - if let clean::Type::Tuple(types) = &self.for_ && - let [clean::Type::Generic(name)] = &types[..] && - (self.kind.is_fake_variadic() || self.kind.is_auto()) + if let clean::Type::Tuple(types) = &self.for_ + && let [clean::Type::Generic(name)] = &types[..] + && (self.kind.is_fake_variadic() || self.kind.is_auto()) { // Hardcoded anchor library/core/src/primitive_docs.rs // Link should match `# Trait implementations` - primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?; - } else if let clean::BareFunction(bare_fn) = &self.for_ && - let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] && - (self.kind.is_fake_variadic() || self.kind.is_auto()) + primitive_link_fragment( + f, + PrimitiveType::Tuple, + format_args!("({name}₁, {name}₂, …, {name}ₙ)"), + "#trait-implementations-1", + cx, + )?; + } else if let clean::BareFunction(bare_fn) = &self.for_ + && let [clean::Argument { type_: clean::Type::Generic(name), .. }] = + &bare_fn.decl.inputs.values[..] + && (self.kind.is_fake_variadic() || self.kind.is_auto()) { // Hardcoded anchor library/core/src/primitive_docs.rs // Link should match `# Trait implementations` @@ -1286,22 +1297,18 @@ impl clean::Impl { let unsafety = bare_fn.unsafety.print_with_space(); let abi = print_abi_with_space(bare_fn.abi); if f.alternate() { - write!( - f, - "{hrtb:#}{unsafety}{abi:#}", - )?; + write!(f, "{hrtb:#}{unsafety}{abi:#}",)?; } else { - write!( - f, - "{hrtb}{unsafety}{abi}", - )?; + write!(f, "{hrtb}{unsafety}{abi}",)?; } - let ellipsis = if bare_fn.decl.c_variadic { - ", ..." - } else { - "" - }; - primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?; + let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" }; + primitive_link_fragment( + f, + PrimitiveType::Tuple, + format_args!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), + "#trait-implementations-1", + cx, + )?; // Write output. if !bare_fn.decl.output.is_unit() { write!(f, " -> ")?; @@ -1447,7 +1454,9 @@ impl clean::FnDecl { let amp = if f.alternate() { "&" } else { "&" }; write!(f, "(")?; - if let Some(n) = line_wrapping_indent && !self.inputs.values.is_empty() { + if let Some(n) = line_wrapping_indent + && !self.inputs.values.is_empty() + { write!(f, "\n{}", Indent(n + 4))?; } for (i, input) in self.inputs.values.iter().enumerate() { diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index d8e36139a78..b762c8a1ce6 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -185,8 +185,8 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> { if self.pending_elems.is_empty() { return false; } - if let Some((_, parent_class)) = self.closing_tags.last() && - can_merge(current_class, Some(*parent_class), "") + if let Some((_, parent_class)) = self.closing_tags.last() + && can_merge(current_class, Some(*parent_class), "") { for (text, class) in self.pending_elems.iter() { string(self.out, Escape(text), *class, &self.href_context, false); @@ -194,7 +194,9 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> { } else { // We only want to "open" the tag ourselves if we have more than one pending and if the // current parent tag is not the same as our pending content. - let close_tag = if self.pending_elems.len() > 1 && let Some(current_class) = current_class { + let close_tag = if self.pending_elems.len() > 1 + && let Some(current_class) = current_class + { Some(enter_span(self.out, current_class, &self.href_context)) } else { None @@ -260,10 +262,12 @@ pub(super) fn write_code( Highlight::Token { text, class } => { // If we received a `ExitSpan` event and then have a non-compatible `Class`, we // need to close the `<span>`. - let need_current_class_update = if let Some(pending) = token_handler.pending_exit_span && - !can_merge(Some(pending), class, text) { - token_handler.handle_exit_span(); - true + let need_current_class_update = if let Some(pending) = + token_handler.pending_exit_span + && !can_merge(Some(pending), class, text) + { + token_handler.handle_exit_span(); + true // If the two `Class` are different, time to flush the current content and start // a new one. } else if !can_merge(token_handler.current_class, class, text) { @@ -293,7 +297,8 @@ pub(super) fn write_code( } } if should_add { - let closing_tag = enter_span(token_handler.out, class, &token_handler.href_context); + let closing_tag = + enter_span(token_handler.out, class, &token_handler.href_context); token_handler.closing_tags.push((closing_tag, class)); } @@ -302,8 +307,14 @@ pub(super) fn write_code( } Highlight::ExitSpan => { token_handler.current_class = None; - token_handler.pending_exit_span = - Some(token_handler.closing_tags.last().as_ref().expect("ExitSpan without EnterSpan").1); + token_handler.pending_exit_span = Some( + token_handler + .closing_tags + .last() + .as_ref() + .expect("ExitSpan without EnterSpan") + .1, + ); } }; }); @@ -466,7 +477,9 @@ impl<'a> PeekIter<'a> { } /// Returns the next item after the current one. It doesn't interfere with `peek_next` output. fn peek(&mut self) -> Option<&(TokenKind, &'a str)> { - if self.stored.is_empty() && let Some(next) = self.iter.next() { + if self.stored.is_empty() + && let Some(next) = self.iter.next() + { self.stored.push_back(next); } self.stored.front() diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 2807dfed072..39d7e120df9 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1119,10 +1119,10 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { return None; } let indices = self.parse_string(pos)?; - if let Some((_, c)) = self.inner.peek().copied() && - c != '{' && - !is_separator(c) && - c != '(' + if let Some((_, c)) = self.inner.peek().copied() + && c != '{' + && !is_separator(c) + && c != '(' { self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`")); return None; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 50777134d70..f0199703c4e 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -176,9 +176,9 @@ impl<'tcx> Context<'tcx> { let mut render_redirect_pages = self.render_redirect_pages; // If the item is stripped but inlined, links won't point to the item so no need to generate // a file for it. - if it.is_stripped() && - let Some(def_id) = it.def_id() && - def_id.is_local() + if it.is_stripped() + && let Some(def_id) = it.def_id() + && def_id.is_local() { if self.is_inside_inlined_module || self.shared.cache.inlined_items.contains(&def_id) { // For now we're forced to generate a redirect page for stripped items until @@ -371,7 +371,9 @@ impl<'tcx> Context<'tcx> { path = href.into_inner().to_string_lossy().into_owned(); - if let Some(c) = path.as_bytes().last() && *c != b'/' { + if let Some(c) = path.as_bytes().last() + && *c != b'/' + { path.push('/'); } @@ -741,9 +743,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { shared.fs.write(scrape_examples_help_file, v)?; } - if let Some(ref redirections) = shared.redirections && !redirections.borrow().is_empty() { - let redirect_map_path = - self.dst.join(crate_name.as_str()).join("redirect-map.json"); + if let Some(ref redirections) = shared.redirections + && !redirections.borrow().is_empty() + { + let redirect_map_path = self.dst.join(crate_name.as_str()).join("redirect-map.json"); let paths = serde_json::to_string(&*redirections.borrow()).unwrap(); shared.ensure_dir(&self.dst.join(crate_name.as_str()))?; shared.fs.write(redirect_map_path, paths)?; @@ -790,7 +793,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } } if !self.is_inside_inlined_module { - if let Some(def_id) = item.def_id() && self.cache().inlined_items.contains(&def_id) { + if let Some(def_id) = item.def_id() + && self.cache().inlined_items.contains(&def_id) + { self.is_inside_inlined_module = true; } } else if !self.cache().document_hidden && item.is_doc_hidden() { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index c52fa01bdc4..90691411f44 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -180,7 +180,9 @@ impl Serialize for IndexItemFunctionType { _ => seq.serialize_element(&self.output)?, } for constraint in &self.where_clause { - if let [one] = &constraint[..] && one.generics.is_none() { + if let [one] = &constraint[..] + && one.generics.is_none() + { seq.serialize_element(one)?; } else { seq.serialize_element(constraint)?; @@ -915,7 +917,9 @@ fn render_stability_since_raw_with_extra( containing_const_ver: Option<StableSince>, extra_class: &str, ) -> bool { - let stable_version = if ver != containing_ver && let Some(ver) = &ver { + let stable_version = if ver != containing_ver + && let Some(ver) = &ver + { since_to_string(ver) } else { None diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index d226701ba4a..8fdbef65135 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -974,8 +974,9 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // if any Types with the same name but different DefId have been found. let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default(); for implementor in implementors { - if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cache) && - !did.is_local() { + if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cache) + && !did.is_local() + { extern_crates.insert(did.krate); } match implementor.inner_impl().for_.without_borrowed_ref() { @@ -1152,9 +1153,10 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: .take(cx.current.len()) .chain(std::iter::once("trait.impl")) .collect(); - if let Some(did) = it.item_id.as_def_id() && - let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } && - let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) { + if let Some(did) = it.item_id.as_def_id() + && let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } + && let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) + { js_src_path.extend(fqp[..fqp.len() - 1].iter().copied()); js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap())); } else { @@ -1564,8 +1566,8 @@ fn should_show_enum_discriminant( ) -> bool { let mut has_variants_with_value = false; for variant in variants { - if let clean::VariantItem(ref var) = *variant.kind && - matches!(var.kind, clean::VariantKind::CLike) + if let clean::VariantItem(ref var) = *variant.kind + && matches!(var.kind, clean::VariantKind::CLike) { has_variants_with_value |= var.discriminant.is_some(); } else { @@ -1706,8 +1708,8 @@ fn item_variants( " rightside", ); w.write_str("<h3 class=\"code-header\">"); - if let clean::VariantItem(ref var) = *variant.kind && - let clean::VariantKind::CLike = var.kind + if let clean::VariantItem(ref var) = *variant.kind + && let clean::VariantKind::CLike = var.kind { display_c_like_variant( w, diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index af1dab59496..1284f69e5d7 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -228,10 +228,11 @@ pub(crate) fn build_index<'tcx>( let mut associated_item_duplicates = FxHashMap::<(isize, ItemType, Symbol), usize>::default(); for &item in &crate_items { - if item.impl_id.is_some() && let Some(parent_idx) = item.parent_idx { - let count = associated_item_duplicates - .entry((parent_idx, item.ty, item.name)) - .or_insert(0); + if item.impl_id.is_some() + && let Some(parent_idx) = item.parent_idx + { + let count = + associated_item_duplicates.entry((parent_idx, item.ty, item.name)).or_insert(0); *count += 1; } } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index ba4aaaff5a7..3d28937eb99 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -435,9 +435,9 @@ fn sidebar_deref_methods<'a>( } // Recurse into any further impls that might exist for `target` - if let Some(target_did) = target.def_id(c) && - let Some(target_impls) = c.impls.get(&target_did) && - let Some(target_deref_impl) = target_impls.iter().find(|i| { + if let Some(target_did) = target.def_id(c) + && let Some(target_impls) = c.impls.get(&target_did) + && let Some(target_deref_impl) = target_impls.iter().find(|i| { i.inner_impl() .trait_ .as_ref() @@ -445,14 +445,7 @@ fn sidebar_deref_methods<'a>( .unwrap_or(false) }) { - sidebar_deref_methods( - cx, - out, - target_deref_impl, - target_impls, - derefs, - used_links, - ); + sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs, used_links); } } } @@ -494,8 +487,13 @@ fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> { && it .name .or_else(|| { - if let clean::ImportItem(ref i) = *it.kind && - let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None } + if let clean::ImportItem(ref i) = *it.kind + && let clean::ImportKind::Simple(s) = i.kind + { + Some(s) + } else { + None + } }) .is_some() }) diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index 377daaeb9d4..ee581173a4a 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -42,37 +42,35 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>( let ty = tcx.type_of(ty_def_id).instantiate_identity(); let type_layout = tcx.layout_of(param_env.and(ty)); - let variants = - if let Ok(type_layout) = type_layout && - let Variants::Multiple { variants, tag, tag_encoding, .. } = - type_layout.layout.variants() && - !variants.is_empty() - { - let tag_size = - if let TagEncoding::Niche { .. } = tag_encoding { - 0 - } else if let Primitive::Int(i, _) = tag.primitive() { - i.size().bytes() - } else { - span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int") - }; - variants - .iter_enumerated() - .map(|(variant_idx, variant_layout)| { - let Adt(adt, _) = type_layout.ty.kind() else { - span_bug!(tcx.def_span(ty_def_id), "not an adt") - }; - let name = adt.variant(variant_idx).name; - let is_unsized = variant_layout.abi.is_unsized(); - let is_uninhabited = variant_layout.abi.is_uninhabited(); - let size = variant_layout.size.bytes() - tag_size; - let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size }; - (name, type_layout_size) - }) - .collect() + let variants = if let Ok(type_layout) = type_layout + && let Variants::Multiple { variants, tag, tag_encoding, .. } = + type_layout.layout.variants() + && !variants.is_empty() + { + let tag_size = if let TagEncoding::Niche { .. } = tag_encoding { + 0 + } else if let Primitive::Int(i, _) = tag.primitive() { + i.size().bytes() } else { - Vec::new() + span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int") }; + variants + .iter_enumerated() + .map(|(variant_idx, variant_layout)| { + let Adt(adt, _) = type_layout.ty.kind() else { + span_bug!(tcx.def_span(ty_def_id), "not an adt") + }; + let name = adt.variant(variant_idx).name; + let is_unsized = variant_layout.abi.is_unsized(); + let is_uninhabited = variant_layout.abi.is_uninhabited(); + let size = variant_layout.size.bytes() - tag_size; + let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size }; + (name, type_layout_size) + }) + .collect() + } else { + Vec::new() + }; let type_layout_size = tcx.layout_of(param_env.and(ty)).map(|layout| { let is_unsized = layout.abi.is_unsized(); diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index ce620c226de..e160ec12f52 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -90,7 +90,9 @@ impl LocalSourcesCollector<'_, '_> { ); let mut href = href.into_inner().to_string_lossy().into_owned(); - if let Some(c) = href.as_bytes().last() && *c != b'/' { + if let Some(c) = href.as_bytes().last() + && *c != b'/' + { href.push('/'); } let mut src_fname = p.file_name().expect("source has no filename").to_os_string(); @@ -212,7 +214,9 @@ impl SourceCollector<'_, '_> { let root_path = PathBuf::from("../../").join(root_path.into_inner()); let mut root_path = root_path.to_string_lossy(); - if let Some(c) = root_path.as_bytes().last() && *c != b'/' { + if let Some(c) = root_path.as_bytes().last() + && *c != b'/' + { root_path += "/"; } let mut cur = self.dst.join(cur.into_inner()); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 48c9a53a283..a3606f4302b 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1309,36 +1309,6 @@ function initSearch(rawSearchIndex) { } /** - * This function checks generics in search query `queryElem` can all be found in the - * search index (`fnType`), - * - * This function returns `true` if it matches, and also writes the results to mgensInout. - * It returns `false` if no match is found, and leaves mgensInout untouched. - * - * @param {FunctionType} fnType - The object to check. - * @param {QueryElement} queryElem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map<number,number>|null} mgensInout - Map functions generics to query generics. - * - * @return {boolean} - Returns true if a match, false otherwise. - */ - function checkGenerics(fnType, queryElem, whereClause, mgensInout) { - return unifyFunctionTypes( - fnType.generics, - queryElem.generics, - whereClause, - mgensInout, - mgens => { - if (mgensInout) { - for (const [fid, qid] of mgens.entries()) { - mgensInout.set(fid, qid); - } - } - return true; - } - ); - } - /** * This function checks if a list of search query `queryElems` can all be found in the * search index (`fnTypes`). * @@ -1561,7 +1531,7 @@ function initSearch(rawSearchIndex) { ) { // [] matches primitive:array or primitive:slice // if it matches, then we're fine, and this is an appropriate match candidate - } else if (fnType.id !== queryElem.id) { + } else if (fnType.id !== queryElem.id || queryElem.id === null) { return false; } // If the query elem has generics, and the function doesn't, @@ -1649,38 +1619,17 @@ function initSearch(rawSearchIndex) { * @return {boolean} - Returns true if the type matches, false otherwise. */ function checkType(row, elem, whereClause) { - if (row.id === null) { - // This is a pure "generic" search, no need to run other checks. - return row.generics.length > 0 - ? checkIfInList(row.generics, elem, whereClause) - : false; - } - - if (row.id < 0 && elem.id >= 0) { - const gid = (-row.id) - 1; - return checkIfInList(whereClause[gid], elem, whereClause); - } - - if (row.id < 0 && elem.id < 0) { - return true; + if (elem.id < 0) { + return row.id < 0 || checkIfInList(row.generics, elem, whereClause); } - - const matchesExact = row.id === elem.id; - const matchesArrayOrSlice = elem.id === typeNameIdOfArrayOrSlice && - (row.id === typeNameIdOfSlice || row.id === typeNameIdOfArray); - - if ((matchesExact || matchesArrayOrSlice) && - typePassesFilter(elem.typeFilter, row.ty)) { - if (elem.generics.length > 0) { - return checkGenerics(row, elem, whereClause, new Map()); - } - return true; + if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && + typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && + // special case + elem.id !== typeNameIdOfArrayOrSlice + ) { + return row.id === elem.id || checkIfInList(row.generics, elem, whereClause); } - - // If the current item does not match, try [unboxing] the generic. - // [unboxing]: - // https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf - return checkIfInList(row.generics, elem, whereClause); + return unifyFunctionTypes([row], [elem], whereClause); } function checkPath(contains, ty, maxEditDistance) { diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html index b8b7785a2a1..c75fc34a9df 100644 --- a/src/librustdoc/html/templates/type_layout.html +++ b/src/librustdoc/html/templates/type_layout.html @@ -54,7 +54,7 @@ <strong>Note:</strong> Encountered an error during type layout; {#+ #} the type failed to be normalized. {# #} </p> {# #} - {% when Err(LayoutError::Cycle) %} + {% when Err(LayoutError::Cycle(_)) %} <p> {# #} <strong>Note:</strong> Encountered an error during type layout; {#+ #} the type's layout depended on the type's layout itself. {# #} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 285923251f7..61376ab31dd 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -250,15 +250,16 @@ pub(crate) fn id_from_item_inner( // their parent module, which isn't present in the output JSON items. So // instead, we directly get the primitive symbol and convert it to u32 to // generate the ID. - if matches!(tcx.def_kind(def_id), DefKind::Mod) && - let Some(prim) = tcx.get_attrs(*def_id, sym::rustc_doc_primitive) - .find_map(|attr| attr.value_str()) { + if matches!(tcx.def_kind(def_id), DefKind::Mod) + && let Some(prim) = tcx + .get_attrs(*def_id, sym::rustc_doc_primitive) + .find_map(|attr| attr.value_str()) + { format!(":{}", prim.as_u32()) } else { - tcx - .opt_item_name(*def_id) - .map(|n| format!(":{}", n.as_u32())) - .unwrap_or_default() + tcx.opt_item_name(*def_id) + .map(|n| format!(":{}", n.as_u32())) + .unwrap_or_default() } } }; diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 27e8a27ba2f..c1d90020e87 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -80,9 +80,9 @@ impl<'tcx> JsonRenderer<'tcx> { // document primitive items in an arbitrary crate by using // `rustc_doc_primitive`. let mut is_primitive_impl = false; - if let clean::types::ItemKind::ImplItem(ref impl_) = *item.kind && - impl_.trait_.is_none() && - let clean::types::Type::Primitive(_) = impl_.for_ + if let clean::types::ItemKind::ImplItem(ref impl_) = *item.kind + && impl_.trait_.is_none() + && let clean::types::Type::Primitive(_) = impl_.for_ { is_primitive_impl = true; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index dda06d4c9c1..5144bbdaf5e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -53,6 +53,7 @@ extern crate rustc_interface; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_lint_defs; +extern crate rustc_log; extern crate rustc_macros; extern crate rustc_metadata; extern crate rustc_middle; @@ -175,7 +176,7 @@ pub fn main() { // in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml). init_logging(&handler); - rustc_driver::init_env_logger(&handler, "RUSTDOC_LOG"); + rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")); let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&handler) { Some(args) => main_args(&mut handler, &args, using_internal_features), @@ -796,14 +797,7 @@ fn main_args( let sess = compiler.session(); if sess.opts.describe_lints { - let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints()); - let registered_lints = if let Some(register_lints) = compiler.register_lints() { - register_lints(sess, &mut lint_store); - true - } else { - false - }; - rustc_driver::describe_lints(sess, &lint_store, registered_lints); + rustc_driver::describe_lints(sess); return Ok(()); } diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index d1c4cc1f595..a5f4972d409 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -79,9 +79,9 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - let def_id = item.item_id.expect_def_id().expect_local(); // check if parent is trait impl - if let Some(parent_def_id) = cx.tcx.opt_local_parent(def_id) && - let Some(parent_node) = cx.tcx.hir().find_by_def_id(parent_def_id) && - matches!( + if let Some(parent_def_id) = cx.tcx.opt_local_parent(def_id) + && let Some(parent_node) = cx.tcx.hir().find_by_def_id(parent_def_id) + && matches!( parent_node, hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }), diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index fcd07885894..1e8ece7e114 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -303,7 +303,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { Res::Def(DefKind::Enum, did) => match tcx.type_of(did).instantiate_identity().kind() { ty::Adt(def, _) if def.is_enum() => { if let Some(variant) = def.variants().iter().find(|v| v.name == variant_name) - && let Some(field) = variant.fields.iter().find(|f| f.name == variant_field_name) { + && let Some(field) = + variant.fields.iter().find(|f| f.name == variant_field_name) + { Ok((ty_res, field.did)) } else { Err(UnresolvedPath { @@ -973,7 +975,8 @@ impl LinkCollector<'_, '_> { && let Some(def_id) = item.item_id.as_def_id() && let Some(def_id) = def_id.as_local() && !self.cx.tcx.effective_visibilities(()).is_exported(def_id) - && !has_primitive_or_keyword_docs(&item.attrs.other_attrs) { + && !has_primitive_or_keyword_docs(&item.attrs.other_attrs) + { // Skip link resolution for non-exported items. return; } @@ -1250,9 +1253,10 @@ impl LinkCollector<'_, '_> { // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether. // However I'm not sure how to check that across crates. - if let Some(candidate) = candidates.get(0) && - candidate.0 == Res::Primitive(PrimitiveType::RawPointer) && - key.path_str.contains("::") // We only want to check this if this is an associated item. + if let Some(candidate) = candidates.get(0) + && candidate.0 == Res::Primitive(PrimitiveType::RawPointer) + && key.path_str.contains("::") + // We only want to check this if this is an associated item. { if key.item_id.is_local() && !self.cx.tcx.features().intra_doc_pointers { self.report_rawptr_assoc_feature_gate(diag.dox, &diag.link_range, diag.item); @@ -1318,8 +1322,8 @@ impl LinkCollector<'_, '_> { for other_ns in [TypeNS, ValueNS, MacroNS] { if other_ns != expected_ns { if let Ok(res) = - self.resolve(path_str, other_ns, item_id, module_id) && - !res.is_empty() + self.resolve(path_str, other_ns, item_id, module_id) + && !res.is_empty() { err = ResolutionFailure::WrongNamespace { res: full_res(self.cx.tcx, res[0]), @@ -1892,8 +1896,10 @@ fn resolution_failure( }; let is_struct_variant = |did| { if let ty::Adt(def, _) = tcx.type_of(did).instantiate_identity().kind() - && def.is_enum() - && let Some(variant) = def.variants().iter().find(|v| v.name == res.name(tcx)) { + && def.is_enum() + && let Some(variant) = + def.variants().iter().find(|v| v.name == res.name(tcx)) + { // ctor is `None` if variant is a struct variant.ctor.is_none() } else { diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index a57321b5822..b60d7d4c718 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -154,9 +154,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in new_items_external.iter().chain(new_items_local.iter()) { - if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = *it.kind && - trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() && - cleaner.keep_impl(for_, true) + if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = *it.kind + && trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() + && cleaner.keep_impl(for_, true) { let target = items .iter() @@ -250,8 +250,8 @@ impl<'cache> DocVisitor for ItemAndAliasCollector<'cache> { fn visit_item(&mut self, i: &Item) { self.items.insert(i.item_id); - if let TypeAliasItem(alias) = &*i.kind && - let Some(did) = alias.type_.def_id(self.cache) + if let TypeAliasItem(alias) = &*i.kind + && let Some(did) = alias.type_.def_id(self.cache) { self.items.insert(ItemId::DefId(did)); } diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 00d15a3ca8d..4a80c22ed08 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -213,7 +213,9 @@ fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> { .take_while(|(_, c)| is_id_start(*c) || is_id_continue(*c)) .reduce(|_accum, item| item) .and_then(|(new_pos, c)| is_id_start(c).then_some(new_pos)); - if let Some(new_pos) = new_pos && current_pos != new_pos { + if let Some(new_pos) = new_pos + && current_pos != new_pos + { current_pos = new_pos; continue; } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index b3561841594..df955421ba4 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -208,24 +208,25 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { // Because we don't inline in `maybe_inline_local` if the output format is JSON, // we need to make a special check for JSON output: we want to keep it unless it has // a `#[doc(hidden)]` attribute if the `for_` type is exported. - if let Some(did) = imp.for_.def_id(self.cache) && - !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) + if let Some(did) = imp.for_.def_id(self.cache) + && !imp.for_.is_assoc_ty() + && !self.should_keep_impl(&i, did) { debug!("ImplStripper: impl item for stripped type; removing"); return None; } - if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) && - !self.should_keep_impl(&i, did) { + if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) + && !self.should_keep_impl(&i, did) + { debug!("ImplStripper: impl item for stripped trait; removing"); return None; } if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) { for typaram in generics { - if let Some(did) = typaram.def_id(self.cache) && !self.should_keep_impl(&i, did) + if let Some(did) = typaram.def_id(self.cache) + && !self.should_keep_impl(&i, did) { - debug!( - "ImplStripper: stripped item in trait's generics; removing impl" - ); + debug!("ImplStripper: stripped item in trait's generics; removing impl"); return None; } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 549fd67e32a..d84a137987c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -135,14 +135,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // macro in the same module. let mut inserted = FxHashSet::default(); for child in self.cx.tcx.module_children_local(CRATE_DEF_ID) { - if !child.reexport_chain.is_empty() && - let Res::Def(DefKind::Macro(_), def_id) = child.res && - let Some(local_def_id) = def_id.as_local() && - self.cx.tcx.has_attr(def_id, sym::macro_export) && - inserted.insert(def_id) + if !child.reexport_chain.is_empty() + && let Res::Def(DefKind::Macro(_), def_id) = child.res + && let Some(local_def_id) = def_id.as_local() + && self.cx.tcx.has_attr(def_id, sym::macro_export) + && inserted.insert(def_id) { let item = self.cx.tcx.hir().expect_item(local_def_id); - top_level_module.items.insert((local_def_id, Some(item.ident.name)), (item, None, None)); + top_level_module + .items + .insert((local_def_id, Some(item.ident.name)), (item, None, None)); } } diff --git a/src/llvm-project b/src/llvm-project -Subproject fef3d7b14ede45d051dc688aae0bb8c8b02a056 +Subproject 7738295178045041669876bf32b0543ec8319a5 diff --git a/src/stage0.json b/src/stage0.json index 2b39e1b1158..3fb6a0d4ed0 100644 --- a/src/stage0.json +++ b/src/stage0.json @@ -18,385 +18,377 @@ "tool is executed." ], "compiler": { - "date": "2023-10-04", + "date": "2023-11-13", "version": "beta" }, "rustfmt": { - "date": "2023-10-04", + "date": "2023-11-13", "version": "nightly" }, "checksums_sha256": { - "dist/2023-10-04/cargo-beta-aarch64-apple-darwin.tar.gz": "a684803e2f7a6c5741b89d3cc471544978ca60256e1d9621978eb190c83e6e2a", - "dist/2023-10-04/cargo-beta-aarch64-apple-darwin.tar.xz": "f6b618c037bf5eb84e7e775fb646f1fd37fc175c321faab0c2a5b39eb4553729", - "dist/2023-10-04/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "47178d02b0d92eb0a1ce511a08ced08c5bf21571d2e560d64d7d00f9d7dc8d76", - "dist/2023-10-04/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "4580c08291405a349c384e34f5ccc728956ff169b7c86c528dbb1e4258a08a77", - "dist/2023-10-04/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "31ca3f2a4d11cdae30925f22154d833e1975b622dbc9bfeb73888fa8666d07f0", - "dist/2023-10-04/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "525dc3440d5bc10c29476e5dcf26c82887459b37fa4adb6f0c45d6f955a9b8e0", - "dist/2023-10-04/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "fac19194eb9261b0df166bccc757d77b78f12c015faf4fd02440978a835ac5b4", - "dist/2023-10-04/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "97e9e2970306e3148307f0a90720764bc74a9e161fe9efe09fb3919a6624e22f", - "dist/2023-10-04/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "805d20b6d572cf1fcfa9f036c9af7133284edf5b67c9ac2e635aaf4eae2d07a5", - "dist/2023-10-04/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "b482d11edee594d984c4da214a0d6dd3dd5538eabe6c6a04b8b36ad4d6918b9d", - "dist/2023-10-04/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "f052ee2d6b5f09d63cc43acbc3e13f044d39bc8cd411286b1fd0318802689e3f", - "dist/2023-10-04/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "3a428c893b46f7ef5e893438b48225d633190e2c99e475429a08556e16ed993e", - "dist/2023-10-04/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "cfdf1f990b6bdd43ad0e0b898065637ab422c4ca4489b78f0ed481684bd4c9a4", - "dist/2023-10-04/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "93239e235a34898e959d891f8d70a75e012f35af62f5576d1b2173fce8979d30", - "dist/2023-10-04/cargo-beta-i686-pc-windows-gnu.tar.gz": "a27be9e3c589d8d92a9b4d95fc6684d4f05951f985485cc516359ea600cb9b4d", - "dist/2023-10-04/cargo-beta-i686-pc-windows-gnu.tar.xz": "9339b0ee5395f75ee8ebfeaeb95582e5922e0f3fc268f59d47c2cc110844f269", - "dist/2023-10-04/cargo-beta-i686-pc-windows-msvc.tar.gz": "6bf3edf4612263ed698bb920e946065e964cd4f7fa6e864b029b8593997b1a0e", - "dist/2023-10-04/cargo-beta-i686-pc-windows-msvc.tar.xz": "46cd39b56d8a6d4724a99574907ddb81a6d170ceccca35a9201b8a61bd3e8190", - "dist/2023-10-04/cargo-beta-i686-unknown-linux-gnu.tar.gz": "f99efe464f62d1c11c035c8ee0fe649380522c8fce7273218ff8a818bbc1628d", - "dist/2023-10-04/cargo-beta-i686-unknown-linux-gnu.tar.xz": "32f68b074e6b9ca7bd216d8d924d0b796c3a6ba81b90d8a0ecbd1904a61867c2", - "dist/2023-10-04/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz": "d1b0179d8c8ab0541bfaf7830bafc0d384616785081c0149ebb80d009860261c", - "dist/2023-10-04/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz": "1265182fc1962018c2754359acbc7b7d07860bdd26eb388c8e332786fbbd678a", - "dist/2023-10-04/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "500f736aeea0a5648bd4703b772d9d9077272daf08c334c281d71f9c19150e1d", - "dist/2023-10-04/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "97e40806dfd8921b7c4347d2d6ebadb66dd315c3d30c0ba3e0b30fcc3de9f1d4", - "dist/2023-10-04/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "1bf735bbae45eb55f4156504299d41c169d3d923ca8615e333e181a0e4b744c4", - "dist/2023-10-04/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "2ab0afd75f20e290fac1063ac0ac8cec98f847918360d95326350671d3b0e05d", - "dist/2023-10-04/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "9092667b450f758c39420530a131e87bcf6c911cb3f2b0177ec6fa4dfb7e58fe", - "dist/2023-10-04/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "50417b4be7631069b8059df56f0834fc65951c5057124b681b7a97a00ff774b6", - "dist/2023-10-04/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "cfb8e229f98f43a612bbf0d6d29f42cbfe2ccae4e3ef03fbb0bd7ea4ced10154", - "dist/2023-10-04/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "32059ff422870f3d45f419f7a59de696fff9474aac1bc4ae20a527079b95cf86", - "dist/2023-10-04/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "d14942f7492df24a577464a2929c2251c1493a70d822b7c38b500e559fce18e8", - "dist/2023-10-04/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "f7688d17e33d49f6fe26a0e7d0d24dfa6fa99e3b1703f6b99dddca46f1080c89", - "dist/2023-10-04/cargo-beta-x86_64-apple-darwin.tar.gz": "9da00b7617cf14e3f5f32c6cfc8e0f4b4197747a6864a962dbaa8725279b52a4", - "dist/2023-10-04/cargo-beta-x86_64-apple-darwin.tar.xz": "a3735ff5fd823461eb411d11b1e675fa890907c859e353ec9c239fc85bc5df1e", - "dist/2023-10-04/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "9f98eb4b034acf6ee9315a7067a62668883d25fd95b306c3083feeb56b907ac5", - "dist/2023-10-04/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "93d0653eae1839dda931ba4e26afb32527a59ca3b4502778135cc5d9608bfeb6", - "dist/2023-10-04/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "23f8e3f8abb551e7c71b391b63d5a9514f49e03d89c0bf4877c94bfd9f1f3754", - "dist/2023-10-04/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "1e8c3996be4a94b54df4f1d5d5f514f254963ed479e40885cc943970d9c31ebb", - "dist/2023-10-04/cargo-beta-x86_64-unknown-freebsd.tar.gz": "3761bb7b4fb64fa4791d09b88fac9da690a422d611935f1627ea96dd43b1d229", - "dist/2023-10-04/cargo-beta-x86_64-unknown-freebsd.tar.xz": "d0a2bac9df9073f766ca0ac2a16b17b6ebf870913ca962c063e6cc9967a32ab8", - "dist/2023-10-04/cargo-beta-x86_64-unknown-illumos.tar.gz": "71d42631ac9867750c5ef86061b2ce6ac1fa56dc24eb07fe5fb13a97e2acffa1", - "dist/2023-10-04/cargo-beta-x86_64-unknown-illumos.tar.xz": "72002ff2f148fdd864fbd4a82ec4fe7fb33c6acf7a7186e30ee4a74759f593f9", - "dist/2023-10-04/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "774be8387ae3849a82447cc20b2bd1daf4d42c7a80faaf5090e153f78d96de9f", - "dist/2023-10-04/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "6f6794bf506362614b97d96f10d6d5171676205beedb655636d82e8344a47011", - "dist/2023-10-04/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "c5ef0aa9f422014bf4d1873f09c77818d2de51fd7d6c030b617cd106402a0f21", - "dist/2023-10-04/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "82ece6a6f7eff45d842ba1cd28e3e86ddac0c49fedca3039dac00d01d99a26e7", - "dist/2023-10-04/cargo-beta-x86_64-unknown-netbsd.tar.gz": "42627d918e4dbb692ef45ff162d7eb39e9126dc30f29cb96b787f0029d11e1d2", - "dist/2023-10-04/cargo-beta-x86_64-unknown-netbsd.tar.xz": "de92a4f9f58e728c63d47a39973c2ef396a073a6fa26bdadf0e833cee9722980", - "dist/2023-10-04/rust-std-beta-aarch64-apple-darwin.tar.gz": "150828b9c7e49d7555eaf989dd6fe84b2b30d06145f48b635ad04e893b537311", - "dist/2023-10-04/rust-std-beta-aarch64-apple-darwin.tar.xz": "c1006b9c0c7d0bf104146b52c92f004811988cc8b73474ef690208e78645a348", - "dist/2023-10-04/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "df8cd652323cdf204683789b902f440e8aa614b9950e096837bc3b6a32a58e80", - "dist/2023-10-04/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "f9bdde6063001b69002ad21777eb52b37ce78ea3ce04131cf81b8f40b0e16c84", - "dist/2023-10-04/rust-std-beta-aarch64-apple-ios.tar.gz": "db0362e805a012249fae465b205970035181a43a19a58d87dd475ebdbb986dc2", - "dist/2023-10-04/rust-std-beta-aarch64-apple-ios.tar.xz": "72fd22f528bb211b0d5ac807f590fac72b1698f7d118e1b3507e6994b3896cb0", - "dist/2023-10-04/rust-std-beta-aarch64-linux-android.tar.gz": "f691fd6cf51e9aad3a11c2b613646b06308ef9558fe04f274458b68a742d6efe", - "dist/2023-10-04/rust-std-beta-aarch64-linux-android.tar.xz": "65fc6a17622bcf0039b34699537f233d193b03514c039258395dff2de8ca5197", - "dist/2023-10-04/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "a99e89663dc6ade963dc13b468fc310ad7b8a17e4d417bc210a39feb6413422c", - "dist/2023-10-04/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "120579d0abbd68b692271290ae926f6677f97930ade6861ae71be0033db5437b", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-fuchsia.tar.gz": "7663940abb0a080daac14573199e81b3000d80a27dcfd8f3d8c6d295efa1216a", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-fuchsia.tar.xz": "59d7eef17b6cc40cf6af729abf173ecc43c03bf38516bf59fe27321db384972d", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "69647628d78604fdbdb830d23f661d31f997d77635ea0c123d1ca0017b18460d", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "93e15186a1ddd33f3cd7737ff5da28bccfd5644c9a579691611ee2e1308cdd4d", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "b8ac97fef649854f78a9290054728f40b90e5ce55dcde7cdd128e2a5a09224f0", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "4b0d7b2bf742425ddf024d7103b8651f662993038f66b9db915713ba8f039540", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "37847cacdcf142fc557bafcfd8b3ed3c9c5532757bf6c06e6493d21356e18f04", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "c64925fcbd6b7f97926fac45d789e70734d3895c46cc958c190815f8cd8f4989", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-none.tar.gz": "a566b9c1784c919317f33678f889a15107fd31a403d50f6cf42036ff14ae507f", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-none.tar.xz": "a1267921d98e56f36937e72a1dcd60624f94de6e9948fd86715f83fb152ff52b", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-uefi.tar.gz": "0fd6a5925d5a69ae7c7f277ab503ddeeef9168ea815492b21a77a07d8e49142e", - "dist/2023-10-04/rust-std-beta-aarch64-unknown-uefi.tar.xz": "8477f0f15e1fd594b05e918f1940ee56a9458353eaac83f355d935ff2971df20", - "dist/2023-10-04/rust-std-beta-arm-linux-androideabi.tar.gz": "63ea0d1311b4c7a10a185dad0f13e53cac6f7de0047b96e6f23f872f84243339", - "dist/2023-10-04/rust-std-beta-arm-linux-androideabi.tar.xz": "9b35c8013417cdd08480640c609c3e0384814944b81e6f85f8741f53a768c944", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "290589b2438e6ef92366d86916d33f19504dbbe7adeb3e8b12786e524080fee2", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "ce39b799f490f0f7c8d166177d6a4174641872d2fbbd80d7d94dadfc1603b77e", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "5def5470a2c06adb70ab6373b35abdb2404987a41ea2b324d8852849540ed503", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "dfa329c8da739dab40cd111d9399ae2e38e7db8f8e07fda64117fa146b3d071c", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "94cbc1d6984b6018c373f5e7fd250f0bd2de4b4767f7f0c61a70c660576faf12", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "ec393c418b2ac3d2c97814b712648a0fd318dcdec381b4a49409dbb59079cb33", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "4765d35d61d4bb0a8d7dd9f2e6e1b17a9c27ac27fe24f111a42aca6b2dfe2fcf", - "dist/2023-10-04/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "07ee14b28ceec5e76f411b607d68cccb3bac17ed708e180081837f2e05c7057e", - "dist/2023-10-04/rust-std-beta-armebv7r-none-eabi.tar.gz": "f5f35b007304d6d25a189f000a89a64cebad035e3ab7d2f1d2225c7063575db8", - "dist/2023-10-04/rust-std-beta-armebv7r-none-eabi.tar.xz": "68ede435cb805a0e7d9c3bf172fda8c20486dcb2ab655cabc5ecfa25f8564955", - "dist/2023-10-04/rust-std-beta-armebv7r-none-eabihf.tar.gz": "53f1586ef3640a35e36d2b3b4caf62e444d43da64d744d99323f9b238fd0981d", - "dist/2023-10-04/rust-std-beta-armebv7r-none-eabihf.tar.xz": "404517420148caad58da5c5903d6b64b00aaef728dc9146f1c85d3675b2f7944", - "dist/2023-10-04/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "333a58268238a43864b65f51d5731c76e43f56f6f7c556057307d36d1c47bfd2", - "dist/2023-10-04/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "fe4ac5283e9025f7ec919d324592a3b88cf8ed02bfb6bd077d46a452af1fd723", - "dist/2023-10-04/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "ce2edf3faf28d92d2ecace77207e0a5b68aacbaf0f202e79503fabf913472f17", - "dist/2023-10-04/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "12df2bab8ce8301e3dc6eca8693b19eafe31894c285c013bc4445596cc93e49a", - "dist/2023-10-04/rust-std-beta-armv7-linux-androideabi.tar.gz": "f15088dd162f584123f07d58303e5aef3ed12a479ad6e351ad9c676c9d7953e1", - "dist/2023-10-04/rust-std-beta-armv7-linux-androideabi.tar.xz": "8ac08cc08e29b8aa4425889b5ed7f309ab98a3af51c66f2a40c5a5c62efbef97", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "d4b8fbf3b3d96e893145aa2868f6a470ad70bd946793fc49c435eb6fc921a00e", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "153c726bc061d8cd01f5c6c1786d12d04e699505a927ad5ee0e385ee1ea9ded7", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "4dfe33527e1c54cd0a9ff653e6c81376a01119bd894f5db47fb7c716464fc876", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "42081a3129ffd33ff6d9bbc10a10f214f909b4700255fa57decf82161e095d85", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "c1693a1887633f4c0baef1bb9910c502df5a70f2deaf00ead2a40d7ad210216a", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "a1f0068e719d231a6c1ec12055b883773584d9fba609f01fdc32a0dc856bd516", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "7568f5a3e65a649f13481afe384cccf54789c99fe74e62365b4922e649a36f2a", - "dist/2023-10-04/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "ad2989860396bc23ca4337887cd9865fa8a4e930d648df50f193330011f2e65e", - "dist/2023-10-04/rust-std-beta-armv7a-none-eabi.tar.gz": "0ed22040479e23cbf3e82087636c6ea30d75139af30db9e95b8ea7847be1184a", - "dist/2023-10-04/rust-std-beta-armv7a-none-eabi.tar.xz": "d182765ec6d63e0fbf2c6af1ecf57c475dda93c03676e8f3958da74aa47fa168", - "dist/2023-10-04/rust-std-beta-armv7r-none-eabi.tar.gz": "cdc524d00fbafccc363b456c32cc6410cefe98d9c1d37c1e5a22db986b600e7d", - "dist/2023-10-04/rust-std-beta-armv7r-none-eabi.tar.xz": "8815ae220f5bf7bced09274a1e17dd0f95e81ef5cbe1938b738298835c47001b", - "dist/2023-10-04/rust-std-beta-armv7r-none-eabihf.tar.gz": "c641416a3d67ca2a9b797d4800e5ac12a688a7af9daaeae10465c13d0f8cdeba", - "dist/2023-10-04/rust-std-beta-armv7r-none-eabihf.tar.xz": "3ce3dde8809d41f51534d9e8fd86f1c5f34b5450a2bdfffdc2d72e9e54af6ef0", - "dist/2023-10-04/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "a3700f29bbf1f7508a438ef7ecf4cfa25acef21bc9205285736f34840932388d", - "dist/2023-10-04/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "7a6927e77caad52d111366712697b3a7e6b3fa904e0a50eb4628b70d5a92717b", - "dist/2023-10-04/rust-std-beta-i586-pc-windows-msvc.tar.gz": "97c5d4837a4e4f019293c4dce265f6d09ddf3abdc7d7ab7f08ce0b62e0278b2c", - "dist/2023-10-04/rust-std-beta-i586-pc-windows-msvc.tar.xz": "f566a6b8083d3a1e0f5cff28a5131af4bce33a1519d7a7e6fb14d7ae349e3150", - "dist/2023-10-04/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "7a344a573529887631387d8e2f3e007653216f959a8f7c543e2c2cfb52673615", - "dist/2023-10-04/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "d6543b8eb696596fd35616fdc839af08f9a386412e7a8344ed92e08d840613cf", - "dist/2023-10-04/rust-std-beta-i586-unknown-linux-musl.tar.gz": "e8790ee297ef2661d8cead7875d3fa511d7ec4be5aac858562ddc107257648d7", - "dist/2023-10-04/rust-std-beta-i586-unknown-linux-musl.tar.xz": "8998fc7c0d7ecb5a06fae1799f2aa044cf3680546461ec9033115476f3acd2db", - "dist/2023-10-04/rust-std-beta-i686-linux-android.tar.gz": "f918380c6e01128fb16a047d6adc7a8fd3cb3c14bd18d25690ebc69c9a8f0f22", - "dist/2023-10-04/rust-std-beta-i686-linux-android.tar.xz": "cafedd439975af0f45894e111b89b03adee21136dd43558cb049608914b0a8fb", - "dist/2023-10-04/rust-std-beta-i686-pc-windows-gnu.tar.gz": "442d40afabdd05fbbe8b8570133dc119da6c6f8396a1cd6879a21b58c570dbbd", - "dist/2023-10-04/rust-std-beta-i686-pc-windows-gnu.tar.xz": "31c86d09c30d670a4915412d3fed90e0bc640f6b359ddb88ecc4c75f50b68b41", - "dist/2023-10-04/rust-std-beta-i686-pc-windows-msvc.tar.gz": "e0b23dd8a1a4805a1d13275e29edef5b5cfd34b271029262b943eda877c074c5", - "dist/2023-10-04/rust-std-beta-i686-pc-windows-msvc.tar.xz": "a954096bc423910cda8816093bcff11a3996d55239c19c3f9285f584057fdcde", - "dist/2023-10-04/rust-std-beta-i686-unknown-freebsd.tar.gz": "84c060e799148378223c56742d9bfef40b15252204f91bd49677083f48fff722", - "dist/2023-10-04/rust-std-beta-i686-unknown-freebsd.tar.xz": "faa471809786a5cef68807de3da9c0c9e0943b7f92014e232ab6693e1b081a9f", - "dist/2023-10-04/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "43ce123cd3f014b33444c853dc91a9921ba9af0b8d52e65c56386b7e300fa595", - "dist/2023-10-04/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "4792bc927d4dc0af996a47c4e82ab37244d0d28052b56abef6dfda60d8e58ac4", - "dist/2023-10-04/rust-std-beta-i686-unknown-linux-musl.tar.gz": "e609884e147deaa8265cd3f9150384d45d43689ba68cfe6e2ca003773d4f31f3", - "dist/2023-10-04/rust-std-beta-i686-unknown-linux-musl.tar.xz": "c27b005cc23453bc2ec9401515fc792d589619119f8f1006d8c9444f1592c1a7", - "dist/2023-10-04/rust-std-beta-i686-unknown-uefi.tar.gz": "e4fb77aefbc3ae63b81019eacfd35b6cee6f801ac2ff397a598d2ddaf84989dc", - "dist/2023-10-04/rust-std-beta-i686-unknown-uefi.tar.xz": "890d607b35053df912d0f3925f8c7af17ef3a66c267f31eadb8aada9b4491b3d", - "dist/2023-10-04/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz": "c07d08b9a17aebf127d01715259f5be3fc8e67d1e4a114a5f7eb9489c36ec529", - "dist/2023-10-04/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz": "27bffe29093dc3ad55cce741607a354035c8a2d40f8480b131c9608fbd9a86d9", - "dist/2023-10-04/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz": "ce7ebda42e41ad68924f2194c1dbb17f2487d2d6426669835e8dca3dc3f0f623", - "dist/2023-10-04/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz": "3564c11a5540b25f6c3ef24f47e64bf88d76aefacab01e0b28b0a48e4c0e9d73", - "dist/2023-10-04/rust-std-beta-loongarch64-unknown-none.tar.gz": "2aa6dfa17a120b2ef4e7899ed32a04e472725404b205739c12bff1829c05c7a9", - "dist/2023-10-04/rust-std-beta-loongarch64-unknown-none.tar.xz": "37cd947a8cc912e9c3fe07fd4b3ed310255cb45321ac48753cb8cbeee8b56fd4", - "dist/2023-10-04/rust-std-beta-mips-unknown-linux-musl.tar.gz": "5dc9a8b4a421a1be0fd4aff1256d98d76aa13ec38b10b0ba7fbb7ae7d0633354", - "dist/2023-10-04/rust-std-beta-mips-unknown-linux-musl.tar.xz": "c4762f98a836bd93c68fbac17c99e443bd05cae9897f3d1d3f6f39388659bfff", - "dist/2023-10-04/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "b1cb089447954c3a9d5244e2f08367db5fbd569eb986dc437cfa70f0dc5a9a6b", - "dist/2023-10-04/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "9d245bcfd07a430725c274504f5c8f80ea71027ca63f4442cdf4e950bb49683a", - "dist/2023-10-04/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "4bc42a59bb123188ea2cf143b169b5c5c25b1c10e6b9aaaa00fd5292e2cb4e1c", - "dist/2023-10-04/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "d58b61471e5bf9279b31b6dd15970efac0a7efef95478d46504bb79643641edf", - "dist/2023-10-04/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "d7982143a43ae7d2de013747558b808cdf5acf146eaadd32ee1f5baf3354b7dd", - "dist/2023-10-04/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "e48fd58c266991b24012eccf24212bcaa9f2ca76b93cae50b3b48efdb1c3b715", - "dist/2023-10-04/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "918d45e2212b240c975986d6db96b92bba1f51b2d66ec265c3b87dfd338bc548", - "dist/2023-10-04/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "311aed63bdb7316627a362a9606cd5ca680c4f77bbb9abc74998fea09e2b057b", - "dist/2023-10-04/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "6787ea0c089880a7fdbddb8565847597c4a25a19cc54fa7186e0468a377ee75e", - "dist/2023-10-04/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "20b812085cbf0b92cb0b0ac16cf6f3ef21a2b8d43e4185f4170850138361c944", - "dist/2023-10-04/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "41067f3710f1727c5e2410731f15cc72e15633921bc13295bb28096aae9feaae", - "dist/2023-10-04/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "8e771dcb137daf00ed15bf4d33418bc238da1f7352fc2ba3dee941e3af3635a2", - "dist/2023-10-04/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "bd69748bc81f972fb41d1ac5816cab38d810619692e9b2b762bdc346f2c84771", - "dist/2023-10-04/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "90d79aa923e15aee4e9aaed6721db1288712f473263cff3780dc66d7d7cf7b57", - "dist/2023-10-04/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "a1396acdfd52c857969db1ed6dbb3c29cf76f9744e39ee86eda25f7c3b6fb140", - "dist/2023-10-04/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "93fd04c8c70b30dae02c690c1326edf00090b5f22332dcecbfb0130b4288e795", - "dist/2023-10-04/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "a0592ab2d956ed79049548010c21d1043306502df200873ee0ab82185699da8d", - "dist/2023-10-04/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "a304cff2944d1c317940851980a55033f1918c8d4dd52636bc20fb93c0131927", - "dist/2023-10-04/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "46adfdd25bdcd3c8a9fd60cba9cb1c699b49a5c77b4e4d5aa1020c0985f7c6b2", - "dist/2023-10-04/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "c78bf8bc612d14ae2d619aeeba6ce10031113793bdce9ee2554d17fea74cf5ca", - "dist/2023-10-04/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "018d34e11a065100d45b7c529a3de5ddc5912affb74e4978bede24b6995bd394", - "dist/2023-10-04/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "d3d9062f44c575705ebe2a6d1f176a79fb6f0baf7095cd620184cfe69ff57ab7", - "dist/2023-10-04/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "a3edcf33b7241e9e06466c24c6325a1c7b7f2c0ef2107042827154480532ea05", - "dist/2023-10-04/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "d34eabbd010d9c30fb5c07d3bc854d13d629270a679d033a10d5359a3cf14423", - "dist/2023-10-04/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "1efd3a8cc38d94fec1b2f69fc286a5eb25b50eddf8bf2ded9d236456dc8af4f0", - "dist/2023-10-04/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "df58f0a2d80a6580c1f39d8dc59e785d624730a265c107dc35b264f20a6cbb75", - "dist/2023-10-04/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "d139f332dd3a40dae7689a673fc66997f318d690eb0a2b0704344fc2f5c3ed2a", - "dist/2023-10-04/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "fd46fd9ffd848d00ebf11002c40698b443c9ee9e056d342c8ebb28ce9f397d08", - "dist/2023-10-04/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "fd681ec7011de04cf347465594a65fef58882f298e527f22461fb9afc05d353e", - "dist/2023-10-04/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "5400c302976234fb4b952b7fa159ab2ec5fd19c8c7657b41dd7bcf925b59e769", - "dist/2023-10-04/rust-std-beta-sparcv9-sun-solaris.tar.gz": "93208b2ba15339c0d95434bb04e37ec98fc8a13ef9f92cf92fe0ea55b69e15fa", - "dist/2023-10-04/rust-std-beta-sparcv9-sun-solaris.tar.xz": "3a96871a781637bd885b374a30460196eb9cd064d31b269909c155537085e4e2", - "dist/2023-10-04/rust-std-beta-thumbv6m-none-eabi.tar.gz": "2367449493235e192851f8f7f891be6a4c6fa7663fceb04fa6d160380190159f", - "dist/2023-10-04/rust-std-beta-thumbv6m-none-eabi.tar.xz": "71592f9e4cb8f25ba7034fb797b1fb1cdd3cb9d44d12a8dcc0d450e125e89cd0", - "dist/2023-10-04/rust-std-beta-thumbv7em-none-eabi.tar.gz": "53bc65c17358c3645003068b0d9f231e543f7229e6c389b78aa95d32ded4eecc", - "dist/2023-10-04/rust-std-beta-thumbv7em-none-eabi.tar.xz": "0325e27cdfa9d1adcdf6be09515c3b71f8700a06c87579d314a56dd92ecdd82b", - "dist/2023-10-04/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "0810093364e500892abfc716e9ea44c51bb4f3e0d554fb6a5ccc5aa51bbe003a", - "dist/2023-10-04/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "21d2b3ec401dece48893c011e791c04f1233c9f579762ce6ed8ec665aa1747d2", - "dist/2023-10-04/rust-std-beta-thumbv7m-none-eabi.tar.gz": "76912f5ccc8582048be7a8e4800e2068fc395c85a28ba99f7ffd103f4a8cf98e", - "dist/2023-10-04/rust-std-beta-thumbv7m-none-eabi.tar.xz": "85282a5e52b22501b1aa805da1bb41805591d2a9c4aaabf62a2fc2096469dac2", - "dist/2023-10-04/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "0e0ec736578012bb05f6f4c3a2156af0255097d74a31586390d4ba9a64d73021", - "dist/2023-10-04/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "fbf578e9ed1903a14f10231d1d149b9efe701f9fb7ffe96231dfebab6e2de123", - "dist/2023-10-04/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "8f25cce159bc95b8e4678840fc9c0c4d0f6be50c70c54afc8324756e92da0e03", - "dist/2023-10-04/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "72226d027daef70c7b6dc7cacf2cb118e99cf4fa5c9cb7affe2214ae79eff6ac", - "dist/2023-10-04/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "5101557cafc2ae9c7acd05f62259618835bb852802863a268784bcc091fca31d", - "dist/2023-10-04/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "f68fd847bea42413ed67d9160926e25174e9e45037d7bca016d86bcceb40c779", - "dist/2023-10-04/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "abcd1dd5293ff16d4ccebfccbd2ef9954389554eacfa7d80c64a67e1bb550c83", - "dist/2023-10-04/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "24ef9d6f0a7f43c22ece67c6ea52a7df1a704e386d867d8f563f9ab0b1f4fbe6", - "dist/2023-10-04/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "ad415b127acce0ab1ece6b21d12b3bde7b98b0185c8bff9cdad967430081eacb", - "dist/2023-10-04/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "69be4cf95dcf1ca7d2de6f9ce6580a9a30d033839d3629d371b640325fb7a852", - "dist/2023-10-04/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "9d04e4d8314594175458d1cd8ee1fa93e0289fa0b94ab3280224d4b5bcb4ebae", - "dist/2023-10-04/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "cd4292ce22cdc5553e28ad53ed130408de0c7f1f5af0ad2d1e8c06fd74aa3de8", - "dist/2023-10-04/rust-std-beta-wasm32-unknown-unknown.tar.gz": "a7d5382ac87dca13292b816164aae7ecd5b1221d672f2c03b7d236937ab66e3b", - "dist/2023-10-04/rust-std-beta-wasm32-unknown-unknown.tar.xz": "4f165c1f17ebc81875d6d310619c880581a5bfbaeecfa89743f9771894046214", - "dist/2023-10-04/rust-std-beta-wasm32-wasi-preview1-threads.tar.gz": "4ce6dcb79a483cc1c298d19386fe3782429371c7d3a6bfa4f614cceed8f5dc5e", - "dist/2023-10-04/rust-std-beta-wasm32-wasi-preview1-threads.tar.xz": "2b98636f16a3b988ee27cae0915906593e34d4a6475b37370ca541d53aff1ff3", - "dist/2023-10-04/rust-std-beta-wasm32-wasi.tar.gz": "02b684cc228945097c7ba2340fe7c6ca37ad01ad2c4385b70f0c49aa8d6fcc07", - "dist/2023-10-04/rust-std-beta-wasm32-wasi.tar.xz": "68addf98d1fa241ebe10fc9c3d4fdc00f694c5c603dacd6f306cac07802043bb", - "dist/2023-10-04/rust-std-beta-x86_64-apple-darwin.tar.gz": "f49920ef48466960edf9f21bb7c5374bd9edbbc8c5f6c47eb068e5081e3adaf7", - "dist/2023-10-04/rust-std-beta-x86_64-apple-darwin.tar.xz": "b20cffe93d14408618e149c922114061aedadcdb59e65c72bbbb06e378381ac7", - "dist/2023-10-04/rust-std-beta-x86_64-apple-ios.tar.gz": "22cb46613453e6c5a6cffcda03d7a0337b978b64209afec29d4132b121a7b13d", - "dist/2023-10-04/rust-std-beta-x86_64-apple-ios.tar.xz": "bc65c441d62c0d3fe2432a265cacc8f5e871ee9e894b5572f2e1511c4398c173", - "dist/2023-10-04/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "18c6e2495d8be9598e515229e4ea683c1ea2866f9a299dde52ebfbb3d0e1df4d", - "dist/2023-10-04/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "b9f57cd15a15701e0dc77c4b7de1f02444318a338bc176abd7737219746c840e", - "dist/2023-10-04/rust-std-beta-x86_64-linux-android.tar.gz": "b7299cf0a98d7eb7a28ecd57fbdc4fdc8bc6cde32cdaab27e9c5b4e323647908", - "dist/2023-10-04/rust-std-beta-x86_64-linux-android.tar.xz": "ff3190da0a9f11e43bd53b795bea384481026c5d426ac91768e0818a4c35c0ca", - "dist/2023-10-04/rust-std-beta-x86_64-pc-solaris.tar.gz": "2e602b85e49de5efbcc425c33c64bb85dd40cd7fe7b740a8e2953aa193bc4884", - "dist/2023-10-04/rust-std-beta-x86_64-pc-solaris.tar.xz": "e3120a09217977bd0fca8c10909319f1d5b2cf1c3a14d1a0bfa894a25c1dd30d", - "dist/2023-10-04/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "84508811863e1da5bb903fdbc1fe8335e15455469f58f243052b1d8e3885f3d1", - "dist/2023-10-04/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "105de75121b217dafb5b3432bbfcaee0432fc84d17bbfe0d8f6d27a7c9e3ad6c", - "dist/2023-10-04/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "559f9d676c36a236131a73776879103f68b7782ecfbacb7207bc443de44457e7", - "dist/2023-10-04/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "7baa8e4a543211d2235965d681f6d92127f2adc0a51c25d2fbb123715fb512c1", - "dist/2023-10-04/rust-std-beta-x86_64-sun-solaris.tar.gz": "ac20916902c464c9a9ee037b53f90e3b0cd3ec4c7b642deae88dc5f60bf98a8e", - "dist/2023-10-04/rust-std-beta-x86_64-sun-solaris.tar.xz": "c25d29435a43a607ff21204fb109675496229b975fbe879ce88bbb0c290f6cdf", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "8aae13ca12a349bcc0a2eae61543122095266e1fff1e6abd2d53e6f4609574ab", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "c94ec8fc50a4e003b4e4cd3ac3106b9cea0709dcd9d7729f72499744d875fa10", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-fuchsia.tar.gz": "e8cf05fa4f01186b6e1810c0e5f8c3f6e5c7efe88cbf2efb6e9d6e4fed2692d9", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-fuchsia.tar.xz": "70688ef9098dda4c2c0acb06a4edcf7457ccf5f6ae509edcad89ce1eb9b33fe6", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-illumos.tar.gz": "4ee1f9699c22200beb40523976617b32e097c443bae1201fc64ad3235c8f58ea", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-illumos.tar.xz": "988079564d076c2c49f8eae77069a0636945a980389ae06ed09a90326ddf9f76", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "0897b02e38e01b0c47075c9f2ebe1e8d127193890be499acec84c56e7a04c036", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "746317cab5b1c8676191287077e4a93877c545fbde577c3cc96149aaaec80afb", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "8130a54368f699c26108314690745dc69bcdc572ccedfcb5799728fb902b2ec7", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "c7493f1d747cbd893195a548f23ec9e713694057a15caa86d121fd981cf50ab6", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "6eec43a326e3c26a5f64c0aea25bcef1f890bbd4ec7bdfe9e32ea47641fb5820", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "1bca9c3dafa477ce34e2f58f6625bd3b461ae188468759921db591ec065cd3f6", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "bbb87aabd230f4209df5f63fdb88fb9f7a2f6d0b4e9cd9f9770e2a60881d4442", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "b235872c8041c38321d3e9d9ff3b4fce3ace96493c61af46e47b531bfab0eade", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-none.tar.gz": "d20e50343834f77e39043ab23fe5591bff07bf913bbffcfbc9e618a920fb2043", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-none.tar.xz": "495a4e236f92d2c9ec0ffaee4792e356ee15de447093538f9a34e85c94fd3ddf", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-redox.tar.gz": "de46554c25acc2b3571063da2d63d32337a5c60f71afff96ead0339dedcb7f3d", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-redox.tar.xz": "dea31f24ddd5c5bac73c6bc731f5356232be331fc8cdff80f4fa460266f1f5b5", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-uefi.tar.gz": "7a1f61d37a132fe288652e7679f78c8f36d51a0724102ae39ccf81f0bc9e88bd", - "dist/2023-10-04/rust-std-beta-x86_64-unknown-uefi.tar.xz": "c452f747af508bfa76f27a3ecfdd333c50e9168ced7738fda38ea4e7f1ea904e", - "dist/2023-10-04/rustc-beta-aarch64-apple-darwin.tar.gz": "68aa57ce1a40d955bbc110bbcf1a9a433ef8492d91961658f163eea0f1917de6", - "dist/2023-10-04/rustc-beta-aarch64-apple-darwin.tar.xz": "30063d4818173bd302eecbe204942fc7c7a908a4294cfd3b5670de6a91c5937c", - "dist/2023-10-04/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "e91ae34b35404fd9466b3c4210c4859489b726df6bd1c6b5deda2f54f8243662", - "dist/2023-10-04/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "f5f62ccb98f5340a02ce4ad73cf370692eafb722d89a7dc18d411216dc62d082", - "dist/2023-10-04/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "b9dd02920bcd80f7bf58e5545ec8cfed0763eea657c2903cd21526b6f9ad3891", - "dist/2023-10-04/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "9ac274cd2078ddba5c856e3258862d45b61375f98d326a32505267c4856fa21f", - "dist/2023-10-04/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "ca1accabfd224bcefc1e9726cbb19066e42b3f5eff349c1be837e6adc4b4c63f", - "dist/2023-10-04/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "cd53dd9d96dff348f45ba28ffe68dcdb0fa90b372acc18e3edff9748e0ef6bed", - "dist/2023-10-04/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "3ca0229603110cb4492c2c399da2b57e5084791d8ec84b4cf9557498134362ce", - "dist/2023-10-04/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "f7950c38c09c37a8fb3d521b62185316e85cefcee13ecf6cf86238c03854aec9", - "dist/2023-10-04/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "332e1f2d816e878bcef183485ededaca830f72265c676af2df92e359df608fcc", - "dist/2023-10-04/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "0b88d3083b62f3e212bb2c823c7c2f5491eaed0baed99a031220f6ca115d4b17", - "dist/2023-10-04/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "0b4854c6290e97b4539f55973ccc9f9a4941a07c5b559f94b79490635913cf0f", - "dist/2023-10-04/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "fac86fc193852246947b648e2c725a4e9973d67a8129e66e9df88a91e7403ffa", - "dist/2023-10-04/rustc-beta-i686-pc-windows-gnu.tar.gz": "1ab3eecffe819ee2b4057a06998fce122931f1989dfab260272fc238f5672592", - "dist/2023-10-04/rustc-beta-i686-pc-windows-gnu.tar.xz": "8af88f78bbe66d35013a9b51606c570ade03c10c3e01115340ac62a99ad28ef8", - "dist/2023-10-04/rustc-beta-i686-pc-windows-msvc.tar.gz": "1d9c70146764cce221cc3e8144395541fc0f3b4af1cc4178342cc6c54e9adb6f", - "dist/2023-10-04/rustc-beta-i686-pc-windows-msvc.tar.xz": "5599aec7ac269dd2a03f5c549a50eca656a309cfe4178e097b64f6357f1d38e5", - "dist/2023-10-04/rustc-beta-i686-unknown-linux-gnu.tar.gz": "7b2b59a9e5acaa56d614c68fbe3b9f0e792f75a32e5f7c27b1ffb6d74508a433", - "dist/2023-10-04/rustc-beta-i686-unknown-linux-gnu.tar.xz": "2ed53af655d58300f88bae54967f964959c01f7d39e7198b14ad835392826dae", - "dist/2023-10-04/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz": "720e53352f5388ce0880b3f51b9941c4d7db86a549ae4c624238808438b02ecf", - "dist/2023-10-04/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz": "9fe3dc9d520284e4004c712fd328d86b6e4601283efe0bf519c3386906dd460f", - "dist/2023-10-04/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "d479a5254d2707ddf725b7400ae04c8c68c138a54aa65861ee5f8e6ef107c4a9", - "dist/2023-10-04/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "bf1756fc7b439b338defd1cbdcc1127c364a5ce4248c73f9a5881c39dd6a7bff", - "dist/2023-10-04/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "691e59ad96392bd1b61538fe2bcd8665470d9243a09c372bad93c115092aa5b7", - "dist/2023-10-04/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "3597d20828b23e94934ea8bbd4aafc51e06bd006ff993e95d1106822131403b7", - "dist/2023-10-04/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "24d283898ca2ce98b2ba38a9ac2fd2fb039da21badbe7c2e0c15660cd622a7e4", - "dist/2023-10-04/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "5c927ce64da0e898c03b56677047f087f88eab5c0777b6f07fb5356fb7dfd913", - "dist/2023-10-04/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "e10ef3fafa1dd6bf2c935853bb9289e1fbf85cfb092683b99107f986a2ba0b9c", - "dist/2023-10-04/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "d0fa4adb60babd26da08c8e141f1ce472c0c3111b1c361bb1a28cc65cd83afa7", - "dist/2023-10-04/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "65020cdd2b30795f3a8771fe41176e747bd13f3b948860aefed5675b89c55257", - "dist/2023-10-04/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "233890aaf235832448dbf05f7ac547cc74c53490fd6c9818d7ea673bb6391d35", - "dist/2023-10-04/rustc-beta-x86_64-apple-darwin.tar.gz": "745e29cf4127d5f67c1a663e47755a8cb200a14cbb44a33e5da6d25e11c145a3", - "dist/2023-10-04/rustc-beta-x86_64-apple-darwin.tar.xz": "609f55c6c784caf6978c176ddfe1b2675bd7f23a4ce4ebff98147f0ce58c0cac", - "dist/2023-10-04/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "78e37a161d41f1012896efb345b1e53febc11943e003c3e848106828e2b9337c", - "dist/2023-10-04/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "58baad93c3fd4a9c25472bfb10cea890efc94cc7c7b131d8dbca727bae79491e", - "dist/2023-10-04/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "3e5497aa975297ad9abae5913ab7ffe8a77a40a37cf26d7fbf18410993731ddf", - "dist/2023-10-04/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "157877278bb6b5af2676a61377e4a2b637470c89971e649b3476ec33d2f84987", - "dist/2023-10-04/rustc-beta-x86_64-unknown-freebsd.tar.gz": "923235f207f5477e455e82c1f516fcb1f9281f67c2f4fefdcb8b4a64b651d7eb", - "dist/2023-10-04/rustc-beta-x86_64-unknown-freebsd.tar.xz": "8e298f4524ee23f9f9b7ca00ce8c45c9e1a16d85c19acb4897fcf24ae1e40d8a", - "dist/2023-10-04/rustc-beta-x86_64-unknown-illumos.tar.gz": "e443cd9be3b531b645ad8ef32bb7f91f25fd17bdbd9439f1f77cd8345c13cc03", - "dist/2023-10-04/rustc-beta-x86_64-unknown-illumos.tar.xz": "682f11962582237689f9b8bc75c5a42b7492a26957909d15e919e0871b33b030", - "dist/2023-10-04/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "60faf9e993a2e2ebec1922a485fa63a47a1c4ad9e282928be8c21dd6397efe9e", - "dist/2023-10-04/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "e0a5cee0346f2ec96d14c5aa6b9d26d8d6d8ac80ea852b2df89d2df999c965e9", - "dist/2023-10-04/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "62ad39e3d0ee9eee3d88f3e2e0b151fc9eed8475e2e4b601dea37b8c6990a9c6", - "dist/2023-10-04/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "8946e23ee7e0eec9b519c0a7c0428592c30a605efb6b58109936300b59ac3074", - "dist/2023-10-04/rustc-beta-x86_64-unknown-netbsd.tar.gz": "651b08daca5f9de5c24ff465ee8f93beaa77477b6b54af8f7929d54876ca08e2", - "dist/2023-10-04/rustc-beta-x86_64-unknown-netbsd.tar.xz": "443bc94ae5274c8f42961bd2a297a32ee1d543ee1014d0f6cfce45febfc19c18", - "dist/2023-10-04/rustc-nightly-aarch64-apple-darwin.tar.gz": "2932fbd7dbe55d1bdfe33788d9aae9e40e42e5fc81841b605873cd118a769947", - "dist/2023-10-04/rustc-nightly-aarch64-apple-darwin.tar.xz": "c9bb124d9b5433d0a5fbfe8728fe4db5314b07f6f38617e6c8fa0b263f02b813", - "dist/2023-10-04/rustc-nightly-aarch64-pc-windows-msvc.tar.gz": "8b9a4eea2d57230d6dcc867b0564ac7c5b557e4eadfe8198ac253c523e72c00a", - "dist/2023-10-04/rustc-nightly-aarch64-pc-windows-msvc.tar.xz": "993f2afb93e5736973eba557ff5e14cebf4836ec02cfbdf2b652f06fce39cce5", - "dist/2023-10-04/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz": "0e10e13a02462859451cddcb429af9453ac3ba11dbe896d49b008905ec3b0fec", - "dist/2023-10-04/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz": "be81aa143335be4d36c01ca557128cbff380cdcea72d9df20260ac6d91ac79fe", - "dist/2023-10-04/rustc-nightly-aarch64-unknown-linux-musl.tar.gz": "f4300af316cc86ea85fb9775ad488e22e1f36fc10a2c40a55bea26fd1d180359", - "dist/2023-10-04/rustc-nightly-aarch64-unknown-linux-musl.tar.xz": "8579fdaf0c89dc6dfb69ef6f9c1c650476c4aa3d6f2664776a453028516366d3", - "dist/2023-10-04/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz": "bf6cb4c51f515a63e0ab92904c364238dcb889f21148ceffce0c2110238a7fca", - "dist/2023-10-04/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz": "359a9a8ca14829ef6f530631d8b88eda2fbfd9a7d01a3b9c8e98b121c8470056", - "dist/2023-10-04/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz": "9508938031c9333accd01cffad43b5ff1c552843cb4d5f5a0f767a3ccf908c01", - "dist/2023-10-04/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz": "e5d7638eab37ee18b58d50848398cef79d241939e1330796a7678399d395d1c8", - "dist/2023-10-04/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "196c7e12591a22f5c354045bb1f8ad18e8f069191b5d234fac3490d4c0782839", - "dist/2023-10-04/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "d01560008ec3c254b5ec1c4f9b4b707b7e60d4c7eb1560a2e95efd9fd681a63f", - "dist/2023-10-04/rustc-nightly-i686-pc-windows-gnu.tar.gz": "13987991229171f65295f901451bc735d5b38737c012150c7d912a9dbb627136", - "dist/2023-10-04/rustc-nightly-i686-pc-windows-gnu.tar.xz": "de3b82265e5157b0fe36ba0686b4dfaf9b697594ba50c0697d1d261812bddd30", - "dist/2023-10-04/rustc-nightly-i686-pc-windows-msvc.tar.gz": "aa2292fbcd0ea65407e5cf47924616a90762ca4750535c3e980b9057623bd896", - "dist/2023-10-04/rustc-nightly-i686-pc-windows-msvc.tar.xz": "063fcf503b76f1ae1ed7278a01c61e12bef4aa6d5e69ddec751986dfc7038b27", - "dist/2023-10-04/rustc-nightly-i686-unknown-linux-gnu.tar.gz": "cb0a6e9cfbf7a2869818fe16c853127e4d7cafb69cda01d7737d30476f7d9b49", - "dist/2023-10-04/rustc-nightly-i686-unknown-linux-gnu.tar.xz": "0057ecebc5d9e12f6ef8d0c0ea737b7694507b9c7a8ce0e6e0d323acd0a39b3e", - "dist/2023-10-04/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz": "b38717c03d77eb019262ea3a82e7d91f620170bcb1f05717f8e5dadfac1df483", - "dist/2023-10-04/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz": "4e6a0e9d35101983388772f77ac0fc0714f3025d670c649e6050fb50ca031738", - "dist/2023-10-04/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz": "1ea0bd0e5070fa590dc2301b6f41a54b9895b0fd688d9c6f37d77d0b6bbef8ee", - "dist/2023-10-04/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz": "6666cc6e04743127cd63ec7c8fe1e9fb7882222778471ea1368bbab4adce903a", - "dist/2023-10-04/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz": "60c4c413d85f86de2709e4b9ab4c0e25bb6e99da3e10cc68eb2b3864a5d60e18", - "dist/2023-10-04/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz": "8569bba6344da9ceb87d53c3d5664d442a1251621625f7b332c4a6f4cb93643a", - "dist/2023-10-04/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "016f8daba0a904e185075840c55f477bab7c91a8b6ca1aee936074f01adb2b44", - "dist/2023-10-04/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "573286e527de43850a25b2c03b61d89fc97437ceacc9f5b3be3a313c9175027e", - "dist/2023-10-04/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "d03339ef02057584100232a6def4a9b80f13b9d242fe57fa47c0b18e84f3c56f", - "dist/2023-10-04/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "2e5ceb85b2612164e1727471a775d5ca1924f92327deddce0d1509c5a87e4bb9", - "dist/2023-10-04/rustc-nightly-s390x-unknown-linux-gnu.tar.gz": "b991507575a08b993508ee4c7ac065f3f1983278a457c141ffbfb15b46ae25c2", - "dist/2023-10-04/rustc-nightly-s390x-unknown-linux-gnu.tar.xz": "155a4e360eeae301379200415820a6f9c53d3923943d791a3e802ef8083494c4", - "dist/2023-10-04/rustc-nightly-x86_64-apple-darwin.tar.gz": "0ae7a68d86c72f42e3a98737abe466ad88a6be92cb17994b26fc2a495abacd2e", - "dist/2023-10-04/rustc-nightly-x86_64-apple-darwin.tar.xz": "5e2b0a191b34e133ca9e0d883714bea46dc0f6381c9e5ee145851bba799bf7db", - "dist/2023-10-04/rustc-nightly-x86_64-pc-windows-gnu.tar.gz": "62e5b0dae1aa25488dab73869da8ed2425114b778adc55db6c6c6b9f77c67d7e", - "dist/2023-10-04/rustc-nightly-x86_64-pc-windows-gnu.tar.xz": "5494b11d55bb36ae0536b534796870a0ad8e2a427f64a21418d5bcc2d0f27ab9", - "dist/2023-10-04/rustc-nightly-x86_64-pc-windows-msvc.tar.gz": "38b8add3755f62016c40ac84ad3b7085b6058e40768b40e2df672ea4c5d3e91d", - "dist/2023-10-04/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "e511c5e3adfffd33e6dd3cd9525409366f3b54ebb1b3a7f2c09cb7444e678006", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-freebsd.tar.gz": "54190bb432f9dbc4fb414ff40a5e8a5668b8aa343599b4e4b28f7a2e9fc0fa3b", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-freebsd.tar.xz": "7c260043c68b31484f3d415d43ad8bd702f642cfc5050e09ea6b9bb7fa108020", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-illumos.tar.gz": "7cc87c2a5f801a1023688cc6bbecfa0c4c072108b4edcf4afea5df059ef1c2cc", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-illumos.tar.xz": "b21f227d6b8c4c8847db2e3299613a86b13c95bff4f17c7055e1c0d46721fbc6", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz": "52b372926f5f72a711c36da67a09dfd8d4c9e819b76917053865cce5bc603732", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "499a3aa2baff90e710e2aa9d6be421569119ac90b484093ec38717da1fea3142", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-linux-musl.tar.gz": "dc43a1ff5639479cef66778baf3a61ccee67b1aba57dc24a81a0d2f35b24b3ad", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-linux-musl.tar.xz": "0f953831e44c7084c86f393cfa3a3b3a0ea8aa2de3a0f7701094683f491c4a6b", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-netbsd.tar.gz": "e636cdb58be6fc3d97387121c404d02772c5a7519f0f001f1c9ff282acdaead4", - "dist/2023-10-04/rustc-nightly-x86_64-unknown-netbsd.tar.xz": "79db9632154590294cd0a9f1229b95ec82163a278b3011e25f2fb86c8af6d894", - "dist/2023-10-04/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "0cde181bc0e9af1eb2813aea3ada3333974eaa85227eab12eaeffe026fe7f31b", - "dist/2023-10-04/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "0aa47b92b78591f522c5d5b4109b1a0fcb4489ceeb3a9014d3f71dc76bc00a6e", - "dist/2023-10-04/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "30ac919cb1eec11a22d96ab32debddf9ee91e776d0edac25fd2c71421145d77c", - "dist/2023-10-04/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "55d800eab787c0bb1d41def853b1a48ab54c2e6cfd6b118e905ec283cc1814eb", - "dist/2023-10-04/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "9e3ae2cbc37307c47c78c953cb2703daddd46b6df44cc61c2b3cf43921c5b03c", - "dist/2023-10-04/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "6812bf2f1ff86bce8bc82cf2ea2f29171fced04b9ca89cfc43121824c4d15eda", - "dist/2023-10-04/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "99ee18898dde1821751cc06eead4d53208d66d98c7f95e8122c7a1161924aaea", - "dist/2023-10-04/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "f65c4577aad313101a6b22f55d777a76db31f21c6467f25eb1d8775a484256ba", - "dist/2023-10-04/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "94b76e626d2460f6701d5c16dc91d633a8c88b0f2583f4996b1173709c29c8cb", - "dist/2023-10-04/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "d63186fe9980b884f375f8ea44ea2e4e8493529047a7035c49c9007261310d8d", - "dist/2023-10-04/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "04e20827f34cab6f259686decdec4df335a8bfa2846a46fc83bee1d65b39ee15", - "dist/2023-10-04/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "f46798ddac7d5b3cafaba54b0fa561ff618f7b931cba4b59eaf2f38e5a60b720", - "dist/2023-10-04/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "3d2b5974f820e976c890300755efbcebb4b14558bd9824bc66d9ebf1dc33de7d", - "dist/2023-10-04/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "ce148b424ce7122fb2330c9911fdb134a67b7365dbd10fff182bd70517ea39e1", - "dist/2023-10-04/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "369278645833248dad11a65a886791134880e26bf0a35ff9462913a355fffb90", - "dist/2023-10-04/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "995727a9213386e7e02d27a3d2f99f5beda5fc0469fccc5736b92ecb4e317ba6", - "dist/2023-10-04/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "92da2162ef18033e68593617613ac11d9ba16bda81a0a685a89fcbd80a815994", - "dist/2023-10-04/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "abca8c7e31afc80ea515c92a7e12a8d6d0af6d41d5f18caeba74a1cd5372876c", - "dist/2023-10-04/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "e755ab5413fa52013cc00df1d8cda68c5b3ef1e99d1d964259d5b002f293e8b2", - "dist/2023-10-04/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "1100241ea39bc177140c458a12174b54af5d569000f59c9791ee8c6497fd4fe6", - "dist/2023-10-04/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz": "5ac4fe7bb0f596cd8565d1edef6f1696043c698c90f5562f5d4a94fca26021a6", - "dist/2023-10-04/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz": "9ecf3bac557d64bd467f9213959e8b1a361041e5a98552a448936fd2903b334d", - "dist/2023-10-04/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "71bbdb6c608d2cdc60619dc2acd7bc7e35e9a20e5e02a633b224b124438a7cb6", - "dist/2023-10-04/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "2d3af87b5c6e0ea524cde039a6deb0b014ca103e4183602b47aedb5bfb97ff9d", - "dist/2023-10-04/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "e3678dc35f3dede5d6fc5e75eec6f19990e4a8b034163c4ef79eb522e654f6f3", - "dist/2023-10-04/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "da9ed66ae0021c2cc16d70a29151963149a433fd4a2a299a45d7cd23556fc58f", - "dist/2023-10-04/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "8a08e5a3d5eea3f414e7b8ed939793ce02f843a60dadf0e4d6eb62e8265fa20c", - "dist/2023-10-04/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "68441252ba1e058023cd480e431e04d755309c1acd7330d49a20c0aeefb1ceac", - "dist/2023-10-04/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "305f24d9813129ce07117dff8cf0de42aff539c7a89df58c2a11f7056594a702", - "dist/2023-10-04/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "beca739e374e20d15a3a16240a801662c42ef135eea5311d8bae7335bf27634c", - "dist/2023-10-04/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "c33dc1b858ad79f2ef89159f40b893d071bd54b1891868064bd210fa2ab1c917", - "dist/2023-10-04/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "0929169a91b4d66058e251ebf7c8bbeb73e3859600158afeb9fcdedd4de84168", - "dist/2023-10-04/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "13684516018f9c56c504793b4d110610f6f8c4a2a1624c63630a12927a9b5b29", - "dist/2023-10-04/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "50686ab2eeeb73d51830b8f19c76a615eec1b17df8f34ddbcbee6458fc51832d", - "dist/2023-10-04/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "315a87aa3fbbc7594d64b4771d105961144f9937c9c1b33991de4ff33c186bd6", - "dist/2023-10-04/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "289c99380cfb2933d5c85b20f7f38e4a61de2c3ce3640ca45a81eb68a7005b72", - "dist/2023-10-04/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "81f689344b8d0d2312790109c18e206849906bcee5bb167c9a07632c1ba71eaa", - "dist/2023-10-04/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "6f17e1e8327cb470c00e58acb8f4b076aa8989c98bcbb0c55867dd6ba1f436ae", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "93e0ccbe7484ee679c4cefa80e46d76c0adfe93f0b52b34ba80cefbde69845b9", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "4c9fde270df24fbca4286d42577d454fab9aa3cc7abec2f8eb4fce359162d8ad", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "e6d4e9b727c7c61a28dbfead22492c7503c4b5d05b41e2991eb9e3551365d440", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "30db9362b5f7eb09ba8ac2050cb98599fb352b332bf3e5c94ad6bc182fe9f294", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "348b94f9020c65fec79a36c6044857a7e897164f1b2a4250e807f258efe99e1b", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "2914983968c484e4e74cee620764aeb476445b1e324253c5087354dbb95a0132", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "0ab0a77a8fadc6cc4e21238cb9487bf5e3b6085663351b2b3c429038621ad83d", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "a992de62c67d56889e11dc7c5401729d8658c3e50365037b4bd975624122dec5", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "e9ab091b5bf9d307164b8c774f98912a0a8282f98e6d9528569acb2dcf29a10d", - "dist/2023-10-04/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "d9421dbbb9b995b6df1144cebf0d6b10fc4a91414b97d66b28335e78b8859d5b" + "dist/2023-11-13/cargo-beta-aarch64-apple-darwin.tar.gz": "747e7a7b31217abef7f5e6e451669cbada43ccaecf6e58df2dabb2b059256406", + "dist/2023-11-13/cargo-beta-aarch64-apple-darwin.tar.xz": "4ab42395708f3d2b495217c08f96a80ad5a6415edb226391e0f010927b206b88", + "dist/2023-11-13/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "96a5f6d7d5fb25cb749b4f20405d7eb1aee296ea58915032f589943faaa87819", + "dist/2023-11-13/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "0f350bb1a28c7d822782137d80f0664c7906cdfce050e303559ea98891a0e3c8", + "dist/2023-11-13/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "a02bafa141d71877c806aa7bd9ad6831c4cf99bb62503ec4a7046e2e8a8a7885", + "dist/2023-11-13/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "26b16d1805b6424109dc1586146fec72ad370eb7d6be0e746b627884a17f8ab7", + "dist/2023-11-13/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "b8cfc7a38693863a8b8b2928654a86347c07b8da14356a424dc86a4451e2201f", + "dist/2023-11-13/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "579c0fe919fe22221a545bd75152d7cfdac9f1b6529f9eacf397c0bb4d4db63c", + "dist/2023-11-13/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "a4fa076f7512a74fd3c4cb6f3ae12322049197ba5e719801fc4d72fcf33b5837", + "dist/2023-11-13/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "4574101b1cc23cb87abc038f342a73fb3b46a5a8d18f660ff4ce047a30a507e4", + "dist/2023-11-13/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "cafa333ea302e7066ad642994eb92811b85f41da5f2d8da83bb9c380eef44a77", + "dist/2023-11-13/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "9fb8b43ae9c7c18cee03403cd5b01cd246d5a0ab542e4eb19bbd03fda0e611cb", + "dist/2023-11-13/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "d3f6f85bba4b890fae52c593d8d57ccccaf86dd2d2545afe49d1dc924e7a72b0", + "dist/2023-11-13/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "7380fd4af83d9caad6f9a7d5d03f9ca33fe770fd35cec2e7ff78bd39f5a1e417", + "dist/2023-11-13/cargo-beta-i686-pc-windows-gnu.tar.gz": "15673b84641058cc0a91fedfad9f74c284d87231905980182e535ad7ccd9b31e", + "dist/2023-11-13/cargo-beta-i686-pc-windows-gnu.tar.xz": "2661610cb774326a04fdea7011ffa10a5635ff679ba744c05eb3abc56b8fc8da", + "dist/2023-11-13/cargo-beta-i686-pc-windows-msvc.tar.gz": "e1aee3221f3f17d129cd06f506a9cc384e91d211cc3ac09b927e3c7f069c74df", + "dist/2023-11-13/cargo-beta-i686-pc-windows-msvc.tar.xz": "d2f18bc8e07711ccc9723be50cd8bf42bc710efc50f12ef72921d12af7d2b9f1", + "dist/2023-11-13/cargo-beta-i686-unknown-linux-gnu.tar.gz": "0aac1acdb7cd73b19b9581f2262a22ab43378ad17c68e7b8133d34ab8b56ad6c", + "dist/2023-11-13/cargo-beta-i686-unknown-linux-gnu.tar.xz": "e592ec051931d944f2cbe23d42e3b59ec4b077f6122dca928d065cfab333d91c", + "dist/2023-11-13/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz": "b5905ac53e476ecf1e0ea7d6625c1306cf8de27a2f1ff6b904e58bebfd87d384", + "dist/2023-11-13/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz": "3ee0542b6dec61ae9839f838aa229e13ad8e5ccbc659d6cff5c30f5cc8da1b7e", + "dist/2023-11-13/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "dc469696bbb2c80105c615dbd1e404de4e2552db295c20840f8253fac58b494f", + "dist/2023-11-13/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "0d162454c64f09c54f55c177d1b9f73cef27f399065095830ede9f3879395bcf", + "dist/2023-11-13/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "1c5a563db7c53e639823b56251efdd755c052c4cbc83956c84cd1f6af880ccf6", + "dist/2023-11-13/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "1302079a10c98b469effdda4de7bf8489cc577497c125f609b72f2f8257419fb", + "dist/2023-11-13/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "c739f65c2e06c5843bf619b1028fe6f2c82cbe48e922d1bb30d2379fbe74317d", + "dist/2023-11-13/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "83e25573d9c4713e095e3f2f253228ea558c5826b00319c9622fd47be5787351", + "dist/2023-11-13/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "04bb36ec9d2e636f11f7c5573f4749e3c767dc371fb7dd7e5fb7bc5497a9f02a", + "dist/2023-11-13/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "4d79803c2f04a69f02bfbf9e300b8d48baf381a4fadc9f665f748b81f1c2b126", + "dist/2023-11-13/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "49354596eb8d6be2afd40022504987ccf0e54c205d74ee5b8352260f2e95f9b7", + "dist/2023-11-13/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "1df49a605408c0b72c95f5f1fadc6da14f847622617a70d4ae0df6437505896b", + "dist/2023-11-13/cargo-beta-x86_64-apple-darwin.tar.gz": "3ac3c0170016d6ca3fa18902a1c8714820ce8c743e7dd3674c54dd69f1f84645", + "dist/2023-11-13/cargo-beta-x86_64-apple-darwin.tar.xz": "d403d08162ecf41d53190ea5a3a945c1fc5bd0e2c380dbaac764d03ad8377f0d", + "dist/2023-11-13/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "1003fe8288c982459ee4379a5746d825ff0b6c5d0bebb2b269903da9432b712b", + "dist/2023-11-13/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "07a364faf47204c92d021fbbae57b369ad82ba64fad2173189aea3764568eaa8", + "dist/2023-11-13/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "1a7213217869ffb3c48b557dc8c172be61eee258f7bf34bb2b5f8c9ad96f8521", + "dist/2023-11-13/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "64966d116575a18354d6e884317a6400941994462e305269f25f17ef17605417", + "dist/2023-11-13/cargo-beta-x86_64-unknown-freebsd.tar.gz": "7a1e553ef97d3d88d532060a8dd64234742df04ada95f5d13536b21d380a9d1e", + "dist/2023-11-13/cargo-beta-x86_64-unknown-freebsd.tar.xz": "67f1bb5008080c00f4f22be794b1fa63a88c1503187dc3396260aa73d52a1f7c", + "dist/2023-11-13/cargo-beta-x86_64-unknown-illumos.tar.gz": "436959c6da9c1c4bc49c4df51747729c0cc31b68c5a4dbf0e1875f3535dd1d26", + "dist/2023-11-13/cargo-beta-x86_64-unknown-illumos.tar.xz": "fbe0068c48b9b90467fe22e61d7c0707dbc8b113f07966f6c796821d283fe113", + "dist/2023-11-13/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "aea309719c709ba1a8663dcfd8f4ac379695d65b69115641e6adb57cd24819f0", + "dist/2023-11-13/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "cb0fcf1a9804d106f4168f6f8d2a071bddbfaa55430f3284696982bc6bc6c4f8", + "dist/2023-11-13/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "03062f953a10777d1a26ab13190a5493c989ab560b182c5968cca3df97fd2b48", + "dist/2023-11-13/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "790a859eaf49da5fdef1ea1ebf8b7f1f001bdaa62798a745d791bc915d886094", + "dist/2023-11-13/cargo-beta-x86_64-unknown-netbsd.tar.gz": "f14395b57c0eca7c8ac9012b303c3781e16a2287698f68705fcbbc5c7c5916c9", + "dist/2023-11-13/cargo-beta-x86_64-unknown-netbsd.tar.xz": "2c759c8bfc9cc556eb3464ac7e5589b573c46bd3938ac288ba52549121d3333d", + "dist/2023-11-13/rust-std-beta-aarch64-apple-darwin.tar.gz": "818e3554a6bd82c3533e68f8597c09563af1dc08d6b23bbd2323b8ebcce251f6", + "dist/2023-11-13/rust-std-beta-aarch64-apple-darwin.tar.xz": "6f9333c281859e40b1c1c20e9f07ac1633ca2c99e86268b521945b8ed6cf6a50", + "dist/2023-11-13/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "fa4bb7a5d4dcecb2dfd8596e3ad442a5fe05468c939de3b36e44a43e10ea9d30", + "dist/2023-11-13/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "4a8e8ffb4658162c3bbd7b1c819f50181ca88c6afbb61c689c621ee4d29a66c7", + "dist/2023-11-13/rust-std-beta-aarch64-apple-ios.tar.gz": "42d37c57380ea2b97c1ba62d50d49db5e674387b0953f2bdcb323f4383f5f456", + "dist/2023-11-13/rust-std-beta-aarch64-apple-ios.tar.xz": "699f2f3c386f4bde50cc59616339abb95d95f292650b4b1f04cf8d923fccb03a", + "dist/2023-11-13/rust-std-beta-aarch64-linux-android.tar.gz": "b872dd2e1f7af33d327033ee602bcc62a5e49cd6178d0974a0f0ffcba86b457c", + "dist/2023-11-13/rust-std-beta-aarch64-linux-android.tar.xz": "ef121a3a4db77c3935d23887ead6c6399dbe45c739566c8bd66ad4fbf6adef2f", + "dist/2023-11-13/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "055043974520b270fdfa63074e5f44d2881052542549ae215e5701c1d476235a", + "dist/2023-11-13/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "6c320b375bb029c144eebfb01127ac0f1f2e4567df3ea505ad5f48d144115272", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-fuchsia.tar.gz": "9aedfe4d4186d26878ffa73f6562a88e5123a54fc81293315c4e62c9e559b6cf", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-fuchsia.tar.xz": "7f623ca49e51ad78caf64221581e96eb95a489532cea22d91f98794f5a6819eb", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "07bc11565052f5a417f650451f2ff0aa6f0aadd506b664e77789006429746d9e", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "d4bbc5dc49a1f6d53c1928807531679836aa69a4b3715295472e3df4a40da27a", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "493f4f196274a1141904e1aeb9cd32dcc6d4a06283395012d257e2a8d545d7ef", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "594529eb5be03d6f376c75d04260700205f815e33697e6cabd5449754704c748", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "5b9a95cccb9179fe2b62998292a40ec05137ca9423b103392fe4d7a6e6d03402", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "aeab81bc3066bdb94227a31473ded8a623d9bad924a39bb6db6e7d6f4fcb77b2", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-none.tar.gz": "24a9a76b4a2a24c9388d55ff95dc100436ed7047f95c0d6476d66cb9266f6c03", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-none.tar.xz": "0052dac98cec080b2bc15955f7ac93c11db351d32c7f5a1b46e00eef9ca39f81", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-uefi.tar.gz": "3bf855413ee4c57ffa54eebb00b819e57014977bd85c69dd5ec35ec0b64d1b1b", + "dist/2023-11-13/rust-std-beta-aarch64-unknown-uefi.tar.xz": "0dc3b0d4f1cf927b1e1a53755b1f2da81bd4e43c172e446426ce70db6ba22802", + "dist/2023-11-13/rust-std-beta-arm-linux-androideabi.tar.gz": "9f6d485b3e53a3604f80f48149742e22820d64fa4e4bdba0b0206b6d6be78486", + "dist/2023-11-13/rust-std-beta-arm-linux-androideabi.tar.xz": "52efc21d5c92a8bb3eaedfaf87fda75b5b19bf0c65538b718f1fabc3b14e0582", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "5c5089e88d841aeabd4ba279a696a687cb68850185da92b61afbd3739c0a409d", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "55ee23f7747ecfd8c8c6678372109c7068d3363d710519bc607135fe8b5fc46c", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "08afb933ce18021e8bc6a016dcc63f8001bf94f201618b8e004c2f4c4c9bafd8", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "1b16c956b8b05fbbd0d3adf0356dddc64be1a8f099d99f6d8d7f76a0b07d19ba", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "ac49dee872f3cf6d74abb60c6a717a2e8fc8994e8a2a4cf2e89eab7808f9013f", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "5852f7f9f08574a351f5094a5268cddc27afc4b9933c998d0757a838504e9959", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "c9b806a780d1cb75f7892b2d1adca343fcbfc626fd4c364af42c13d8f8e71e82", + "dist/2023-11-13/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "d81d26a77da1d034caafd194e2d36de5be1d4c4f99095d39a378b661dd20f28e", + "dist/2023-11-13/rust-std-beta-armebv7r-none-eabi.tar.gz": "be2105b20ecfd7103613a4e2924e6057806535f467a5aadacdf8f458eff9866f", + "dist/2023-11-13/rust-std-beta-armebv7r-none-eabi.tar.xz": "f130958a449aaeb7fb81864eae5e25e5f1b30c4b59c438e15783eb501ec01e80", + "dist/2023-11-13/rust-std-beta-armebv7r-none-eabihf.tar.gz": "68e2ae2d1d6bb4eadfea2e1baccbe2984c38c13d9b492390ea897cf828976fc0", + "dist/2023-11-13/rust-std-beta-armebv7r-none-eabihf.tar.xz": "08858898e6b3c4855e8dfb015353a76ae5afa056208de6110135967fd752e6ed", + "dist/2023-11-13/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "06726cce8e9d1106443ef1d34fc9ff960cc8bfe7b4553ddd8e8c455360fb7a3b", + "dist/2023-11-13/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "b582686035b5ab0b855886feaa11a6b978cd0b9a1cbefe4afe9083e726c17916", + "dist/2023-11-13/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "2bcbccb51e5d10a766103d132cc10176e0747801a6fc1e3fcdc68b55f8970273", + "dist/2023-11-13/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "979f9c7c3a804552f41836a152fdfd8a68db2c144ff37bba3a85c99c4c7d21c3", + "dist/2023-11-13/rust-std-beta-armv7-linux-androideabi.tar.gz": "e5a563a023c4c90b2908c7de6535e208b412cf8ef67bc870d5a5079d7a2c4ab6", + "dist/2023-11-13/rust-std-beta-armv7-linux-androideabi.tar.xz": "88c84f7196f86a1e20400e9addbf1051ca90c1f6fae06ea711a0f49f5ba3ff1d", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "2a164112ad94b3729b39c2dcb5e6e1ac756e2f30bb0730db679a9aa59ad2cc26", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "c0eb6162b48266df4747833613ecf2a0234a4a7bf75331459bc230e7b868b676", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "2340f23434b411fa3da490cff5a41752d4f557324b3ee28bee2b089030f2e53f", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "fa34f4a1dbd757aebcbdd4e3e3de5053ef268b0a9fba7efa47607596f03b9e70", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "a9c3505a537f119a261dc882c0979c262efe68a5a798ee860924d0a0ad9ac359", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "3cb01b367a700d8ac8df14b6b87a81ff308fa9e4efaa08156ce3ea51b515c2ea", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "a57268eeb2308841c947b26c1492c3d9a36fb47408bb5b7909f0c3d4ab84f27c", + "dist/2023-11-13/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "38d8c2a2c7b8770c517f1cf17bba625dbe2f13c6899f61f3156d769a34515fbc", + "dist/2023-11-13/rust-std-beta-armv7a-none-eabi.tar.gz": "2e2d0f38992ef60701d1ea6ff647f72039e6a7fb483bf9ee4b341120e1d6df43", + "dist/2023-11-13/rust-std-beta-armv7a-none-eabi.tar.xz": "5a3115daef7c1d7138baa27e7bdc9ec3834fcab9e910ae2cbaec47ea833b8318", + "dist/2023-11-13/rust-std-beta-armv7r-none-eabi.tar.gz": "bb61701c8cdc26851b3939db5d9f965bb8167be3d681e006a1a9f0ae21e5a293", + "dist/2023-11-13/rust-std-beta-armv7r-none-eabi.tar.xz": "ca5264eaa211f9fe9fb2a61bb6c7efe9b2ad63a84976ce1a1d0742278003d484", + "dist/2023-11-13/rust-std-beta-armv7r-none-eabihf.tar.gz": "f399d12314de408001e925042b464052809c57c533dafa4694c16e4a4ae1d874", + "dist/2023-11-13/rust-std-beta-armv7r-none-eabihf.tar.xz": "50dd55169e78b93428c19c8dad61e037a019de50cb91ff559c29069de63d8e66", + "dist/2023-11-13/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "e2e512e6a1d5d86ed040c22d7a1fe504ba6f5de30c24065f19c23763cb4c8743", + "dist/2023-11-13/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "3ae3afdae7a52e933e75564476a8b080e414905436fe0e9706932d69900b1fc0", + "dist/2023-11-13/rust-std-beta-i586-pc-windows-msvc.tar.gz": "b06d0dc821d3d67ef20fe1987411b57cdfeb29e6f330d6b9922fd3b854cff28d", + "dist/2023-11-13/rust-std-beta-i586-pc-windows-msvc.tar.xz": "3c22775d9760be3699bfb1f4a0379cbd6838c03e2f8205a85d90c7a476664414", + "dist/2023-11-13/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "cdc0219c4ab66986f4de668b2c7cebd90ccde6958b50ce0dadee81518e67de34", + "dist/2023-11-13/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "09e838ae1922f7170e85c5b95d032f9feae9876f5e11bfc3693624b62b5a358f", + "dist/2023-11-13/rust-std-beta-i586-unknown-linux-musl.tar.gz": "e8b92575033950392e252dd68a0f8da310909bda23b4e40d6323d54211ee2604", + "dist/2023-11-13/rust-std-beta-i586-unknown-linux-musl.tar.xz": "584f829d6f324705c783ad97f67acdb34fe475fed8c4b91817978335d922ba1a", + "dist/2023-11-13/rust-std-beta-i686-linux-android.tar.gz": "0ef6e63ce3999c3035f0fb76bdc314966c4892a0ff7023941b1dc193e5c37a6b", + "dist/2023-11-13/rust-std-beta-i686-linux-android.tar.xz": "67d8dc585cec56aade8fcda986512fefd2c6346f446ab4df0764deb12cee5567", + "dist/2023-11-13/rust-std-beta-i686-pc-windows-gnu.tar.gz": "af77f489643aaf9b38681f038d7b6841122469841f68e1826525849e99081f69", + "dist/2023-11-13/rust-std-beta-i686-pc-windows-gnu.tar.xz": "5456f998f86146cb9a522734eb32ccc75155a10e28f26728909bc5e53e7c7b8b", + "dist/2023-11-13/rust-std-beta-i686-pc-windows-msvc.tar.gz": "2d770b47791453dacf349942b97c9fc8f4cf1c38130a37de6b698663194e6112", + "dist/2023-11-13/rust-std-beta-i686-pc-windows-msvc.tar.xz": "502623d29afe9167e2266b42a89b4224fb232fce73676da71502ca015daf8964", + "dist/2023-11-13/rust-std-beta-i686-unknown-freebsd.tar.gz": "d87ff9aea045e2414e3986227a78b75ac543200063d55022ae08e26e02f62819", + "dist/2023-11-13/rust-std-beta-i686-unknown-freebsd.tar.xz": "3c50e2ac1823ad810c360ff754984d5fa85d108ca38d83158059e95876127d97", + "dist/2023-11-13/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "233e5fac1a76ba28d23a5173e703bd24455307a78ff767dbb049192892dfce0c", + "dist/2023-11-13/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "f307359373a6dc48f5e18f3fff52326cb473c6b17db458c78a640bb488f294ef", + "dist/2023-11-13/rust-std-beta-i686-unknown-linux-musl.tar.gz": "360cecf2a1cd51f88c4da401f27b24f3a6395247b112428df3191990781f4e5b", + "dist/2023-11-13/rust-std-beta-i686-unknown-linux-musl.tar.xz": "d8246447e17987bdb19c6bd500eb575bf9041537fbfef8a6d82c1c98693ce63a", + "dist/2023-11-13/rust-std-beta-i686-unknown-uefi.tar.gz": "b5735aa3bf0c111c5bd652aca5bd8a2249d6a5d8b6152b404cd4f34b82bcd46f", + "dist/2023-11-13/rust-std-beta-i686-unknown-uefi.tar.xz": "f9a8e850c9a72948f31ba398037b389eea987b81213a2d743a04d4b818000e14", + "dist/2023-11-13/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz": "2eb39cd50a93749af784c634ea757e72c4e24347b7258642481aef73829ab87a", + "dist/2023-11-13/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz": "f01b91ea400ec0b9e565e2de689ab6ba48c78ef2f91ebccc2cfaf45c1b8cefbe", + "dist/2023-11-13/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz": "6b141c0f56a27540186a6cc880c3330a5bce099ff002f3681c680c1221a3b1c1", + "dist/2023-11-13/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz": "8f95e17e81d1b3dd039826f3ca483a570c9086b36c5523c277eaf302d0d6ff38", + "dist/2023-11-13/rust-std-beta-loongarch64-unknown-none.tar.gz": "78526ba4e1e78cdbb6943d8406a5bbc5fb8df2e1890d50d613b33bdc701b1c99", + "dist/2023-11-13/rust-std-beta-loongarch64-unknown-none.tar.xz": "bfb7baf432d1fba29933a55498764a4e563dbb75f444a4ef3e2bce71e25b8f84", + "dist/2023-11-13/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "9216385a85d7085474ce36bfc961b84e6a48ded07017ff5a057f502f6b0c7eec", + "dist/2023-11-13/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "f5501192af94ff399de9e74bd8d87ff37a986d369d3a6ed40fff85ab11d93676", + "dist/2023-11-13/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "2350b2f1bebda9335750671e6716dc6b7cee4897507e828fb8a65cc88a057b03", + "dist/2023-11-13/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "f31c185d7532fdaf4a358ef7ead703623fa3c2196f354eabdc7923f6da44db0f", + "dist/2023-11-13/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "22a03f008b066e0667bea31c0b1702d04c3eb57c34f19b2da6410e15eebb6872", + "dist/2023-11-13/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "8016d83a15e9ea72dc3f453671ae85dfcebfb518d1b306da96ae1a870997b337", + "dist/2023-11-13/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "3b166df751af8d54f4555d70407b2da6ade6972c07400a462680a8a40972691a", + "dist/2023-11-13/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "863da3831b4c6990f68a83cf3067b490e383ff1a33f7113a845a1ab8c2e2b714", + "dist/2023-11-13/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "8b571f65ec378563147a64a18fe202ac76a2c13a6ca2987976edbaa358a0e797", + "dist/2023-11-13/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "79d82bc4dc99af74ddebfc72305b32c1c819d9105c59359bb86b9b07d68db515", + "dist/2023-11-13/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "2ab118b25f237453d3aa68b9288c317e4474a469f4ed5e2f0fa10d85495a7c8f", + "dist/2023-11-13/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "25b9e9f41cfd797ffd25c2f5bfaf4ecb295de95db81e72a265e9d54bd5f41406", + "dist/2023-11-13/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "d6b3e8d853108716a2a9c6779a0ec2e7a496b58f98a27e95a5cfcc4c1acde7c4", + "dist/2023-11-13/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "e0f93c480e505d241ba00f4846d94a667df425800c204fdc0a30db2384012a13", + "dist/2023-11-13/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "8cf3de56808a9222c0b59f15616ccfc0c311a6c01c889be0f208ec48c3f92471", + "dist/2023-11-13/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "b46efcf1348f1f718355cfdb4a196001af55248a4c4f4cadf72a159c0369b30d", + "dist/2023-11-13/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "445a73b9646aa7e1bd02789db0c2486ef5b40ae76ba8f79fb7752e8a6e21e251", + "dist/2023-11-13/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "5afe29968b96ea83bb5e9e76b508ba3f51cae9da7c1d5635943b3d5dd00f1340", + "dist/2023-11-13/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "bab78b1672ca3b76a15cd151e9a63941dc226b7a64308290faa21eec777fada5", + "dist/2023-11-13/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "955965774ce971cdcb6b168604e110c7a1226059671d001f551aa1330783f909", + "dist/2023-11-13/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "f0eca14f82d86753444ac617789d4fd922437e62616123d0cdf3593fc2886e25", + "dist/2023-11-13/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "0d4ef0c263c798c5d636519d8893af46eee02339704bdc9c50d04e2029d48f47", + "dist/2023-11-13/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "fb652a85e83ca307eacfbd3155d46ec355bdcd46b2d9c94ae839c6cdbc78c4e3", + "dist/2023-11-13/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "1b8cfaeaf1d5d47760f9ee671b32ebbe81bfaa34074ad7c437602af65e91b1d8", + "dist/2023-11-13/rust-std-beta-sparcv9-sun-solaris.tar.gz": "5e5363302fee2567c27bfbe59a874b9d47c8dd27da502f7b5dda093a8327b936", + "dist/2023-11-13/rust-std-beta-sparcv9-sun-solaris.tar.xz": "fcd48e58e9ef18401d86482636f1bf2dd645e03e3d12896f5ac082ab5dc1990f", + "dist/2023-11-13/rust-std-beta-thumbv6m-none-eabi.tar.gz": "4b7e449d6d81ad8042506d617c27cf3fb3980299cf17a3b6b084f9253c95dfd0", + "dist/2023-11-13/rust-std-beta-thumbv6m-none-eabi.tar.xz": "b5f0cace5ec3a46e5cec31444e9d6340df5ae43584579d1b6171c7c22a925050", + "dist/2023-11-13/rust-std-beta-thumbv7em-none-eabi.tar.gz": "ebbe4cb796fbe017d0ceea0be022c1a6e6166e66eff943ac9aac548474ba774d", + "dist/2023-11-13/rust-std-beta-thumbv7em-none-eabi.tar.xz": "416e127948af40411b0063e677f2b212a46f784c9a15db9e4f762a487692ba07", + "dist/2023-11-13/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "5737d11a1a1ddb39dbef8f7f3feb752e396cda780180ebbf8d27be015a0ad2b1", + "dist/2023-11-13/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "616c5ffc3410720a3b4fbdbfca8399750bdccc5dfdb9e2a5eac52400186baddc", + "dist/2023-11-13/rust-std-beta-thumbv7m-none-eabi.tar.gz": "57f785f8ded6d3cfdf63658474f3ac65e80cdb00dae57f83a2d40f2c676491c1", + "dist/2023-11-13/rust-std-beta-thumbv7m-none-eabi.tar.xz": "5bed08004eac3bd3fb77bc47035ea3ae2095122f3fe4b83eb1401ed77d6fbc97", + "dist/2023-11-13/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "698e3213fc2e8a3b1fd2609a7bfe31549166f67768e7db6f0f3b8f462dfdb0f7", + "dist/2023-11-13/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "fabc41d905c2594bfddb7e789ca9ff42039ae43219ad70da73ddde5f3903110b", + "dist/2023-11-13/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "87eed169aeec1e8b3adefd3b3765e563a32fa355202e3110f115460198cd67be", + "dist/2023-11-13/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "f6b9bc666d0ada37ca8e9fb695d971e254ab1487d16923a413297d364f0a7827", + "dist/2023-11-13/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "b2ee2d569a0f4cc9f00d476b6197869bd46e6a25054f910bc997d4a47cd216c6", + "dist/2023-11-13/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "04e619d6cc94478bd474f97402ef0147e02078c5f24b4f9c3ab2d904a09cad62", + "dist/2023-11-13/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "49e2121df0ea56468be58522fc74c047b3bee7390242900575dfbe65edb96f54", + "dist/2023-11-13/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "ee743c99bfccb84fca2a1ffb569f1674b5e1710d7b670e3e62cc184c12bd61a6", + "dist/2023-11-13/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "cb9f8fd4fc4f2b27be37ec3777cd64bc638584b1744292b10af969bfb1b50f78", + "dist/2023-11-13/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "d62839e513f8a1fa57b97453d7bf6b15f85616828049a36d74d4de553e5b9856", + "dist/2023-11-13/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "ef29dbc48b483b4e8264e03d9869a87eb5f60f7f7f28dcca1361fadb54b79cdb", + "dist/2023-11-13/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "155f3844de6b49f5db34265320f4f94e45ac0f12f12000589d3a17cddef3ccce", + "dist/2023-11-13/rust-std-beta-wasm32-unknown-unknown.tar.gz": "e8a74c1a40b9bbe0d062d57d7169931d6d32b17ad0c82bc007cae16d400851f3", + "dist/2023-11-13/rust-std-beta-wasm32-unknown-unknown.tar.xz": "20521840fd7d292aece540525ccd11f9179700d3bc5c3be5403b032867e87afc", + "dist/2023-11-13/rust-std-beta-wasm32-wasi-preview1-threads.tar.gz": "7c090f46555efca8a60cf028321756b44ce7a27555204d114fc2661e64f2aa9a", + "dist/2023-11-13/rust-std-beta-wasm32-wasi-preview1-threads.tar.xz": "07b638d2f77fbb4286dd26537a8123fcf4a66f3bd065e541dc736536507127bd", + "dist/2023-11-13/rust-std-beta-wasm32-wasi.tar.gz": "089a845111c76efda5f50d84f3ab796f7e00a6710031a8a3d077d00414823637", + "dist/2023-11-13/rust-std-beta-wasm32-wasi.tar.xz": "1017d96927a3b65d8425e0c79320960b763ff9af3d31ebf71bf6ebcdcc7180d6", + "dist/2023-11-13/rust-std-beta-x86_64-apple-darwin.tar.gz": "98ce9ca7ff9a5d31006196bda22710f4a476b50f1e18759465c6026dec85fd08", + "dist/2023-11-13/rust-std-beta-x86_64-apple-darwin.tar.xz": "52d3637b78762c064e0548850e1ec5854fc0e98d339d7ae7d301ea9bb254ca3c", + "dist/2023-11-13/rust-std-beta-x86_64-apple-ios.tar.gz": "459b9e2b063afbb3c3014c9b6259ee5b3f774c4592393c22090dc1764b2a4eb4", + "dist/2023-11-13/rust-std-beta-x86_64-apple-ios.tar.xz": "3f8125a3565ff9f1bc4e528d6b23c4edc5bb12568418d88ca27ae4a0772dd6fd", + "dist/2023-11-13/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "8d35128421e601daab5fb5bbf2e3f5f23b18453efb39b71d54b36a15ba54c3f6", + "dist/2023-11-13/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "0f6b3cabefb9bc82eb4d8438524b3030a26053cc9b340f2aed4e68270c832c71", + "dist/2023-11-13/rust-std-beta-x86_64-linux-android.tar.gz": "5816f58ee8cbb0832357220ae4d25b87fde5090305a2298f3804faf1d0c87e85", + "dist/2023-11-13/rust-std-beta-x86_64-linux-android.tar.xz": "c0df3bc87d839b95bc1a5f69e2211020c183922a6d65508df90d60371f10e4a4", + "dist/2023-11-13/rust-std-beta-x86_64-pc-solaris.tar.gz": "d4a929235c25fdff8331e359612c6e2158b9984ed5b1a2d22d135c9ff714ad52", + "dist/2023-11-13/rust-std-beta-x86_64-pc-solaris.tar.xz": "d807cf6b57c12c9ab9baa703d2c38fb8817a185c0f1a3d846173d46c83f591ea", + "dist/2023-11-13/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "01e0338a3d36292ad73490c29e8ef11694dbbbc43eeb42faef2b4a824fd2f82e", + "dist/2023-11-13/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "5ebf0b26f4ece70aea9146c48eb2733fc517f387919842d5ec7caede5a7b4295", + "dist/2023-11-13/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "799e830f3151d8227e4c2a2c9c7f68706459b6d39fd9cb1041b3a87628cd7c58", + "dist/2023-11-13/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "4cd37d3f6214d120855ca215baadb9483218317dfc345bd390c86082d9801dfb", + "dist/2023-11-13/rust-std-beta-x86_64-sun-solaris.tar.gz": "b573908c72c8a11c879fc9b97d956f599e5642d38aed8a0a7dac68f988e0ac7a", + "dist/2023-11-13/rust-std-beta-x86_64-sun-solaris.tar.xz": "6493f17b04ea8817693a85268cb21e0b47f95edd4fd00561e104dccdae66812f", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "5dcedf3a1a8c12602932bc6683987a75db68a381019bad8207414843c35552d0", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "28c43fb5b7bf4c254ef61c5bc39da09e81881e01261a8a3346968170c8774581", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-fuchsia.tar.gz": "85f5052ba7d38dc951bbfbfa6f0d932866a8b49f54457ca1611804b1d9014ce6", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-fuchsia.tar.xz": "fbac8d59fd510bd5e4978fcd8e56a1e73de7140726df9c3106784496e744848c", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-illumos.tar.gz": "289dfdbd7607d2275b0ac57ceadbda7ea4c060636f007685e66d7a7170494ad7", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-illumos.tar.xz": "7d3a1849998b3212f226bd9c0a3193c0abca814d73edd0ff3c041d6b2338bc18", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "4d9e48adce6450cd114bf7509694c5ab7bedbf06210a9abd5461412617934133", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "f96c3b0bd75f15b8b6b9a6326c03ff5b4ee9aa16eaf426fdeb2e2213297854a4", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "6c4c00e554ff08bf6fc79df4790e3315c703bd789634f0dab01901a3527ced74", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "7f94911e59d0e7ca49f810124358d6ba8247df2136db1415216b90817200e91e", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "6408769d5540cc5a8cf741886ab9079184ff5562c6761d40b19874dbbc137e0a", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "a6b25bc4020a9f3ed59b16ee8e26ba8deab3d32dc3f771452e21301d1e9e905e", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "ad01d75532a0a05890e69c2d3a77b0c871a5aa50acda7cefce0654a2b634ad27", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "7eeb9cdaba6ffabd6f1aceb6c2eac354bb237b606582f6c88a754bedf5987735", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-none.tar.gz": "2e3292d36d8ce3b383b220106513a5e7cd7e90160c9326dd6d0960678b4b961c", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-none.tar.xz": "078cae8dae258af5fccde5efb2ce3599f5c1cb4a0aac05a9bb7e215e85d19cf7", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-redox.tar.gz": "7f6f2e63a6ef9501630dc02c8a66e805a1d8cae3e457d163cf02c65d3be213aa", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-redox.tar.xz": "0174212e92efc759ae9c75f9a32743a03029b7292f5b6b604b57ed8be9c58304", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-uefi.tar.gz": "228fe781f4206dd1cd22210c22404087eeb5a56b3a56adb9fda90100cea87efc", + "dist/2023-11-13/rust-std-beta-x86_64-unknown-uefi.tar.xz": "4515744a3dcbc08ce672efaf4589874597e0feae691d14d6ba9050c56c8098df", + "dist/2023-11-13/rustc-beta-aarch64-apple-darwin.tar.gz": "4bdddcb1c681df3203a723dc31e683ca0b517d8fec2e98cfde24550c375b3d79", + "dist/2023-11-13/rustc-beta-aarch64-apple-darwin.tar.xz": "28e922e7c6b08c6a33e25038fb86773948c5131a4360a000b488d1be9cf14fba", + "dist/2023-11-13/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "8a10a25a15e933d9807859e58bc1dabe1d3cce515148a91ccf767369b385e6c3", + "dist/2023-11-13/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "8a842f833c006b361bf10e6b68c54a86744c4dde9248d7a2240cef6a46761548", + "dist/2023-11-13/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "f8cf7a5a8ce11f260eb73b2d2fa06714263e3d5355e7dc1288e2bbc2407cf64b", + "dist/2023-11-13/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "4426e7b5da73ff357094f07f0f5dfafa7a627daa3c73101d9424a174acad4794", + "dist/2023-11-13/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "21d8b92246382c25630fd0bc462a485becb309027414311d64268babcbfaaa63", + "dist/2023-11-13/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "221d51e77fbc6af202563fdf557631f75064e5ecd7750cfdf03a9dab6e7748a5", + "dist/2023-11-13/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "659197905941615e7cd8e2c41a09699b0df9ff4540b514e942e66b0e0acb02cd", + "dist/2023-11-13/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "5624af4e8f4f2cb0c11d504d4d1514a75a2c5e35a5fee9e2888e12d6dfd5fa76", + "dist/2023-11-13/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "27bb3004bc4dd6822b67de5f05c90850b1b8b0f0702f1685a572fcf416dd04ab", + "dist/2023-11-13/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "5102179b53fbaa0c416d5305c8b76be4e7ee707d840ab5ccc0a76335debc236a", + "dist/2023-11-13/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "493076756274e9c588700821abf39193c7d7ee51f41034db773f649ceeceb530", + "dist/2023-11-13/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "c487bdb531c75d273026c256a4e5d126aa68ebeb40e2eb602b7b232719e76ada", + "dist/2023-11-13/rustc-beta-i686-pc-windows-gnu.tar.gz": "158a1e8b326c5077c335cac2d031d266d7d5db04a1b2e9d76c9081730166a6de", + "dist/2023-11-13/rustc-beta-i686-pc-windows-gnu.tar.xz": "ba0306b06819ee34b62edb40c9e78a60ab58d6de0493af1bf2dd72ba2d31ae38", + "dist/2023-11-13/rustc-beta-i686-pc-windows-msvc.tar.gz": "0188d1e4f66dc89611b9567356b9fcbe633a857140ebba83ba43c99c22353c6c", + "dist/2023-11-13/rustc-beta-i686-pc-windows-msvc.tar.xz": "1f66b7976350cb06ea1d7e8869c0276676dc82ebf1d23c57adf37c2b36da1c93", + "dist/2023-11-13/rustc-beta-i686-unknown-linux-gnu.tar.gz": "6646a59d419a19aa9fadb55c12faa93524a85e3e1d7f0e7e20ad8007757fbff9", + "dist/2023-11-13/rustc-beta-i686-unknown-linux-gnu.tar.xz": "7fa6483a4bb592ea4e86b7ddbb9e938aceb2fee0ad7a4106f415eed66699bf97", + "dist/2023-11-13/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz": "965c3f1a939386134e563f8c6fd5399221b8bf47f52d62fd640276762ff94dba", + "dist/2023-11-13/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz": "a956879fc43f02d6f3558494c59fbdd67a93d0a51f44a85247cbb0f69a9793dc", + "dist/2023-11-13/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "4474c8e5d3b1931c5330f7b337dfe093110d006466a846bc8b7e06abb9497e67", + "dist/2023-11-13/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "a7dded69245f0b007f6ed5c803cc886d04fdff68884c44e426df3bccc00d9dd3", + "dist/2023-11-13/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "c35144e8205a84e48ff5b32fb7c363d8a18d42f873b47b0a54189f32a422c462", + "dist/2023-11-13/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "22f8c6f103806968ec4dac5a53581cb862cda6b291864e555a93f908971f87bb", + "dist/2023-11-13/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "5eb310b5a54135e443fd7aa571815e289a1bd0f96f2bca1ad719cf156f382531", + "dist/2023-11-13/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "7fdbc3fedaa77d12b04dc9b9602a44d45eea72d29642f1112a839cc90d72c262", + "dist/2023-11-13/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "7e936ba6928cd7116045f9c0dad358661fa867fe32c62c0815a8b392b64ea9fa", + "dist/2023-11-13/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "dd41b7b468837fd8a96dc37c99ad5a29edf185df38b0f82e5582a6200aabc564", + "dist/2023-11-13/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "3bc91d1003d07f9378317cf38eadcba4c69c65c34cf73016ab524f63b5ae77c2", + "dist/2023-11-13/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "30022ac67369c112725c8e327a76100d438ffb3cab27bbe3fd62316de60af5f7", + "dist/2023-11-13/rustc-beta-x86_64-apple-darwin.tar.gz": "848549ae18928e8c2ceafa986fe99d3d103a5a97348f4ed9cf9825096e44d6f8", + "dist/2023-11-13/rustc-beta-x86_64-apple-darwin.tar.xz": "00f5ea9ee85bfb85ff66b1b2ce9b04207334cc731e33b7250d1593f92abb9b63", + "dist/2023-11-13/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "0fe8320e4c07636743156943851a17c606d32f79a773a394983021f987bf33ad", + "dist/2023-11-13/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "b61d7f52522f0576e71ebb1d83b995085b020bc00b3423078210239ebcf2f954", + "dist/2023-11-13/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "e2da8e7a2d7f9a558bdbb4f56273e139ea26dd6778d4006479890163c52dbf9d", + "dist/2023-11-13/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "4ea59ac07b85078514af502538535c1ef98799eb574d2036752ed608353266b2", + "dist/2023-11-13/rustc-beta-x86_64-unknown-freebsd.tar.gz": "5870b4f26a3f47ecc8cf5eaf0e5f062087f71dcd94cedb420e402724a5d340c3", + "dist/2023-11-13/rustc-beta-x86_64-unknown-freebsd.tar.xz": "c2094e3b40e602ab804e875ec1b1ab7e91f0ff499bc6c1e4b2a957ed4d136ab8", + "dist/2023-11-13/rustc-beta-x86_64-unknown-illumos.tar.gz": "ef47a9f5bb410eeb8361db7a71bb3faed4f71024ad0659c359a20346e75fc7b6", + "dist/2023-11-13/rustc-beta-x86_64-unknown-illumos.tar.xz": "444e9b6a42b3219bc2c556b9c2b1c20d8ab6411662fc55d76ee0b1775321cbd8", + "dist/2023-11-13/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "f492e76a0ad4182607d27145ad45bb8ba6f41fa194dc7a2dec121cdb7fafd918", + "dist/2023-11-13/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "898c58f43f6bc8f259a87dfa7f57c88f1069883604e927f4e1f4808ac8969838", + "dist/2023-11-13/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "fdf79b4d8aeb5b846caeb19ba7bb4b7e614cc4cb48d3d129470ee2c914afcc85", + "dist/2023-11-13/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "17666599cd367e20a1439b91ae19e3d3214c6aed5ae36016efe34938e9e6ddde", + "dist/2023-11-13/rustc-beta-x86_64-unknown-netbsd.tar.gz": "e3085637e043c5bf132411cea9421fbb6f062fa6dca6b73592512dd739d94cc0", + "dist/2023-11-13/rustc-beta-x86_64-unknown-netbsd.tar.xz": "ac4d1228639906c873f5ab4c12b770ea2c053380e68e46d0afa70bac48cdf16b", + "dist/2023-11-13/rustc-nightly-aarch64-apple-darwin.tar.gz": "1b113b6dc4c3bb4997be30a939f3ef1d699c5052d7387da92a7d17a5bbb3de98", + "dist/2023-11-13/rustc-nightly-aarch64-apple-darwin.tar.xz": "2ae2b64171c7ad6fe7c769a3507900ff024cfc6f621b4d566022000ba1d0febf", + "dist/2023-11-13/rustc-nightly-aarch64-pc-windows-msvc.tar.gz": "b7617099a6409b869f0b186acf775df723c11d0c625db9bdedfa2ef45cefe5db", + "dist/2023-11-13/rustc-nightly-aarch64-pc-windows-msvc.tar.xz": "e73652ac663242b0e10abdec32234ad6fb6c899bb5251488d7b19b17c8ccdff9", + "dist/2023-11-13/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz": "6569fab33d672526354484ec648a7e3287b3c1e43420e11a8e15fe9d5b3d6bc4", + "dist/2023-11-13/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz": "215070976901d265554415ee06df6f377691fbf9287548379a31dcb4beb52042", + "dist/2023-11-13/rustc-nightly-aarch64-unknown-linux-musl.tar.gz": "d3b0eec2524ae12b5c62f3d8e7ec924063e5a867975bea3048b14af2e04a60e9", + "dist/2023-11-13/rustc-nightly-aarch64-unknown-linux-musl.tar.xz": "222e74a5a6d0564d81d97a2c95e9aeeb0f17201505f18f6a073f6ed7d6167148", + "dist/2023-11-13/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz": "20de495b3f5140777d268fcfb17031ef0ee6b28ba483612f8d8544922d42c1eb", + "dist/2023-11-13/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz": "2eb1a8ba2d86fd43a3276e676524a2d34bfff9b12c8531308e5c9ef3a47400a6", + "dist/2023-11-13/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz": "7e3b4243ada8f8d2b95c8e3920dbb92c5712a14033128bb3b5ce65ff83998d7e", + "dist/2023-11-13/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz": "bc6261b5cfc43a2f5d308dd086cad7d4c983cb4b8e57843789fb6271c2221d07", + "dist/2023-11-13/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "366dbc3bd8290cbd17cf831dca5e75a6b92c8b2e256c4cdbf98f6cb13d857538", + "dist/2023-11-13/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "a6584f803d16b4338c1d2535d1ba359932e4d7b539953b579c39c5171f902ea7", + "dist/2023-11-13/rustc-nightly-i686-pc-windows-gnu.tar.gz": "095b37bf7bb69726ff58296da5b597478a5af64682d8e69b7076154123c758cb", + "dist/2023-11-13/rustc-nightly-i686-pc-windows-gnu.tar.xz": "5d5727b6b7c52b7d46a161e31c2637f8de33080ff92424edf1a287d4499b33e2", + "dist/2023-11-13/rustc-nightly-i686-pc-windows-msvc.tar.gz": "06b287a308d728c990c1f7e63dc33e90823a2e74fed57cde8e6c1de1e265a26e", + "dist/2023-11-13/rustc-nightly-i686-pc-windows-msvc.tar.xz": "4f5a923e4193280cd5acbb81e6d578d55b119085963a7915e89a2c9c324ad550", + "dist/2023-11-13/rustc-nightly-i686-unknown-linux-gnu.tar.gz": "2a9b18c971ddd81ce45162d224f3d83954f47d2e4f20c9259aa2f03840b4c42d", + "dist/2023-11-13/rustc-nightly-i686-unknown-linux-gnu.tar.xz": "40769cf6e32903769166a1d47a99de93052c820e53770b7d253452c0f5122e1f", + "dist/2023-11-13/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz": "67eb782c10ce7abc56124f789023cebc5772a01f1ef1adf107a90120a4f065c5", + "dist/2023-11-13/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz": "eca35c310f253e95aa324ab8772f1ab6335020e609f632162707222bd9a0de35", + "dist/2023-11-13/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz": "218a990d7d01b093e59bcfaed89eb3b53d4a50d5e3a82f53e079df11660ed2a4", + "dist/2023-11-13/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz": "007cb6841bcecbe98bbff8b3542cd39afec275fad5d2eb45af733409b6b4802b", + "dist/2023-11-13/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz": "765aa9f6aaed661607ad4a54015df14b67da611a99efcf080036116467c2ca0d", + "dist/2023-11-13/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz": "8d3ff5b8df0befe504be38d8dfb0807980e2be4673f244c4633e3c5d118ce4d9", + "dist/2023-11-13/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "0e74f7782fb99fc29e663634dd2f08cc47b50f714e04a8544eef4eb4defa4db8", + "dist/2023-11-13/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "b1381bc7ecb0c08e65ff32152681c0007a55117df2f52f8cb33cb4abea7f14d7", + "dist/2023-11-13/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "69318b9a4f6e1da3e6751e6e4e089a2180cd4068f54a4e965f9fae8a45bf9c89", + "dist/2023-11-13/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "7c64b2d0a8a9d24ed41235e17e45e75668dca40ef32cca47a9ce428de94a2922", + "dist/2023-11-13/rustc-nightly-s390x-unknown-linux-gnu.tar.gz": "44fd98bb055b90813a346592b7dbc40633b339de09fa1184cc177968f08efcd4", + "dist/2023-11-13/rustc-nightly-s390x-unknown-linux-gnu.tar.xz": "c968ae5d316b3783af6449fbabbf034743711175cdce7922734034b7916ad251", + "dist/2023-11-13/rustc-nightly-x86_64-apple-darwin.tar.gz": "e0540ab928f55092b8e70426bb7d098e74cabbedcebe6ff77ab485262cfedb5b", + "dist/2023-11-13/rustc-nightly-x86_64-apple-darwin.tar.xz": "c37789b8a4f187492506e340a679cfc047332ebf5694ae764cb2474afd1281bc", + "dist/2023-11-13/rustc-nightly-x86_64-pc-windows-gnu.tar.gz": "e0980059f71c1af5388c65f6f8f317bb55c5788a7bfbfaf472358f30faefb14f", + "dist/2023-11-13/rustc-nightly-x86_64-pc-windows-gnu.tar.xz": "39a13cf1d38638bdd98355f2494221304fd9928558719a2dc36e06699a97dc68", + "dist/2023-11-13/rustc-nightly-x86_64-pc-windows-msvc.tar.gz": "6e22b7936652eec3ac0c1da277ba9ef89d805defc4fc3abea61f2ce68bcf784f", + "dist/2023-11-13/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "464cf4f724210a72cff14a6924ffd896cf160bc7ab8831e3580593a2f7e54a31", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-freebsd.tar.gz": "c3a0ef4295a245c46e39c6a644a9a237e81dc95443930ec4230227b8bbdf2927", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-freebsd.tar.xz": "6cf9e31f4e5a5276c426b3a3ba6d9ba1f9c7b253d30fb2dacea92c9dba19c53d", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-illumos.tar.gz": "15cc5b822d9fa3d832899a07f5a373c6705907f26a5d90c97cad472eb6fbb383", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-illumos.tar.xz": "5cf0eb4e4dc8cf25c02fa8cb9bb60c27212a77088a6fde5a97a6dd11d21b9c5c", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz": "dbdbe9ad1b19a9f746d1831fb20be9581dbe7701544bedccb08458fb89719c33", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "e1d08f9cb45c86de3217d8c3bb7fa6f7ef201ac826b19b2750aa770f6b665cac", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-linux-musl.tar.gz": "2b30ecd41818e8e39b286bf1ea69353416bf79b38f3119bdb98c2ac4594a5f76", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-linux-musl.tar.xz": "ad42102fab1902fc1273e776345cdda9e40a5a39574421397405b7657741128a", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-netbsd.tar.gz": "a7b7dcf01dedf0c88443ca2baa49b84ff2027b8bc68502af0f53647fe2005bdf", + "dist/2023-11-13/rustc-nightly-x86_64-unknown-netbsd.tar.xz": "27410cfafe573d12e7fe5c2649cfbb6487a8df172867bf665bf3b3d67dfd44ef", + "dist/2023-11-13/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "fe2710bc7ab0f0a5e4797b011ea47ae797ae3824bd03d59d8c5a92222f750703", + "dist/2023-11-13/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "3cc0c843aa31d9262aff93b597673fe5c0a78bc9fc66a92899c498f566dca806", + "dist/2023-11-13/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "31d23f4bb53bdc6b93a07e50de1a8b5398c5f8e747f4da6b6bafa499a6ef9b83", + "dist/2023-11-13/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "b4d8b84c9fbe2635fb88c9d26a6f86b51651f07a2f8f139d332b49749e364525", + "dist/2023-11-13/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "dd24bd46bf4c24678d7bbee6f7b8e3bb6417131acb3f19c8293b84658e259d37", + "dist/2023-11-13/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "77cfd74f88a8266e3540bec0a4ea669694cb171145ee368158953d47ca74106d", + "dist/2023-11-13/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "63c3c7d5e72813514df27bed4d9daa2d3eefbc50176dba2ae48cc1aa85bd58d0", + "dist/2023-11-13/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "d01d0ce2aa536d635f3d69f6ca7c15b351a2dbace08a1c2aaf8718e6ea07829d", + "dist/2023-11-13/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "8eb1391c5657980cc8a810c1d5b14a28421b4bf81511e4a6e31a3aef15f2c689", + "dist/2023-11-13/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "9eac418607a5f9b1818439a608179ab907a0c2333ec05b18fcf6a36c0d1f67d9", + "dist/2023-11-13/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "d1221f9a330abfd4ffaec357d589148e0484200328cbc1db3224096be012ab30", + "dist/2023-11-13/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "51b2891e0296ce41fa57468cb97b9bb4a3e0a251ba7a5095ad828e5aa175d8e4", + "dist/2023-11-13/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "0d1c59d8947b77f2078122a3fc9c53b3179ba87caea53fa9156d3af913ac42fb", + "dist/2023-11-13/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "607811fe589646c251f14ea1981465000259605e9ddff8ffccdfd0936f851d16", + "dist/2023-11-13/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "044e8b5bff3a8d3259023706c0ef03c8068704534dd8f804c4f53466e35807eb", + "dist/2023-11-13/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "a27ea67409922132ab294a734828980a279ab5cd8a3d8c26b590e313e7da2ed5", + "dist/2023-11-13/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "0bd3c933ed3345fee541549127e20dd63f52a533f2f22523d1db096132ce0aaa", + "dist/2023-11-13/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "4334d49822f9a85d43fbfa463b55d4b9c424ef058c4146852a252162fdbc198e", + "dist/2023-11-13/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "5015ca31e974c16050e44c6ac6dffcbfa82b7532951842dd2745139463c5edb6", + "dist/2023-11-13/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "40380de3945ea3dee9dd05e4dc2d2c12a7d621d9bdfa59233faa2437e80db915", + "dist/2023-11-13/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz": "478ebf6629bf890c8d3726f090b8fcbc9cc5d5d828ec3554287668491e2bbf8e", + "dist/2023-11-13/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz": "3a130b2b7e06f6c16325bf36b8f5c702335c745471bf946a9d0ac2202af65fbb", + "dist/2023-11-13/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "002db7233067339672abc2e210d98897a3870842cbf1e487ae5c111d5bc949cc", + "dist/2023-11-13/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "a514b43eb0c1d8e2d20b6ff77b611e126d06522758f6d31b6fbd94639a6f04aa", + "dist/2023-11-13/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "7d21d10b85ec4f720ffca74184758eb568050776f1d3592dc65075b9c83c9f8a", + "dist/2023-11-13/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "2a335532b528ca936903bcaacfec2f34ecb37ddc88bc8c0762fb12c1bdd71d9c", + "dist/2023-11-13/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "73f983755c3f413abb0be9112776ee4ea3a6a122abc37e4f0aa81021dc7dd67a", + "dist/2023-11-13/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "dc164f3dcc898851265f3353f0400953637ea7b5684e91cac6961c2dedb50d03", + "dist/2023-11-13/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "b81877958bad0de48add2148ced0cbee5a66233386d0b50e9fef6ea844ac808d", + "dist/2023-11-13/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "4f755394b456a00a929d89aaa7814e70882f7ffd62489a833be4707ae687c465", + "dist/2023-11-13/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "8d1a9321201939ae453f21fdfe9498e1edcad176b9490531655f47fe7c3c9269", + "dist/2023-11-13/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "1fa1f0161620cd73e1a0bf39ab5225c77e9f3e0888cc0d59b71dab260f7cca9a", + "dist/2023-11-13/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "b6bd3c9eacbb7d10d16e82a9946a77442495259951705dd7c9685f02919c1c0c", + "dist/2023-11-13/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "777f05162f4c6396cf0ca75fed5ff37539534f179a399e3032f0b1b327cf53fe", + "dist/2023-11-13/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "dd5c0eccf137eb8c92211c062057b7fd4a8c49d60953498d3b123d91e1359e29", + "dist/2023-11-13/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "9d90e65fd32c86c77e96b0c46d47d1c2a04090f22d1ba3934bd0507fb8fc09c6", + "dist/2023-11-13/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "a33338f00147ee970b14cd24be361216d3f9ffb963952aa8c5b6731fb6cb3505", + "dist/2023-11-13/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "399cb9ff66482ec0200b4c8750d344ee77900972838b11895f9b542a43e75931", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "46ff40a2afdd62b0b9e3d39ba1ca7ba43a9719ac23cb7b8ec324f0a60859805b", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "c14de41967e9b30a166516944c399f490be89cb9217255e9d22d33fb03489d9a", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "df917224a72d311e8e26632413507dce4e4d3203c049545959c481905939da37", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "1f19725221ec719718a5836bd911f0f04e4a604e71d2557ff28713e5cc58b8e8", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "cb50baa37637b79cda8e7f7fd6a77fedde6ede21063fc3c01c7c61558998df4f", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "a1bf9051bdd85fa33041b84378af684ed974671f17694e00d6f370a27b0563d9", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "5a2df248d0586d4ea49dceaa9400ee9ac4b0cb1a97076b6ec366a9b55217fee9", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "06d4e6d2a909cefef0988d4ef35cd9ae6ed6d87b83a08a91ef2966ebe5989803", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "aba0a7d2d67413990bf0ae9b8f4fd580a43f708b01ae8403fce0c4eff1f35b54", + "dist/2023-11-13/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "53891c1cbf7ff0f648188a655a6dd7c253997f230f4bd7eaa1881bce54128be7" } } diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index aed6796fa13..30c7c7d2b25 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -82,7 +82,6 @@ static TARGETS: &[&str] = &[ "armv7r-none-eabi", "armv7r-none-eabihf", "armv7s-apple-ios", - "asmjs-unknown-emscripten", "bpfeb-unknown-none", "bpfel-unknown-none", "i386-apple-ios", diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 6790a5127895debec95c24aefaeb18e059270df +Subproject 9765a449d9b7341c2b49b88da41c2268ea59972 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 87a96bdeba6..e74df808e06 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,70 @@ document. ## Unreleased / Beta / In Rust Nightly -[1e8fdf49...master](https://github.com/rust-lang/rust-clippy/compare/1e8fdf49...master) +[7671c283...master](https://github.com/rust-lang/rust-clippy/compare/7671c283...master) + +## Rust 1.74 + +Current stable, released 2023-11-16 + +[View all 94 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-08-11T15%3A29%3A18Z..2023-09-25T08%3A48%3A22Z+base%3Amaster) + +### New Lints + +* [`redundant_as_str`] + [#11526](https://github.com/rust-lang/rust-clippy/pull/11526) +* [`needless_borrows_for_generic_args`] + [#11511](https://github.com/rust-lang/rust-clippy/pull/11511) +* [`path_ends_with_ext`] + [#11483](https://github.com/rust-lang/rust-clippy/pull/11483) +* [`unnecessary_map_on_constructor`] + [#11413](https://github.com/rust-lang/rust-clippy/pull/11413) +* [`missing_asserts_for_indexing`] + [#10692](https://github.com/rust-lang/rust-clippy/pull/10692) +* [`iter_out_of_bounds`] + [#11396](https://github.com/rust-lang/rust-clippy/pull/11396) +* [`implied_bounds_in_impls`] + [#11362](https://github.com/rust-lang/rust-clippy/pull/11362) +* [`reserve_after_initialization`] + [#11373](https://github.com/rust-lang/rust-clippy/pull/11373) +* [`should_panic_without_expect`] + [#11204](https://github.com/rust-lang/rust-clippy/pull/11204) + +### Moves and Deprecations + +* Renamed `incorrect_clone_impl_on_copy_type` to [`non_canonical_clone_impl`] + [#11358](https://github.com/rust-lang/rust-clippy/pull/11358) +* Renamed `incorrect_partial_ord_impl_on_ord_type` to [`non_canonical_partial_ord_impl`] + [#11358](https://github.com/rust-lang/rust-clippy/pull/11358) +* Moved [`non_canonical_clone_impl`] to `suspicious` (Now warn-by-default) + [#11358](https://github.com/rust-lang/rust-clippy/pull/11358) +* Moved [`non_canonical_partial_ord_impl`] to `suspicious` (Now warn-by-default) + [#11358](https://github.com/rust-lang/rust-clippy/pull/11358) +* Moved [`needless_pass_by_ref_mut`] to `nursery` (Now allow-by-default) + [#11596](https://github.com/rust-lang/rust-clippy/pull/11596) + +### Enhancements + +* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and + [`accept-comment-above-attributes`] to `true` by default + [#11170](https://github.com/rust-lang/rust-clippy/pull/11170) +* [`explicit_iter_loop`]: Added [`enforce-iter-loop-reborrow`] to disable reborrow linting by default + [#11418](https://github.com/rust-lang/rust-clippy/pull/11418) + +### ICE Fixes + +* [`enum_variant_names`]: No longer crashes if the threshold is 0 and the enum has no variants + [#11552](https://github.com/rust-lang/rust-clippy/pull/11552) +* [`cast_possible_truncation`]: No longer crashes on values larger than `u64::MAX` + [#11517](https://github.com/rust-lang/rust-clippy/pull/11517) +* [`tuple_array_conversions`]: No longer crashes if the array length is not usize + [#11379](https://github.com/rust-lang/rust-clippy/pull/11379) +* [`useless_conversion`]: No longer crashes, when the receiver is a non-fn item + [#11070](https://github.com/rust-lang/rust-clippy/pull/11070) ## Rust 1.73 -Current stable, released 2023-10-05 +Released 2023-10-05 [View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster) @@ -5123,6 +5182,7 @@ Released 2018-09-13 [`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items [`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds +[`iter_over_hash_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_over_hash_type [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 04af1b98b55..b1a59238c82 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -146,16 +146,10 @@ For example, the [`else_if_without_else`][else_if_without_else] lint is register pub mod else_if_without_else; // ... -pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { +pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { // ... store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); // ... - - store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - // ... - LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), - // ... - ]); } ``` diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 4b6688a76b4..3b138b480b6 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.75" +version = "0.1.76" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index 55c0e105b30..1803fc2d2f3 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -270,7 +270,7 @@ When using `cargo dev new_lint`, the lint is automatically registered and nothing more has to be done. When declaring a new lint by hand and `cargo dev update_lints` is used, the lint -pass may have to be registered manually in the `register_plugins` function in +pass may have to be registered manually in the `register_lints` function in `clippy_lints/src/lib.rs`: ```rust,ignore @@ -436,7 +436,7 @@ need to ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are required, just use the one with a lower MSRV. -First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`]. +First, add an MSRV alias for the required feature in [`clippy_config::msrvs`]. This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example. ```rust @@ -506,7 +506,7 @@ fn msrv_1_45() { ``` As a last step, the lint should be added to the lint documentation. This is done -in `clippy_lints/src/utils/conf.rs`: +in `clippy_config/src/conf.rs`: ```rust define_Conf! { @@ -516,7 +516,7 @@ define_Conf! { } ``` -[`clippy_utils::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/msrvs/index.html +[`clippy_config::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_config/msrvs/index.html ## Author lint @@ -657,7 +657,7 @@ Adding a configuration to a lint can be useful for thresholds or to constrain some behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: -1. Adding a new configuration entry to [`clippy_lints::utils::conf`] like this: +1. Adding a new configuration entry to [`clippy_config::conf`] like this: ```rust,ignore /// Lint: LINT_NAME. @@ -736,7 +736,7 @@ for some users. Adding a configuration is done in the following steps: Run `cargo collect-metadata` to generate documentation changes for the book. -[`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs +[`clippy_config::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_config/src/conf.rs [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs [`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui [`tests/ui-toml`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui-toml diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index 7c4aa5d4523..54f77b00190 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -186,7 +186,7 @@ However, sometimes we might want to declare a new lint by hand. In this case, we'd use `cargo dev update_lints` command afterwards. When a lint is manually declared, we might need to register the lint pass -manually in the `register_plugins` function in `clippy_lints/src/lib.rs`: +manually in the `register_lints` function in `clippy_lints/src/lib.rs`: ```rust store.register_late_pass(|_| Box::new(foo_functions::FooFunctions)); diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index cda8d17eed4..4a1805f7523 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1 +1,7 @@ avoid-breaking-exported-api = false + +# use the various `span_lint_*` methods instead, which also add a link to the docs +disallowed-methods = [ + "rustc_lint::context::LintContext::struct_span_lint", + "rustc_middle::ty::context::TyCtxt::struct_span_lint_hir" +] diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 2d41087b51d..20f31320127 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.75" +version = "0.1.76" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index e898221ffa7..df48cc3f5e3 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -1,7 +1,6 @@ use serde::de::{self, Deserializer, Visitor}; use serde::{ser, Deserialize, Serialize}; use std::fmt; -use std::hash::{Hash, Hasher}; #[derive(Clone, Debug, Deserialize)] pub struct Rename { @@ -33,32 +32,19 @@ impl DisallowedPath { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum MatchLintBehaviour { AllTypes, WellKnownTypes, Never, } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct MacroMatcher { pub name: String, - pub braces: (String, String), + pub braces: (char, char), } -impl Hash for MacroMatcher { - fn hash<H: Hasher>(&self, state: &mut H) { - self.name.hash(state); - } -} - -impl PartialEq for MacroMatcher { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} -impl Eq for MacroMatcher {} - impl<'de> Deserialize<'de> for MacroMatcher { fn deserialize<D>(deser: D) -> Result<Self, D::Error> where @@ -83,7 +69,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { V: de::MapAccess<'de>, { let mut name = None; - let mut brace: Option<String> = None; + let mut brace: Option<char> = None; while let Some(key) = map.next_key()? { match key { Field::Name => { @@ -104,7 +90,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?; Ok(MacroMatcher { name, - braces: [("(", ")"), ("{", "}"), ("[", "]")] + braces: [('(', ')'), ('{', '}'), ('[', ']')] .into_iter() .find(|b| b.0 == brace) .map(|(o, c)| (o.to_owned(), c.to_owned())) diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index eeea53ce46f..ddc20f7f37f 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -320,8 +320,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { extract_msrv_attr!({context_import}); }} - // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. - // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` + // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed. + // TODO: Update msrv config comment in `clippy_config/src/conf.rs` "# ) } else { diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 4bc27fd48e2..84246d285c0 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.75" +version = "0.1.76" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -14,7 +14,6 @@ cargo_metadata = "0.15.3" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } -if_chain = "1.0" itertools = "0.10.1" quine-mc_cluskey = "0.2" regex-syntax = "0.7" diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs index e3f4cf79d31..98299e1e4bd 100644 --- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs @@ -52,24 +52,22 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]); impl LateLintPass<'_> for AllowAttribute { // Separate each crate's features. fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) { - if_chain! { - if !in_external_macro(cx.sess(), attr.span); - if cx.tcx.features().lint_reasons; - if let AttrStyle::Outer = attr.style; - if let Some(ident) = attr.ident(); - if ident.name == rustc_span::symbol::sym::allow; - if !is_from_proc_macro(cx, &attr); - then { - span_lint_and_sugg( - cx, - ALLOW_ATTRIBUTES, - ident.span, - "#[allow] attribute found", - "replace it with", - "expect".into(), - Applicability::MachineApplicable, - ); - } + if !in_external_macro(cx.sess(), attr.span) + && cx.tcx.features().lint_reasons + && let AttrStyle::Outer = attr.style + && let Some(ident) = attr.ident() + && ident.name == rustc_span::symbol::sym::allow + && !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, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index 192bc7d9ddc..9799e703afe 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -14,7 +14,9 @@ declare_clippy_lint! { /// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`. /// /// ### Why is this bad? - /// `Arc<T>` is only `Send`/`Sync` when `T` is [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E), + /// `Arc<T>` is an Atomic `RC<T>` and guarantees that updates to the reference counter are + /// Atomic. This is useful in multiprocessing scenarios. To send an `Arc<T>` across processes + /// and make use of the atomic ref counter, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E), /// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` /// /// ### Example @@ -34,7 +36,7 @@ declare_clippy_lint! { #[clippy::version = "1.72.0"] pub ARC_WITH_NON_SEND_SYNC, suspicious, - "using `Arc` with a type that does not implement `Send` or `Sync`" + "using `Arc` with a type that does not implement `Send` and `Sync`" } declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); @@ -61,19 +63,25 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { cx, ARC_WITH_NON_SEND_SYNC, expr.span, - "usage of an `Arc` that is not `Send` or `Sync`", + "usage of an `Arc` that is not `Send` and `Sync`", |diag| { with_forced_trimmed_paths!({ + diag.note(format!("`Arc<{arg_ty}>` is not `Send` and `Sync` as:")); + if !is_send { - diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`")); + diag.note(format!("- the trait `Send` is not implemented for `{arg_ty}`")); } if !is_sync { - diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`")); + diag.note(format!("- the trait `Sync` is not implemented for `{arg_ty}`")); } - diag.note(format!("required for `{ty}` to implement `Send` and `Sync`")); + diag.help("consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types"); + + diag.note("if you intend to use `Arc` with `Send` and `Sync` traits"); - diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`"); + diag.note(format!( + "wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `{arg_ty}`" + )); }); }, ); diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 38364af27c7..2dcaf1f0167 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -5,7 +5,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug use clippy_utils::is_from_proc_macro; use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use if_chain::if_chain; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::{ @@ -20,7 +19,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{sym, DUMMY_SP, Span}; +use rustc_span::{sym, Span, DUMMY_SP}; use semver::Version; static UNIX_SYSTEMS: &[&str] = &[ @@ -371,7 +370,7 @@ declare_clippy_lint! { /// let _ = 1 / random(); /// } /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.74.0"] pub SHOULD_PANIC_WITHOUT_EXPECT, pedantic, "ensures that all `should_panic` attributes specify its expected panic message" @@ -470,13 +469,11 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { return; } for item in items { - if_chain! { - if let NestedMetaItem::MetaItem(mi) = &item; - if let MetaItemKind::NameValue(lit) = &mi.kind; - if mi.has_name(sym::since); - then { - check_semver(cx, item.span(), lit); - } + if let NestedMetaItem::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) + { + check_semver(cx, item.span(), lit); } } } @@ -579,15 +576,13 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { /// Returns the lint name if it is clippy lint. fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.name == sym::clippy; - then { - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - return Some(lint_name); - } + if let Some(meta_item) = lint.meta_item() + && meta_item.path.segments.len() > 1 + && let tool_name = meta_item.path.segments[0].ident + && tool_name.name == sym::clippy + { + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + return Some(lint_name); } None } @@ -856,18 +851,17 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It } fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { - if_chain! { - if msrv.meets(msrvs::TOOL_ATTRIBUTES); + if msrv.meets(msrvs::TOOL_ATTRIBUTES) // check cfg_attr - if attr.has_name(sym::cfg_attr); - if let Some(items) = attr.meta_item_list(); - if items.len() == 2; + && attr.has_name(sym::cfg_attr) + && let Some(items) = attr.meta_item_list() + && items.len() == 2 // check for `rustfmt` - if let Some(feature_item) = items[0].meta_item(); - if feature_item.has_name(sym::rustfmt); + && let Some(feature_item) = items[0].meta_item() + && feature_item.has_name(sym::rustfmt) // check for `rustfmt_skip` and `rustfmt::skip` - if let Some(skip_item) = &items[1].meta_item(); - if skip_item.has_name(sym!(rustfmt_skip)) + && let Some(skip_item) = &items[1].meta_item() + && (skip_item.has_name(sym!(rustfmt_skip)) || skip_item .path .segments @@ -875,21 +869,20 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr .expect("empty path in attribute") .ident .name - == sym::skip; + == sym::skip) // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 - if attr.style == AttrStyle::Outer; - then { - span_lint_and_sugg( - cx, - DEPRECATED_CFG_ATTR, - attr.span, - "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", - "use", - "#[rustfmt::skip]".to_string(), - Applicability::MachineApplicable, - ); - } + && attr.style == AttrStyle::Outer + { + span_lint_and_sugg( + cx, + DEPRECATED_CFG_ATTR, + attr.span, + "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", + "use", + "#[rustfmt::skip]".to_string(), + Applicability::MachineApplicable, + ); } } @@ -989,12 +982,10 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { mismatched.extend(find_mismatched_target_os(list)); }, MetaItemKind::Word => { - if_chain! { - if let Some(ident) = meta.ident(); - if let Some(os) = find_os(ident.name.as_str()); - then { - mismatched.push((os, ident.span)); - } + if let Some(ident) = meta.ident() + && let Some(os) = find_os(ident.name.as_str()) + { + mismatched.push((os, ident.span)); } }, MetaItemKind::NameValue(..) => {}, @@ -1005,30 +996,28 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { mismatched } - if_chain! { - if attr.has_name(sym::cfg); - if let Some(list) = attr.meta_item_list(); - let mismatched = find_mismatched_target_os(&list); - if !mismatched.is_empty(); - then { - let mess = "operating system used in target family position"; - - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { - // Avoid showing the unix suggestion multiple times in case - // we have more than one mismatch for unix-like systems - let mut unix_suggested = false; - - for (os, span) in mismatched { - let sugg = format!("target_os = \"{os}\""); - diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); - - if !unix_suggested && is_unix(os) { - diag.help("did you mean `unix`?"); - unix_suggested = true; - } + if attr.has_name(sym::cfg) + && let Some(list) = attr.meta_item_list() + && let mismatched = find_mismatched_target_os(&list) + && !mismatched.is_empty() + { + let mess = "operating system used in target family position"; + + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { + // Avoid showing the unix suggestion multiple times in case + // we have more than one mismatch for unix-like systems + let mut unix_suggested = false; + + for (os, span) in mismatched { + let sugg = format!("target_os = \"{os}\""); + diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); + + if !unix_suggested && is_unix(os) { + diag.help("did you mean `unix`?"); + unix_suggested = true; } - }); - } + } + }); } } diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs index 04bf541a5bd..28bd3fc7011 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs @@ -4,7 +4,6 @@ use clippy_utils::ty::implements_trait; use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{get_parent_expr, higher}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -114,15 +113,13 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { let _: Option<!> = for_each_expr(cond, |e| { if let ExprKind::Closure(closure) = e.kind { // do not lint if the closure is called using an iterator (see #1141) - if_chain! { - if let Some(parent) = get_parent_expr(cx, e); - if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind; - let caller = cx.typeck_results().expr_ty(self_arg); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, caller, iter_id, &[]); - then { - return ControlFlow::Continue(Descend::No); - } + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind + && let caller = cx.typeck_results().expr_ty(self_arg) + && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && implements_trait(cx, caller, iter_id, &[]) + { + return ControlFlow::Continue(Descend::No); } let body = cx.tcx.hir().body(closure.body); diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 0e0d229e877..d4f2e316890 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; @@ -151,17 +150,15 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { return Ok(Bool::Term(n as u8)); } - if_chain! { - if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind; - if implements_ord(self.cx, e_lhs); - if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; - if negate(e_binop.node) == Some(expr_binop.node); - if eq_expr_value(self.cx, e_lhs, expr_lhs); - if eq_expr_value(self.cx, e_rhs, expr_rhs); - then { - #[expect(clippy::cast_possible_truncation)] - return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); - } + if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind + && implements_ord(self.cx, e_lhs) + && let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind + && negate(e_binop.node) == Some(expr_binop.node) + && eq_expr_value(self.cx, e_lhs, expr_lhs) + && eq_expr_value(self.cx, e_rhs, expr_rhs) + { + #[expect(clippy::cast_possible_truncation)] + return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); } } let n = self.terminals.len(); diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 739ce8f67c2..789cd3b6c21 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -49,69 +49,62 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if_chain! { - if !e.span.from_expansion(); - if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; - if !addrof_target.span.from_expansion(); - if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind; - if !deref_target.span.from_expansion(); - if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); - let ref_ty = cx.typeck_results().expr_ty(deref_target); - if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); - if !is_from_proc_macro(cx, e); - then{ - - if let Some(parent_expr) = get_parent_expr(cx, e){ - if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) && - !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) { - return; - } + if !e.span.from_expansion() + && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind + && !addrof_target.span.from_expansion() + && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind + && !deref_target.span.from_expansion() + && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) + && let ref_ty = cx.typeck_results().expr_ty(deref_target) + && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind() + && !is_from_proc_macro(cx, e) + { + if let Some(parent_expr) = get_parent_expr(cx, e) { + if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) + && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) + { + return; + } - // modification to `&mut &*x` is different from `&mut x` - if matches!(deref_target.kind, ExprKind::Path(..) - | ExprKind::Field(..) - | ExprKind::Index(..) - | ExprKind::Unary(UnOp::Deref, ..)) - && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) { - return; - } + // modification to `&mut &*x` is different from `&mut x` + if matches!( + deref_target.kind, + ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..) + ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) + { + return; } + } - span_lint_and_then( - cx, - BORROW_DEREF_REF, - e.span, - "deref on an immutable reference", - |diag| { - diag.span_suggestion( - e.span, - "if you would like to reborrow, try removing `&*`", - snippet_opt(cx, deref_target.span).unwrap(), - Applicability::MachineApplicable - ); + span_lint_and_then( + cx, + BORROW_DEREF_REF, + e.span, + "deref on an immutable reference", + |diag| { + diag.span_suggestion( + e.span, + "if you would like to reborrow, try removing `&*`", + snippet_opt(cx, deref_target.span).unwrap(), + Applicability::MachineApplicable, + ); - // has deref trait -> give 2 help - // doesn't have deref trait -> give 1 help - if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){ - if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { - return; - } + // has deref trait -> give 2 help + // doesn't have deref trait -> give 1 help + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() { + if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { + return; } - - diag.span_suggestion( - e.span, - "if you would like to deref, try using `&**`", - format!( - "&**{}", - &snippet_opt(cx, deref_target.span).unwrap(), - ), - Applicability::MaybeIncorrect - ); - } - ); - } + diag.span_suggestion( + e.span, + "if you would like to deref, try using `&**`", + format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()), + Applicability::MaybeIncorrect, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs index f7a5b1857be..ec681adb7ae 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -2,7 +2,6 @@ use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId}; use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use itertools::Itertools; use rustc_hir::def_id::LOCAL_CRATE; use rustc_lint::LateContext; @@ -15,31 +14,33 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { let mut packages = metadata.packages.clone(); packages.sort_by(|a, b| a.name.cmp(&b.name)); - if_chain! { - if let Some(resolve) = &metadata.resolve; - if let Some(local_id) = packages - .iter() - .find_map(|p| if p.name == local_name.as_str() { Some(&p.id) } else { None }); - then { - for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { - let group: Vec<&Package> = group.collect(); - - if group.len() <= 1 { - continue; - } - - if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { - let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); - versions.sort(); - let versions = versions.iter().join(", "); - - span_lint( - cx, - MULTIPLE_CRATE_VERSIONS, - DUMMY_SP, - &format!("multiple versions for dependency `{name}`: {versions}"), - ); - } + if let Some(resolve) = &metadata.resolve + && let Some(local_id) = packages.iter().find_map(|p| { + if p.name == local_name.as_str() { + Some(&p.id) + } else { + None + } + }) + { + for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + let group: Vec<&Package> = group.collect(); + + if group.len() <= 1 { + continue; + } + + if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { + let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); + + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{name}`: {versions}"), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs b/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs index 4dcc9cbe3a7..244e98eb666 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs @@ -1,6 +1,5 @@ use cargo_metadata::Metadata; use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use rustc_lint::LateContext; use rustc_span::DUMMY_SP; @@ -9,19 +8,17 @@ use super::WILDCARD_DEPENDENCIES; pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { for dep in &metadata.packages[0].dependencies { // VersionReq::any() does not work - if_chain! { - if let Ok(wildcard_ver) = semver::VersionReq::parse("*"); - if let Some(ref source) = dep.source; - if !source.starts_with("git"); - if dep.req == wildcard_ver; - then { - span_lint( - cx, - WILDCARD_DEPENDENCIES, - DUMMY_SP, - &format!("wildcard dependency for `{}`", dep.name), - ); - } + if let Ok(wildcard_ver) = semver::VersionReq::parse("*") + && let Some(ref source) = dep.source + && !source.starts_with("git") + && dep.req == wildcard_ver + { + span_lint( + cx, + WILDCARD_DEPENDENCIES, + DUMMY_SP, + &format!("wildcard dependency for `{}`", dep.name), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs index ffa571abb39..2ddb0f00ecd 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,5 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::Expr; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::LateContext; use rustc_middle::ty::Ty; use super::{utils, CAST_POSSIBLE_WRAP}; @@ -78,13 +79,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca ), }; - cx.struct_span_lint(CAST_POSSIBLE_WRAP, expr.span, message, |diag| { + span_lint_and_then(cx, CAST_POSSIBLE_WRAP, expr.span, &message, |diag| { if let EmitState::LintOnPtrSize(16) = should_lint { diag - .note("`usize` and `isize` may be as small as 16 bits on some platforms") - .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types") - } else { - diag - } + .note("`usize` and `isize` may be as small as 16 bits on some platforms") + .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); + }; }); } 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 a83dfd94dc2..bd12ee40628 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,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{method_chain_args, sext}; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -28,13 +27,11 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast // Don't lint for positive constants. let const_val = constant(cx, cx.typeck_results(), cast_op); - if_chain! { - if let Some(Constant::Int(n)) = const_val; - if let ty::Int(ity) = *cast_from.kind(); - if sext(cx.tcx, n, ity) >= 0; - then { - return false; - } + if let Some(Constant::Int(n)) = const_val + && let ty::Int(ity) = *cast_from.kind() + && sext(cx.tcx, n, ity) >= 0 + { + return false; } // Don't lint for the result of methods that always return non-negative values. @@ -42,13 +39,11 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast let mut method_name = path.ident.name.as_str(); let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; - if_chain! { - if method_name == "unwrap"; - if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]); - if let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind; - then { - method_name = inner_path.ident.name.as_str(); - } + if method_name == "unwrap" + && let Some(arglist) = method_chain_args(cast_op, &["unwrap"]) + && let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind + { + method_name = inner_path.ident.name.as_str(); } if allowed_methods.iter().any(|&name| method_name == name) { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs index d1410402913..2a9f7fec172 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source; -use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -69,26 +68,24 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); - if_chain! { - if let Some(parent_id) = map.opt_parent_id(expr.hir_id); - if let Some(parent) = map.find(parent_id); - then { - let expr = match parent { - Node::Block(block) => { - if let Some(parent_expr) = block.expr { - parent_expr - } else { - return false; - } - }, - Node::Expr(expr) => expr, - _ => return false, - }; + if let Some(parent_id) = map.opt_parent_id(expr.hir_id) + && let Some(parent) = map.find(parent_id) + { + let expr = match parent { + Node::Block(block) => { + if let Some(parent_expr) = block.expr { + parent_expr + } else { + return false; + } + }, + Node::Expr(expr) => expr, + _ => return false, + }; - matches!(expr.kind, ExprKind::Cast(..)) - } else { - false - } + matches!(expr.kind, ExprKind::Cast(..)) + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index badadf2c9f6..3db1e3e6d97 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; @@ -25,34 +24,32 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> { } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { - if_chain! { - if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); - if let ty::RawPtr(ptrty) = cast_to.kind(); - if let ty::Slice(_) = ptrty.ty.kind(); - if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - if let Some(rpk) = raw_parts_kind(cx, fun_def_id); - let ctxt = expr.span.ctxt(); - if cast_expr.span.ctxt() == ctxt; - then { - let func = match rpk { - RawPartsKind::Immutable => "from_raw_parts", - RawPartsKind::Mutable => "from_raw_parts_mut" - }; - let span = expr.span; - let mut applicability = Applicability::MachineApplicable; - let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; - let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; - span_lint_and_sugg( - cx, - CAST_SLICE_FROM_RAW_PARTS, - span, - &format!("casting the result of `{func}` to {cast_to}"), - "replace with", - format!("core::ptr::slice_{func}({ptr}, {len})"), - applicability - ); - } + if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) + && let ty::RawPtr(ptrty) = cast_to.kind() + && let ty::Slice(_) = ptrty.ty.kind() + && let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(rpk) = raw_parts_kind(cx, fun_def_id) + && let ctxt = expr.span.ctxt() + && cast_expr.span.ctxt() == ctxt + { + let func = match rpk { + RawPartsKind::Immutable => "from_raw_parts", + RawPartsKind::Mutable => "from_raw_parts_mut", + }; + let span = expr.span; + let mut applicability = Applicability::MachineApplicable; + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + span_lint_and_sugg( + cx, + CAST_SLICE_FROM_RAW_PARTS, + span, + &format!("casting the result of `{func}` to {cast_to}"), + "replace with", + format!("core::ptr::slice_{func}({ptr}, {len})"), + applicability, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs index 82e07c98a7e..a7d3868f76c 100644 --- a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -10,32 +9,31 @@ use rustc_middle::ty::{self, UintTy}; use super::CHAR_LIT_AS_U8; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Cast(e, _) = &expr.kind; - if let ExprKind::Lit(l) = &e.kind; - if let LitKind::Char(c) = l.node; - if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); - then { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); + if let ExprKind::Cast(e, _) = &expr.kind + && let ExprKind::Lit(l) = &e.kind + && let LitKind::Char(c) = l.node + && ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind() + { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); - span_lint_and_then( - cx, - CHAR_LIT_AS_U8, - expr.span, - "casting a character literal to `u8` truncates", - |diag| { - diag.note("`char` is four bytes wide, but `u8` is a single byte"); + span_lint_and_then( + cx, + CHAR_LIT_AS_U8, + expr.span, + "casting a character literal to `u8` truncates", + |diag| { + diag.note("`char` is four bytes wide, but `u8` is a single byte"); - if c.is_ascii() { - diag.span_suggestion( - expr.span, - "use a byte literal instead", - format!("b{snippet}"), - applicability, - ); - } - }); - } + if c.is_ascii() { + diag.span_suggestion( + expr.span, + "use a byte literal instead", + format!("b{snippet}"), + applicability, + ); + } + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index 0172e933649..ff069860a11 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -17,29 +16,35 @@ pub(super) fn check<'tcx>( cast_to: Ty<'tcx>, msrv: &Msrv, ) { - if_chain! { - if msrv.meets(msrvs::POINTER_CAST_CONSTNESS); - if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, ty: from_ty }) = cast_from.kind(); - if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, ty: to_ty }) = cast_to.kind(); - if matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)); - if from_ty == to_ty; - then { - let sugg = Sugg::hir(cx, cast_expr, "_"); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; + if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) + && let ty::RawPtr(TypeAndMut { + mutbl: from_mutbl, + ty: from_ty, + }) = cast_from.kind() + && let ty::RawPtr(TypeAndMut { + mutbl: to_mutbl, + ty: to_ty, + }) = cast_to.kind() + && matches!( + (from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) + ) + && from_ty == to_ty + { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; - span_lint_and_sugg( - cx, - PTR_CAST_CONSTNESS, - expr.span, - "`as` casting between raw pointers while changing only its constness", - &format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing only its constness", + &format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 61bfce07e1a..849920bb76d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -3,7 +3,6 @@ use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{for_each_expr, Visitable}; use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; -use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -25,40 +24,40 @@ pub(super) fn check<'tcx>( ) -> bool { let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); - if_chain! { - if let ty::RawPtr(..) = cast_from.kind(); + if let ty::RawPtr(..) = cast_from.kind() // check both mutability and type are the same - if cast_from.kind() == cast_to.kind(); - if let ExprKind::Cast(_, cast_to_hir) = expr.kind; + && cast_from.kind() == cast_to.kind() + && let ExprKind::Cast(_, cast_to_hir) = expr.kind // Ignore casts to e.g. type aliases and infer types // - p as pointer_alias // - p as _ - if let TyKind::Ptr(to_pointee) = cast_to_hir.kind; - then { - match to_pointee.ty.kind { - // Ignore casts to pointers that are aliases or cfg dependant, e.g. - // - p as *const std::ffi::c_char (alias) - // - p as *const std::os::raw::c_char (cfg dependant) - TyKind::Path(qpath) => { - if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { - return false; - } - }, - // Ignore `p as *const _` - TyKind::Infer => return false, - _ => {}, - } - - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"), - "try", - cast_str.clone(), - Applicability::MaybeIncorrect, - ); + && let TyKind::Ptr(to_pointee) = cast_to_hir.kind + { + match to_pointee.ty.kind { + // Ignore casts to pointers that are aliases or cfg dependant, e.g. + // - p as *const std::ffi::c_char (alias) + // - p as *const std::os::raw::c_char (cfg dependant) + TyKind::Path(qpath) => { + if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { + return false; + } + }, + // Ignore `p as *const _` + TyKind::Infer => return false, + _ => {}, } + + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!( + "casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)" + ), + "try", + cast_str.clone(), + Applicability::MaybeIncorrect, + ); } // skip cast of local that is a type alias @@ -86,14 +85,12 @@ pub(super) fn check<'tcx>( } // skip cast to non-primitive type - if_chain! { - if let ExprKind::Cast(_, cast_to) = expr.kind; - if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind; - if let Res::PrimTy(_) = path.res; - then {} - else { - return false; - } + if let ExprKind::Cast(_, cast_to) = expr.kind + && let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind + && let Res::PrimTy(_) = path.res + { + } else { + return false; } // skip cast of fn call that returns type alias @@ -106,18 +103,19 @@ pub(super) fn check<'tcx>( if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = &cast_str; - if_chain! { - if let LitKind::Int(n, _) = lit.node; - if let Some(src) = snippet_opt(cx, cast_expr.span); - if cast_to.is_floating_point(); - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); - let from_nbits = 128 - n.leading_zeros(); - let to_nbits = fp_ty_mantissa_nbits(cast_to); - if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); - then { - lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); - return true - } + if let LitKind::Int(n, _) = lit.node + && let Some(src) = snippet_opt(cx, cast_expr.span) + && cast_to.is_floating_point() + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) + && let from_nbits = 128 - n.leading_zeros() + && let to_nbits = fp_ty_mantissa_nbits(cast_to) + && from_nbits != 0 + && to_nbits != 0 + && from_nbits <= to_nbits + && num_lit.is_decimal() + { + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } match lit.node { diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index d31c2268a65..69fa0821e3f 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -4,7 +4,6 @@ use clippy_config::msrvs::{self, Msrv}; 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 if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -55,20 +54,17 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { return; } - let result = if_chain! { - if !in_constant(cx, item.hir_id); - if !in_external_macro(cx.sess(), item.span); - if let ExprKind::Binary(op, left, right) = &item.kind; - - then { - match op.node { - BinOpKind::Ge | BinOpKind::Le => single_check(item), - BinOpKind::And => double_check(cx, left, right), - _ => None, - } - } else { - None + let result = if !in_constant(cx, item.hir_id) + && !in_external_macro(cx.sess(), item.span) + && let ExprKind::Binary(op, left, right) = &item.kind + { + match op.node { + BinOpKind::Ge | BinOpKind::Le => single_check(item), + BinOpKind::And => double_check(cx, left, right), + _ => None, } + } else { + None }; if let Some(cv) = result { @@ -193,16 +189,13 @@ impl ConversionType { /// Check for `expr <= (to_type::MAX as from_type)` fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - if_chain! { - if let ExprKind::Binary(ref op, left, right) = &expr.kind; - if let Some((candidate, check)) = normalize_le_ge(op, left, right); - if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); - - then { - Conversion::try_new(candidate, from, to) - } else { - None - } + if let ExprKind::Binary(ref op, left, right) = &expr.kind + && let Some((candidate, check)) = normalize_le_ge(op, left, right) + && let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX") + { + Conversion::try_new(candidate, from, to) + } else { + None } } @@ -243,33 +236,27 @@ fn get_types_from_cast<'a>( ) -> Option<(&'a str, &'a str)> { // `to_type::max_value() as from_type` // or `to_type::MAX as from_type` - let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { + let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind // to_type::max_value(), from_type - if let ExprKind::Cast(limit, from_type) = &expr.kind; - if let TyKind::Path(ref from_type_path) = &from_type.kind; - if let Some(from_sym) = int_ty_to_sym(from_type_path); - - then { - Some((limit, from_sym)) - } else { - None - } + && let TyKind::Path(ref from_type_path) = &from_type.kind + && let Some(from_sym) = int_ty_to_sym(from_type_path) + { + Some((limit, from_sym)) + } else { + None }; // `from_type::from(to_type::max_value())` let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { - if_chain! { + if let ExprKind::Call(from_func, [limit]) = &expr.kind // `from_type::from, to_type::max_value()` - if let ExprKind::Call(from_func, [limit]) = &expr.kind; // `from_type::from` - if let ExprKind::Path(ref path) = &from_func.kind; - if let Some(from_sym) = get_implementing_type(path, INTS, "from"); - - then { - Some((limit, from_sym)) - } else { - None - } + && let ExprKind::Path(ref path) = &from_func.kind + && let Some(from_sym) = get_implementing_type(path, INTS, "from") + { + Some((limit, from_sym)) + } else { + None } }); @@ -298,31 +285,27 @@ fn get_types_from_cast<'a>( /// Gets the type which implements the called function fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { - if_chain! { - if let QPath::TypeRelative(ty, path) = &path; - if path.ident.name.as_str() == function; - if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; - if let [int] = tp.segments; - then { - let name = int.ident.name.as_str(); - candidates.iter().find(|c| &name == *c).copied() - } else { - None - } + if let QPath::TypeRelative(ty, path) = &path + && path.ident.name.as_str() == function + && let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind + && let [int] = tp.segments + { + let name = int.ident.name.as_str(); + candidates.iter().find(|c| &name == *c).copied() + } else { + None } } /// Gets the type as a string, if it is a supported integer fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { - if_chain! { - if let QPath::Resolved(_, path) = *path; - if let [ty] = path.segments; - then { - let name = ty.ident.name.as_str(); - INTS.iter().find(|c| &name == *c).copied() - } else { - None - } + if let QPath::Resolved(_, path) = *path + && let [ty] = path.segments + { + let name = ty.ident.name.as_str(); + INTS.iter().find(|c| &name == *c).copied() + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index d21ef195d9b..e5aaf88ab6c 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -15,7 +15,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -121,49 +120,55 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { } fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { - if_chain! { - if let ast::ExprKind::Block(ref block, _) = else_.kind; - if !block_starts_with_comment(cx, block); - if let Some(else_) = expr_block(block); - if else_.attrs.is_empty(); - if !else_.span.from_expansion(); - if let ast::ExprKind::If(..) = else_.kind; - then { - // Prevent "elseif" - // Check that the "else" is followed by whitespace - let up_to_else = then_span.between(block.span); - let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; + if let ast::ExprKind::Block(ref block, _) = else_.kind + && !block_starts_with_comment(cx, block) + && let Some(else_) = expr_block(block) + && else_.attrs.is_empty() + && !else_.span.from_expansion() + && let ast::ExprKind::If(..) = else_.kind + { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { + !c.is_whitespace() + } else { + false + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - COLLAPSIBLE_ELSE_IF, - block.span, - "this `else { if .. }` block can be collapsed", - "collapse nested if block", - format!( - "{}{}", - if requires_space { " " } else { "" }, - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COLLAPSIBLE_ELSE_IF, + block.span, + "this `else { if .. }` block can be collapsed", + "collapse nested if block", + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), + applicability, + ); } } fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { - if_chain! { - if !block_starts_with_comment(cx, then); - if let Some(inner) = expr_block(then); - if inner.attrs.is_empty(); - if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; + if !block_starts_with_comment(cx, then) + && let Some(inner) = expr_block(then) + && inner.attrs.is_empty() + && let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind // Prevent triggering on `if c { if let a = b { .. } }`. - if !matches!(check_inner.kind, ast::ExprKind::Let(..)); - let ctxt = expr.span.ctxt(); - if inner.span.ctxt() == ctxt; - then { - span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { + && !matches!(check_inner.kind, ast::ExprKind::Let(..)) + && let ctxt = expr.span.ctxt() + && inner.span.ctxt() == ctxt + { + span_lint_and_then( + cx, + COLLAPSIBLE_IF, + expr.span, + "this `if` statement can be collapsed", + |diag| { let mut app = Applicability::MachineApplicable; let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); @@ -177,8 +182,8 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & ), app, // snippet ); - }); - } + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index e3a09636e24..3b6d4886ba3 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -117,7 +117,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub IF_SAME_THEN_ELSE, - correctness, + style, "`if` with the same `then` and `else` blocks" } diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs index 5d04ad0112d..db850edd640 100644 --- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -5,8 +5,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use if_chain::if_chain; - declare_clippy_lint! { /// ### What it does /// Checks for types that implement `Copy` as well as @@ -38,25 +36,23 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - .. - }) = item.kind; - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - if is_copy(cx, ty); - if let Some(trait_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); - then { - span_lint_and_note( - cx, - COPY_ITERATOR, - item.span, - "you are implementing `Iterator` on a `Copy` type", - None, - "consider implementing `IntoIterator` instead", - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + .. + }) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && is_copy(cx, ty) + && let Some(trait_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Iterator, trait_id) + { + span_lint_and_note( + cx, + COPY_ITERATOR, + item.span, + "you are implementing `Iterator` on a `Copy` type", + None, + "consider implementing `IntoIterator` instead", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index a2005638d24..637d5aae1be 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -53,35 +53,31 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if item.attrs.iter().any(is_macro_export); - if let ItemKind::MacroDef(macro_def) = &item.kind; - let tts = macro_def.body.tokens.clone(); - if let Some(span) = contains_unhygienic_crate_reference(&tts); - then { - span_lint_and_sugg( - cx, - CRATE_IN_MACRO_DEF, - span, - "`crate` references the macro call's crate", - "to reference the macro definition's crate, use", - String::from("$crate"), - Applicability::MachineApplicable, - ); - } + if item.attrs.iter().any(is_macro_export) + && let ItemKind::MacroDef(macro_def) = &item.kind + && let tts = macro_def.body.tokens.clone() + && let Some(span) = contains_unhygienic_crate_reference(&tts) + { + span_lint_and_sugg( + cx, + CRATE_IN_MACRO_DEF, + span, + "`crate` references the macro call's crate", + "to reference the macro definition's crate, use", + String::from("$crate"), + Applicability::MachineApplicable, + ); } } } fn is_macro_export(attr: &Attribute) -> bool { - if_chain! { - if let AttrKind::Normal(normal) = &attr.kind; - if let [segment] = normal.item.path.segments.as_slice(); - then { - segment.ident.name == sym::macro_export - } else { - false - } + if let AttrKind::Normal(normal) = &attr.kind + && let [segment] = normal.item.path.segments.as_slice() + { + segment.ident.name == sym::macro_export + } else { + false } } @@ -89,14 +85,12 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> { let mut prev_is_dollar = false; let mut cursor = tts.trees(); while let Some(curr) = cursor.next() { - if_chain! { - if !prev_is_dollar; - if let Some(span) = is_crate_keyword(curr); - if let Some(next) = cursor.look_ahead(0); - if is_token(next, &TokenKind::ModSep); - then { - return Some(span); - } + if !prev_is_dollar + && let Some(span) = is_crate_keyword(curr) + && let Some(next) = cursor.look_ahead(0) + && is_token(next, &TokenKind::ModSep) + { + return Some(span); } if let TokenTree::Delimited(_, _, tts) = &curr { let span = contains_unhygienic_crate_reference(tts); @@ -110,10 +104,18 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> { } fn is_crate_keyword(tt: &TokenTree) -> Option<Span> { - if_chain! { - if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }, _) = tt; - if symbol.as_str() == "crate"; - then { Some(*span) } else { None } + if let TokenTree::Token( + Token { + kind: TokenKind::Ident(symbol, _), + span, + }, + _, + ) = tt + && symbol.as_str() == "crate" + { + Some(*span) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 2bca695c43b..97b736dfd8f 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -33,22 +32,20 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]); impl LateLintPass<'_> for CreateDir { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg, ..]) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id); - then { - span_lint_and_sugg( - 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, - ) - } + if let ExprKind::Call(func, [arg, ..]) = expr.kind + && let ExprKind::Path(ref path) = func.kind + && 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( + 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, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 49452136d6f..4774917c7b5 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Pos, Span}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -31,31 +31,6 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -/// Gets the span of the statement up to the next semicolon, if and only if the next -/// non-whitespace character actually is a semicolon. -/// E.g. -/// ```rust,ignore -/// -/// dbg!(); -/// ^^^^^^^ this span is returned -/// -/// foo!(dbg!()); -/// no span is returned -/// ``` -fn span_including_semi(cx: &LateContext<'_>, span: Span) -> Option<Span> { - let sm = cx.sess().source_map(); - let sf = sm.lookup_source_file(span.hi()); - let src = sf.src.as_ref()?.get(span.hi().to_usize()..)?; - let first_non_whitespace = src.find(|c: char| !c.is_whitespace())?; - - if src.as_bytes()[first_non_whitespace] == b';' { - let hi = span.hi() + BytePos::from_usize(first_non_whitespace + 1); - Some(span.with_hi(hi)) - } else { - None - } -} - #[derive(Copy, Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, @@ -88,10 +63,10 @@ impl LateLintPass<'_> for DbgMacro { ExprKind::Block(..) => { // If the `dbg!` macro is a "free" statement and not contained within other expressions, // remove the whole statement. - if let Some(Node::Stmt(stmt)) = cx.tcx.hir().find_parent(expr.hir_id) - && let Some(span) = span_including_semi(cx, stmt.span.source_callsite()) + if let Some(Node::Stmt(_)) = cx.tcx.hir().find_parent(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) { - (span, String::new()) + (macro_call.span.to(semi_span), String::new()) } else { (macro_call.span, String::from("()")) } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 1a646ba38c3..85854a0dfb7 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -10,8 +10,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, @@ -141,6 +139,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO, crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, + crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO, crate::doc::UNNECESSARY_SAFETY_DOC_INFO, crate::double_parens::DOUBLE_PARENS_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO, @@ -232,6 +231,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::iter_over_hash_type::ITER_OVER_HASH_TYPE_INFO, crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO, crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO, crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, @@ -628,7 +628,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::strings::STR_TO_STRING_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, - crate::suspicious_doc_comments::SUSPICIOUS_DOC_COMMENTS_INFO, crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO, crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index c74b2b8831e..b325449c5a3 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_drop, is_copy}; use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -81,33 +80,31 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); + if !expr.span.from_expansion() // Avoid cases already linted by `field_reassign_with_default` - if !self.reassigned_linted.contains(&expr.span); - if let ExprKind::Call(path, ..) = expr.kind; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::default_fn, def_id); - if !is_update_syntax_base(cx, expr); + && !self.reassigned_linted.contains(&expr.span) + && let ExprKind::Call(path, ..) = expr.kind + && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::default_fn, def_id) + && !is_update_syntax_base(cx, expr) // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type. - if let QPath::Resolved(None, _path) = qpath; - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind(); - if !is_from_proc_macro(cx, expr); - then { - let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{replacement}` is more clear than this expression"), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } + && let QPath::Resolved(None, _path) = qpath + && let expr_ty = cx.typeck_results().expr_ty(expr) + && let ty::Adt(def, ..) = expr_ty.kind() + && !is_from_proc_macro(cx, expr) + { + let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{replacement}` is more clear than this expression"), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } @@ -124,38 +121,36 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find all binding statements like `let mut _ = T::default()` where `T::default()` is the // `default` method of the `Default` trait, and store statement index in current block being // checked and the name of the bound variable - let (local, variant, binding_name, binding_type, span) = if_chain! { + let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind // only take `let ...` statements - if let StmtKind::Local(local) = stmt.kind; - if let Some(expr) = local.init; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if !expr.span.from_expansion(); + && let Some(expr) = local.init + && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + && !expr.span.from_expansion() // only take bindings to identifiers - if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; + && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind // only when assigning `... = Default::default()` - if is_expr_default(expr, cx); - let binding_type = cx.typeck_results().node_type(binding_id); - if let Some(adt) = binding_type.ty_adt_def(); - if adt.is_struct(); - let variant = adt.non_enum_variant(); - if adt.did().is_local() || !variant.is_field_list_non_exhaustive(); - let module_did = cx.tcx.parent_module(stmt.hir_id); - if variant + && is_expr_default(expr, cx) + && let binding_type = cx.typeck_results().node_type(binding_id) + && let Some(adt) = binding_type.ty_adt_def() + && adt.is_struct() + && let variant = adt.non_enum_variant() + && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) + && let module_did = cx.tcx.parent_module(stmt.hir_id) + && variant .fields .iter() - .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); - let all_fields_are_copy = variant + .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)) + && let all_fields_are_copy = variant .fields .iter() .all(|field| { is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity()) - }); - if !has_drop(cx, binding_type) || all_fields_are_copy; - then { - (local, variant, ident.name, binding_type, expr.span) - } else { - continue; - } + }) + && (!has_drop(cx, binding_type) || all_fields_are_copy) + { + (local, variant, ident.name, binding_type, expr.span) + } else { + continue; }; let init_ctxt = local.span.ctxt(); @@ -216,21 +211,19 @@ impl<'tcx> LateLintPass<'tcx> for Default { .join(", "); // give correct suggestion if generics are involved (see #6944) - let binding_type = if_chain! { - if let ty::Adt(adt_def, args) = binding_type.kind(); - if !args.is_empty(); - then { - let adt_def_ty_name = cx.tcx.item_name(adt_def.did()); - let generic_args = args.iter().collect::<Vec<_>>(); - let tys_str = generic_args - .iter() - .map(ToString::to_string) - .collect::<Vec<_>>() - .join(", "); - format!("{adt_def_ty_name}::<{}>", &tys_str) - } else { - binding_type.to_string() - } + let binding_type = if let ty::Adt(adt_def, args) = binding_type.kind() + && !args.is_empty() + { + let adt_def_ty_name = cx.tcx.item_name(adt_def.did()); + let generic_args = args.iter().collect::<Vec<_>>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::<Vec<_>>() + .join(", "); + format!("{adt_def_ty_name}::<{}>", &tys_str) + } else { + binding_type.to_string() }; let sugg = if ext_with_default { @@ -260,48 +253,42 @@ impl<'tcx> LateLintPass<'tcx> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { - if_chain! { - if let ExprKind::Call(fn_expr, _) = &expr.kind; - if let ExprKind::Path(qpath) = &fn_expr.kind; - if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); - then { - // right hand side of assignment is `Default::default` - cx.tcx.is_diagnostic_item(sym::default_fn, def_id) - } else { - false - } + if let ExprKind::Call(fn_expr, _) = &expr.kind + && let ExprKind::Path(qpath) = &fn_expr.kind + && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id) + { + // right hand side of assignment is `Default::default` + cx.tcx.is_diagnostic_item(sym::default_fn, def_id) + } else { + false } } /// Returns the reassigned field and the assigning expression (right-hand side of assign). fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { - if_chain! { + if let StmtKind::Semi(later_expr) = this.kind // only take assignments - if let StmtKind::Semi(later_expr) = this.kind; - if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind; + && let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement - if let ExprKind::Field(binding, field_ident) = assign_lhs.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; - if let Some(second_binding_name) = path.segments.last(); - if second_binding_name.ident.name == binding_name; - then { - Some((field_ident, assign_rhs)) - } else { - None - } + && let ExprKind::Field(binding, field_ident) = assign_lhs.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind + && let Some(second_binding_name) = path.segments.last() + && second_binding_name.ident.name == binding_name + { + Some((field_ident, assign_rhs)) + } else { + None } } /// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }` fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let ExprKind::Struct(_, _, Some(base)) = parent.kind; - then { - base.hir_id == expr.hir_id - } else { - false - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Struct(_, _, Some(base)) = parent.kind + { + base.hir_id == expr.hir_id + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index bf070432ef9..b90d01b765a 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -56,32 +56,30 @@ fn is_alias(ty: hir::Ty<'_>) -> bool { impl LateLintPass<'_> for DefaultConstructedUnitStructs { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if_chain!( + if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind // make sure we have a call to `Default::default` - if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind; - if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind; + && let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind // make sure this isn't a type alias: // `<Foo as Bar>::Assoc` cannot be used as a constructor - if !is_alias(*base); - if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::default_fn, def_id); + && !is_alias(*base) + && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id) + && cx.tcx.is_diagnostic_item(sym::default_fn, def_id) // make sure we have a struct with no fields (unit struct) - if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind(); - if def.is_struct(); - if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant(); - if !var.is_field_list_non_exhaustive(); - if !expr.span.from_expansion() && !qpath.span().from_expansion(); - then { - span_lint_and_sugg( - cx, - DEFAULT_CONSTRUCTED_UNIT_STRUCTS, - expr.span.with_lo(qpath.qself_span().hi()), - "use of `default` to create a unit struct", - "remove this call to `default`", - String::new(), - Applicability::MachineApplicable, - ) - } - ); + && let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind() + && def.is_struct() + && let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant() + && !var.is_field_list_non_exhaustive() + && !expr.span.from_expansion() && !qpath.span().from_expansion() + { + span_lint_and_sugg( + cx, + DEFAULT_CONSTRUCTED_UNIT_STRUCTS, + expr.span.with_lo(qpath.qself_span().hi()), + "use of `default` to create a unit struct", + "remove this call to `default`", + String::new(), + Applicability::MachineApplicable, + ); + }; } } 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 b296ea20f9c..fb29703957d 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{get_parent_node, numeric_literal}; -use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; @@ -82,40 +81,40 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { /// Check whether a passed literal has potential to cause fallback or not. fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { - if_chain! { - if !in_external_macro(self.cx.sess(), lit.span); - if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); - if matches!(lit.node, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - then { - 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, - emit_hir_id, - lit.span, - "default numeric fallback might occur", - |diag| { - diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); - } - ); + if !in_external_macro(self.cx.sess(), lit.span) + && matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))) + && matches!( + lit.node, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) + ) + { + 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, + emit_hir_id, + lit.span, + "default numeric fallback might occur", + |diag| { + diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); + }, + ); } } } @@ -149,36 +148,33 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { ExprKind::Struct(_, fields, base) => { let ty = self.cx.typeck_results().expr_ty(expr); - if_chain! { - if let Some(adt_def) = ty.ty_adt_def(); - if adt_def.is_struct(); - if let Some(variant) = adt_def.variants().iter().next(); - then { - let fields_def = &variant.fields; - - // Push field type then visit each field expr. - for field in *fields { - let bound = - fields_def - .iter() - .find_map(|f_def| { - if f_def.ident(self.cx.tcx) == field.ident - { Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) } - else { None } - }); - self.ty_bounds.push(bound.into()); - self.visit_expr(field.expr); - self.ty_bounds.pop(); - } - - // Visit base with no bound. - if let Some(base) = base { - self.ty_bounds.push(ExplicitTyBound(false)); - self.visit_expr(base); - self.ty_bounds.pop(); - } - return; + if let Some(adt_def) = ty.ty_adt_def() + && adt_def.is_struct() + && let Some(variant) = adt_def.variants().iter().next() + { + let fields_def = &variant.fields; + + // Push field type then visit each field expr. + for field in *fields { + let bound = fields_def.iter().find_map(|f_def| { + if f_def.ident(self.cx.tcx) == field.ident { + Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) + } else { + None + } + }); + self.ty_bounds.push(bound.into()); + self.visit_expr(field.expr); + self.ty_bounds.pop(); + } + + // Visit base with no bound. + if let Some(base) = base { + self.ty_bounds.push(ExplicitTyBound(false)); + self.visit_expr(base); + self.ty_bounds.pop(); } + return; } }, diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 6c109a51f83..cbeb0050be0 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1,10 +1,11 @@ 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, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; +use core::mem; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; @@ -170,9 +171,7 @@ pub struct Dereferencing<'tcx> { #[derive(Debug)] struct StateData<'tcx> { - /// Span of the top level expression - span: Span, - hir_id: HirId, + first_expr: &'tcx Expr<'tcx>, adjusted_ty: Ty<'tcx>, } @@ -198,6 +197,7 @@ enum State { }, ExplicitDerefField { name: Symbol, + derefs_manually_drop: bool, }, Reborrow { mutability: Mutability, @@ -242,7 +242,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Stop processing sub expressions when a macro call is seen if expr.span.from_expansion() { if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data); + report(cx, expr, state, data, cx.typeck_results()); } return; } @@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { - report(cx, expr, state, data); + report(cx, expr, state, data, typeck); } return; }; @@ -272,14 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (Some(use_cx), RefOp::Deref) => { let sub_ty = typeck.expr_ty(sub_expr); if let ExprUseNode::FieldAccess(name) = use_cx.node - && adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()) + && !use_cx.moved_before_use && !ty_contains_field(sub_ty, name.name) { self.state = Some(( - State::ExplicitDerefField { name: name.name }, + State::ExplicitDerefField { + name: name.name, + derefs_manually_drop: is_manually_drop(sub_ty), + }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -293,8 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -313,8 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { mutbl, }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -342,8 +342,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) }); let can_auto_borrow = match use_cx.node { - ExprUseNode::Callee => true, - ExprUseNode::FieldAccess(_) => adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()), + ExprUseNode::FieldAccess(_) + if !use_cx.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) => + { + // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` + // field expressions when the base type is a union and the parent + // expression is also a field access. + // + // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a + // deref through `ManuallyDrop<_>` will not compile. + !adjust_derefs_manually_drop(use_cx.adjustments, expr_ty) + }, + ExprUseNode::Callee | ExprUseNode::FieldAccess(_) => true, ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => { // Check for calls to trait methods where the trait is implemented // on a reference. @@ -357,11 +367,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { .tcx .erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target)) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() - && let args = cx - .typeck_results() - .node_args_opt(hir_id) - .map(|args| &args[1..]) - .unwrap_or_default() + && let args = + typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default() && let impl_ty = if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0] .is_ref() @@ -436,14 +443,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { count: deref_count - required_refs, msg, stability, - for_field_access: match use_cx.node { - ExprUseNode::FieldAccess(name) => Some(name.name), - _ => None, + for_field_access: if let ExprUseNode::FieldAccess(name) = use_cx.node + && !use_cx.moved_before_use + { + Some(name.name) + } else { + None }, }), StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), }, )); @@ -455,8 +464,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.state = Some(( State::Borrow { mutability }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), }, )); @@ -501,13 +509,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { let adjusted_ty = data.adjusted_ty; let stability = state.stability; - report(cx, expr, State::DerefedBorrow(state), data); + report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { self.state = Some(( State::Borrow { mutability }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -517,15 +524,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let adjusted_ty = data.adjusted_ty; let stability = state.stability; let for_field_access = state.for_field_access; - report(cx, expr, State::DerefedBorrow(state), data); + report(cx, expr, State::DerefedBorrow(state), data, typeck); if let Some(name) = for_field_access - && !ty_contains_field(typeck.expr_ty(sub_expr), name) + && let sub_expr_ty = typeck.expr_ty(sub_expr) + && !ty_contains_field(sub_expr_ty, name) { self.state = Some(( - State::ExplicitDerefField { name }, + State::ExplicitDerefField { + name, + derefs_manually_drop: is_manually_drop(sub_expr_ty), + }, StateData { - span: expr.span, - hir_id: expr.hir_id, + first_expr: expr, adjusted_ty, }, )); @@ -535,8 +545,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { - span: parent.span, - hir_id: parent.hir_id, + first_expr: parent, adjusted_ty, }, )); @@ -566,13 +575,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => { self.state = state; }, - (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref) - if !ty_contains_field(typeck.expr_ty(sub_expr), name) => + ( + Some(( + State::ExplicitDerefField { + name, + derefs_manually_drop, + }, + data, + )), + RefOp::Deref, + ) if let sub_expr_ty = typeck.expr_ty(sub_expr) + && !ty_contains_field(sub_expr_ty, name) => { - self.state = Some((State::ExplicitDerefField { name }, data)); + self.state = Some(( + State::ExplicitDerefField { + name, + derefs_manually_drop: derefs_manually_drop || is_manually_drop(sub_expr_ty), + }, + data, + )); }, - (Some((state, data)), _) => report(cx, expr, state, data), + (Some((state, data)), _) => report(cx, expr, state, data, typeck), } } @@ -597,26 +621,24 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if_chain! { - if !pat.span.from_expansion(); - if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind(); + if !pat.span.from_expansion() + && let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind() // only lint immutable refs, because borrowed `&mut T` cannot be moved out - if let ty::Ref(_, _, Mutability::Not) = *tam.kind(); - then { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; - self.current_body = self.current_body.or(cx.enclosing_body); - self.ref_locals.insert( - id, - Some(RefPat { - always_deref: true, - spans: vec![pat.span], - app, - replacements: vec![(pat.span, snip.into())], - hir_id: pat.hir_id, - }), - ); - } + && let ty::Ref(_, _, Mutability::Not) = *tam.kind() + { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; + self.current_body = self.current_body.or(cx.enclosing_body); + self.ref_locals.insert( + id, + Some(RefPat { + always_deref: true, + spans: vec![pat.span], + app, + replacements: vec![(pat.span, snip.into())], + hir_id: pat.hir_id, + }), + ); } } } @@ -689,6 +711,14 @@ fn try_parse_ref_op<'tcx>( } } +// Checks if the adjustments contains a deref of `ManuallyDrop<_>` +fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { + adjustments.iter().any(|a| { + let ty = mem::replace(&mut ty, a.target); + matches!(a.kind, Adjust::Deref(Some(ref op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + }) +} + // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { @@ -741,7 +771,7 @@ impl TyCoercionStability { DefinedTy::Mir(ty) => Self::for_mir_ty( cx.tcx, ty.param_env, - cx.tcx.erase_late_bound_regions(ty.value), + cx.tcx.instantiate_bound_regions_with_erased(ty.value), for_return, ), } @@ -898,7 +928,13 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { } #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData<'tcx>) { +fn report<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + state: State, + data: StateData<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, +) { match state { State::DerefMethod { ty_changed_count, @@ -906,8 +942,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data mutbl, } => { let mut app = Applicability::MachineApplicable; - let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); - let ty = cx.typeck_results().expr_ty(expr); + 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 deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time @@ -947,7 +984,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data span_lint_and_sugg( cx, EXPLICIT_DEREF_METHODS, - data.span, + data.first_expr.span, match mutbl { Mutability::Not => "explicit `deref` method call", Mutability::Mut => "explicit `deref_mut` method call", @@ -959,26 +996,34 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }, State::DerefedBorrow(state) => { let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); - span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { - let (precedence, calls_field) = match get_parent_node(cx.tcx, data.hir_id) { - Some(Node::Expr(e)) => match e.kind { - ExprKind::Call(callee, _) if callee.hir_id != data.hir_id => (0, false), - ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), - _ => (e.precedence().order(), false), - }, - _ => (0, false), - }; - let sugg = if !snip_is_macro - && (calls_field || expr.precedence().order() < precedence) - && !has_enclosing_paren(&snip) - { - format!("({snip})") - } else { - snip.into() - }; - diag.span_suggestion(data.span, "change this to", sugg, app); - }); + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); + span_lint_hir_and_then( + cx, + NEEDLESS_BORROW, + data.first_expr.hir_id, + data.first_expr.span, + state.msg, + |diag| { + let (precedence, calls_field) = match get_parent_node(cx.tcx, data.first_expr.hir_id) { + Some(Node::Expr(e)) => match e.kind { + ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), + ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), + _ => (e.precedence().order(), false), + }, + _ => (0, false), + }; + let sugg = if !snip_is_macro + && (calls_field || expr.precedence().order() < precedence) + && !has_enclosing_paren(&snip) + { + format!("({snip})") + } else { + snip.into() + }; + diag.span_suggestion(data.first_expr.span, "change this to", sugg, app); + }, + ); }, State::ExplicitDeref { mutability } => { if matches!( @@ -996,7 +1041,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } let (prefix, precedence) = if let Some(mutability) = mutability - && !cx.typeck_results().expr_ty(expr).is_ref() + && !typeck.expr_ty(expr).is_ref() { let prefix = match mutability { Mutability::Not => "&", @@ -1009,53 +1054,61 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, - data.hir_id, - data.span, + data.first_expr.hir_id, + data.first_expr.span, "deref which would be done by auto-deref", |diag| { let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let (snip, snip_is_macro) = + snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let sugg = if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { format!("{prefix}({snip})") } else { format!("{prefix}{snip}") }; - diag.span_suggestion(data.span, "try", sugg, app); + diag.span_suggestion(data.first_expr.span, "try", sugg, app); }, ); }, - State::ExplicitDerefField { .. } => { - if matches!( - expr.kind, - ExprKind::Block(..) - | ExprKind::ConstBlock(_) - | ExprKind::If(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - ) && data.adjusted_ty.is_sized(cx.tcx, cx.param_env) - { - // Rustc bug: auto deref doesn't work on block expression when targeting sized types. - return; - } - - if let ExprKind::Field(parent_expr, _) = expr.kind - && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() - && adt.is_union() - { - // Auto deref does not apply on union field - return; - } + State::ExplicitDerefField { + derefs_manually_drop, .. + } => { + let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..)) + && (derefs_manually_drop + || adjust_derefs_manually_drop( + typeck.expr_adjustments(data.first_expr), + typeck.expr_ty(data.first_expr), + )) { + // `DerefMut` will not be automatically applied to `ManuallyDrop<_>` + // field expressions when the base type is a union and the parent + // expression is also a field access. + // + // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a + // deref through `ManuallyDrop<_>` will not compile. + let parent_id = cx.tcx.hir().parent_id(expr.hir_id); + if parent_id == data.first_expr.hir_id { + return; + } + (cx.tcx.hir().get(parent_id).expect_expr().span, true) + } else { + (expr.span, false) + }; span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, - data.hir_id, - data.span, + data.first_expr.hir_id, + data.first_expr.span, "deref which would be done by auto-deref", |diag| { let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; - diag.span_suggestion(data.span, "try", snip.into_owned(), app); + let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0; + let sugg = if needs_parens { + format!("({snip})") + } else { + snip.into_owned() + }; + diag.span_suggestion(data.first_expr.span, "try", sugg, app); }, ); }, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index a450becc647..9db56fa8ad0 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -148,83 +148,65 @@ fn check_struct<'tcx>( } fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { - if_chain! { - if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind; - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res; - if let variant_id = cx.tcx.parent(id); - if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id); - if variant_def.fields.is_empty(); - if !variant_def.is_field_list_non_exhaustive(); - - then { - let enum_span = cx.tcx.def_span(adt_def.did()); - let indent_enum = indent_of(cx, enum_span).unwrap_or(0); - let variant_span = cx.tcx.def_span(variant_def.def_id); - let indent_variant = indent_of(cx, variant_span).unwrap_or(0); - span_lint_and_then( - cx, - DERIVABLE_IMPLS, + if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let variant_id = cx.tcx.parent(id) + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) + && variant_def.fields.is_empty() + && !variant_def.is_field_list_non_exhaustive() + { + let enum_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, enum_span).unwrap_or(0); + let variant_span = cx.tcx.def_span(variant_def.def_id); + let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { + diag.span_suggestion_hidden( item.span, - "this `impl` can be derived", - |diag| { - diag.span_suggestion_hidden( - item.span, - "remove the manual implementation...", - String::new(), - Applicability::MachineApplicable - ); - diag.span_suggestion( - enum_span.shrink_to_lo(), - "...and instead derive it...", - format!( - "#[derive(Default)]\n{indent}", - indent = " ".repeat(indent_enum), - ), - Applicability::MachineApplicable - ); - diag.span_suggestion( - variant_span.shrink_to_lo(), - "...and mark the default variant", - format!( - "#[default]\n{indent}", - indent = " ".repeat(indent_variant), - ), - Applicability::MachineApplicable - ); - } + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable, ); - } + diag.span_suggestion( + enum_span.shrink_to_lo(), + "...and instead derive it...", + format!("#[derive(Default)]\n{indent}", indent = " ".repeat(indent_enum),), + Applicability::MachineApplicable, + ); + diag.span_suggestion( + variant_span.shrink_to_lo(), + "...and mark the default variant", + format!("#[default]\n{indent}", indent = " ".repeat(indent_variant),), + Applicability::MachineApplicable, + ); + }); } } impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - items: [child], - self_ty, - .. - }) = item.kind; - if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); - if !item.span.from_expansion(); - if let Some(def_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::Default, def_id); - if let impl_item_hir = child.id.hir_id(); - if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); - if let ImplItemKind::Fn(_, b) = &impl_item.kind; - if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); - if let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !attrs.iter().any(|attr| attr.doc_str().is_some()); - if cx.tcx.hir().attrs(impl_item_hir).is_empty(); - - then { - if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); - } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { - check_enum(cx, item, func_expr, adt_def); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + self_ty, + .. + }) = item.kind + && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !item.span.from_expansion() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Default, def_id) + && let impl_item_hir = child.id.hir_id() + && let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir) + && let ImplItemKind::Fn(_, b) = &impl_item.kind + && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b) + && let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + && let attrs = cx.tcx.hir().attrs(item.hir_id()) + && !attrs.iter().any(|attr| attr.doc_str().is_some()) + && cx.tcx.hir().attrs(impl_item_hir).is_empty() + { + if adt_def.is_struct() { + check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); + } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { + check_enum(cx, item, func_expr, adt_def); } } } diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 3a331564db9..169c2a15db7 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; @@ -232,42 +231,37 @@ fn check_hash_peq<'tcx>( ty: Ty<'tcx>, hash_is_automatically_derived: bool, ) { - if_chain! { - if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait(); - if let Some(def_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::Hash, def_id); - then { - // Look for the PartialEq implementations for `ty` - cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - - if !hash_is_automatically_derived || peq_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialEq<Foo> for Foo` - // For `impl PartialEq<B> for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( - cx, - DERIVED_HASH_WITH_MANUAL_EQ, - span, - "you are deriving `Hash` but have implemented `PartialEq` explicitly", - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); - diag.span_note( - cx.tcx.hir().span(hir_id), - "`PartialEq` implemented here" - ); - } + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Hash, def_id) + { + // Look for the PartialEq implementations for `ty` + cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { + let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + + if !hash_is_automatically_derived || peq_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialEq<Foo> for Foo` + // For `impl PartialEq<B> for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + span_lint_and_then( + cx, + DERIVED_HASH_WITH_MANUAL_EQ, + span, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here"); } - ); - } - }); - } + }, + ); + } + }); } } @@ -279,49 +273,38 @@ fn check_ord_partial_ord<'tcx>( ty: Ty<'tcx>, ord_is_automatically_derived: bool, ) { - if_chain! { - if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord); - if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); - if let Some(def_id) = &trait_ref.trait_def_id(); - if *def_id == ord_trait_def_id; - then { - // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - - if partial_ord_is_automatically_derived == ord_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialOrd<Foo> for Foo` - // For `impl PartialOrd<B> for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - let mess = if partial_ord_is_automatically_derived { - "you are implementing `Ord` explicitly but have derived `PartialOrd`" - } else { - "you are deriving `Ord` but have implemented `PartialOrd` explicitly" - }; - - span_lint_and_then( - cx, - DERIVE_ORD_XOR_PARTIAL_ORD, - span, - mess, - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); - diag.span_note( - cx.tcx.hir().span(hir_id), - "`PartialOrd` implemented here" - ); - } - } - ); - } - }); - } + if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() + && let Some(def_id) = &trait_ref.trait_def_id() + && *def_id == ord_trait_def_id + { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd<Foo> for Foo` + // For `impl PartialOrd<B> for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here"); + } + }); + } + }); } } @@ -394,27 +377,27 @@ fn check_unsafe_derive_deserialize<'tcx>( visitor.has_unsafe } - if_chain! { - if let Some(trait_def_id) = trait_ref.trait_def_id(); - if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE); - if let ty::Adt(def, _) = ty.kind(); - if let Some(local_def_id) = def.did().as_local(); - let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); - if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); - if cx.tcx.inherent_impls(def.did()) + if let Some(trait_def_id) = trait_ref.trait_def_id() + && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE) + && let ty::Adt(def, _) = ty.kind() + && let Some(local_def_id) = def.did().as_local() + && let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) + && cx + .tcx + .inherent_impls(def.did()) .iter() .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) - .any(|imp| has_unsafe(cx, imp)); - then { - span_lint_and_help( - cx, - UNSAFE_DERIVE_DESERIALIZE, - item.span, - "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - None, - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html" - ); - } + .any(|imp| has_unsafe(cx, imp)) + { + span_lint_and_help( + cx, + UNSAFE_DERIVE_DESERIALIZE, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + None, + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); } } @@ -431,12 +414,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { return; } - if_chain! { - if let Some(header) = kind.header(); - if header.unsafety == Unsafety::Unsafe; - then { - self.has_unsafe = true; - } + if let Some(header) = kind.header() + && header.unsafety == Unsafety::Unsafe + { + self.has_unsafe = true; } walk_fn(self, kind, decl, body_id, id); @@ -463,30 +444,28 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - if_chain! { - if let ty::Adt(adt, args) = ty.kind(); - if cx.tcx.visibility(adt.did()).is_public(); - if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); - if let Some(def_id) = trait_ref.trait_def_id(); - if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); + if let ty::Adt(adt, args) = ty.kind() + && cx.tcx.visibility(adt.did()).is_public() + && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]) // If all of our fields implement `Eq`, we can implement `Eq` too - if adt + && adt .all_fields() .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); - then { - span_lint_and_sugg( - cx, - DERIVE_PARTIAL_EQ_WITHOUT_EQ, - span.ctxt().outer_expn_data().call_site, - "you are deriving `PartialEq` and can implement `Eq`", - "consider deriving `Eq` as well", - "PartialEq, Eq".to_string(), - Applicability::MachineApplicable, - ) - } + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])) + { + span_lint_and_sugg( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + "consider deriving `Eq` as well", + "PartialEq, Eq".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs index 5e46b29b639..a1dd4805b9c 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs @@ -31,9 +31,9 @@ pub struct DisallowedNames { } impl DisallowedNames { - pub fn new(disallow: FxHashSet<String>) -> Self { + pub fn new(disallowed_names: &[String]) -> Self { Self { - disallow, + disallow: disallowed_names.iter().cloned().collect(), test_modules_deep: 0, } } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 8982fca6e89..ca277e7eded 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -4,13 +4,14 @@ use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; -use if_chain::if_chain; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind}; +use rustc_ast::token::CommentKind; +use rustc_ast::{AttrKind, AttrStyle}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; @@ -30,8 +31,8 @@ use rustc_resolve::rustdoc::{ use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; -use rustc_span::{sym, BytePos, FileName, Pos, Span}; use rustc_span::source_map::{FilePathMapping, SourceMap}; +use rustc_span::{sym, BytePos, FileName, Pos, Span}; use std::ops::Range; use std::{io, thread}; use url::Url; @@ -261,6 +262,53 @@ declare_clippy_lint! { "`pub fn` or `pub trait` with `# Safety` docs" } +declare_clippy_lint! { + /// ### What it does + /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!` + /// + /// ### Why is this bad? + /// Triple-slash comments (known as "outer doc comments") apply to items that follow it. + /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning. + /// + /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which + /// applies to the parent item (i.e. the item that the comment is contained in, + /// usually a module or crate). + /// + /// ### Known problems + /// Inner doc comments can only appear before items, so there are certain cases where the suggestion + /// made by this lint is not valid code. For example: + /// ```rs + /// fn foo() {} + /// ///! + /// fn bar() {} + /// ``` + /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment + /// is not valid at that position. + /// + /// ### Example + /// In this example, the doc comment is attached to the *function*, rather than the *module*. + /// ```no_run + /// pub mod util { + /// ///! This module contains utility functions. + /// + /// pub fn dummy() {} + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// pub mod util { + /// //! This module contains utility functions. + /// + /// pub fn dummy() {} + /// } + /// ``` + #[clippy::version = "1.70.0"] + pub SUSPICIOUS_DOC_COMMENTS, + suspicious, + "suspicious usage of (outer) doc comments" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -269,9 +317,9 @@ pub struct DocMarkdown { } impl DocMarkdown { - pub fn new(valid_idents: FxHashSet<String>) -> Self { + pub fn new(valid_idents: &[String]) -> Self { Self { - valid_idents, + valid_idents: valid_idents.iter().cloned().collect(), in_trait_impl: false, } } @@ -285,6 +333,7 @@ impl_lint_pass!(DocMarkdown => [ MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN, UNNECESSARY_SAFETY_DOC, + SUSPICIOUS_DOC_COMMENTS ]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { @@ -428,25 +477,21 @@ fn lint_for_missing_headers( span, "docs for function returning `Result` missing `# Errors` section", ); - } else { - if_chain! { - if let Some(body_id) = body_id; - if let Some(future) = cx.tcx.lang_items().future_trait(); - let typeck = cx.tcx.typeck_body(body_id); - let body = cx.tcx.hir().body(body_id); - let ret_ty = typeck.expr_ty(body.value); - if implements_trait(cx, ret_ty, future, &[]); - if let ty::Coroutine(_, subs, _) = ret_ty.kind(); - if is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result); - then { - span_lint( - cx, - MISSING_ERRORS_DOC, - span, - "docs for function returning `Result` missing `# Errors` section", - ); - } - } + } else if let Some(body_id) = body_id + && let Some(future) = cx.tcx.lang_items().future_trait() + && let typeck = cx.tcx.typeck_body(body_id) + && let body = cx.tcx.hir().body(body_id) + && let ret_ty = typeck.expr_ty(body.value) + && implements_trait(cx, ret_ty, future, &[]) + && let ty::Coroutine(_, subs, _) = ret_ty.kind() + && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) + { + span_lint( + cx, + MISSING_ERRORS_DOC, + span, + "docs for function returning `Result` missing `# Errors` section", + ); } } } @@ -483,6 +528,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ return None; } + check_almost_inner_doc(cx, attrs); + let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let mut doc = String::new(); for fragment in &fragments { @@ -511,6 +558,43 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ )) } +/// Looks for `///!` and `/**!` comments, which were probably meant to be `//!` and `/*!` +fn check_almost_inner_doc(cx: &LateContext<'_>, attrs: &[Attribute]) { + let replacements: Vec<_> = attrs + .iter() + .filter_map(|attr| { + if let AttrKind::DocComment(com_kind, sym) = attr.kind + && let AttrStyle::Outer = attr.style + && let Some(com) = sym.as_str().strip_prefix('!') + { + let sugg = match com_kind { + CommentKind::Line => format!("//!{com}"), + CommentKind::Block => format!("/*!{com}*/"), + }; + Some((attr.span, sugg)) + } else { + None + } + }) + .collect(); + + if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) { + span_lint_and_then( + cx, + SUSPICIOUS_DOC_COMMENTS, + lo_span.to(hi_span), + "this is an outer doc comment and does not apply to the parent module or crate", + |diag| { + diag.multipart_suggestion( + "use an inner doc comment to document the parent module or crate", + replacements, + Applicability::MaybeIncorrect, + ); + }, + ); + } +} + const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; #[allow(clippy::too_many_lines)] // Only a big match statement @@ -649,11 +733,12 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<u rustc_span::create_session_globals_then(edition, || { let filename = FileName::anon_source_code(&code); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); + #[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs index 5fcdca7cf36..17be95780cc 100644 --- a/src/tools/clippy/clippy_lints/src/empty_drop.rs +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -36,31 +35,30 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); impl LateLintPass<'_> for EmptyDrop { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - items: [child], - .. - }) = item.kind; - if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); - if let impl_item_hir = child.id.hir_id(); - if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); - if let ImplItemKind::Fn(_, b) = &impl_item.kind; - if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - let func_expr = peel_blocks(func_expr); - if let ExprKind::Block(block, _) = func_expr.kind; - if block.stmts.is_empty() && block.expr.is_none(); - then { - span_lint_and_sugg( - cx, - EMPTY_DROP, - item.span, - "empty drop implementation", - "try removing this impl", - String::new(), - Applicability::MaybeIncorrect - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind + && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait() + && let impl_item_hir = child.id.hir_id() + && let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir) + && let ImplItemKind::Fn(_, b) = &impl_item.kind + && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b) + && let func_expr = peel_blocks(func_expr) + && let ExprKind::Block(block, _) = func_expr.kind + && 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, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index affd0822120..6f5a0cb8801 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -114,27 +114,23 @@ impl LateLintPass<'_> for EndianBytes { return; } - if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind; - if args.is_empty(); - let ty = cx.typeck_results().expr_ty(receiver); - if ty.is_primitive_ty(); - if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty); - then { - return; - } + if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind + && args.is_empty() + && let ty = cx.typeck_results().expr_ty(receiver) + && ty.is_primitive_ty() + && maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty) + { + return; } - if_chain! { - if let ExprKind::Call(function, ..) = expr.kind; - if let ExprKind::Path(qpath) = function.kind; - if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id(); - if let Some(function_name) = cx.get_def_path(def_id).last(); - let ty = cx.typeck_results().expr_ty(expr); - if ty.is_primitive_ty(); - then { - maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); - } + if let ExprKind::Call(function, ..) = expr.kind + && let ExprKind::Path(qpath) = function.kind + && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() + && let Some(function_name) = cx.get_def_path(def_id).last() + && let ty = cx.typeck_results().expr_ty(expr) + && ty.is_primitive_ty() + { + maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); } } } diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 3d0ddca19c9..2f22f344a21 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -8,8 +8,8 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; -use rustc_span::Span; use rustc_span::symbol::kw; +use rustc_span::Span; use rustc_target::spec::abi::Abi; #[derive(Copy, Clone)] diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 02c53fafd69..e0df87e08da 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -247,8 +247,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<'tcx>, call_sig: /// 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(..)) + matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..)) } fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index f976cfd3f22..b7e62e082e4 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -71,40 +70,31 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; - if cx.effective_visibilities.is_exported(item.owner_id.def_id); - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); - then { - let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { - if v.fields().iter().any(|f| { - !cx.tcx.visibility(f.def_id).is_public() - }) { - // skip structs with private fields - return; - } - (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") - } else { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - }; - 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 sugg = format!("#[non_exhaustive]\n{indent}"); - diag.span_suggestion(suggestion_span, - "try adding #[non_exhaustive]", - sugg, - Applicability::MaybeIncorrect); - } + if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind + && cx.effective_visibilities.is_exported(item.owner_id.def_id) + && let attrs = cx.tcx.hir().attrs(item.hir_id()) + && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + { + let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { + if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) { + // skip structs with private fields + return; + } + (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") + } else { + (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") + }; + 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 sugg = format!("#[non_exhaustive]\n{indent}"); + diag.span_suggestion( + suggestion_span, + "try adding #[non_exhaustive]", + sugg, + Applicability::MaybeIncorrect, ); - - } + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index e14b1c556ec..07d025f68c3 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_entrypoint_fn; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,19 +41,17 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path_expr, _args) = e.kind; - if let ExprKind::Path(ref path) = path_expr.kind; - if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::process_exit, def_id); - let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id; - if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent); + if let ExprKind::Call(path_expr, _args) = e.kind + && let ExprKind::Path(ref path) = path_expr.kind + && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) + && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id + && let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent) // If the next item up is a function we check if it is an entry point // and only then emit a linter warning - if !is_entrypoint_fn(cx, parent.to_def_id()); - then { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } + && !is_entrypoint_fn(cx, parent.to_def_id()) + { + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } } } diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 4b5bcb06a1e..08cb2114a2b 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{find_format_args, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -101,30 +100,28 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { /// If `kind` is a block that looks like `{ let result = $expr; result }` then /// returns $expr. Otherwise returns `kind`. fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> { - if_chain! { - if let ExprKind::Block(block, _label @ None) = kind; - if let Block { + if let ExprKind::Block(block, _label @ None) = kind + && let Block { stmts: [Stmt { kind: StmtKind::Local(local), .. }], expr: Some(expr_end_of_block), rules: BlockCheckMode::DefaultBlock, .. - } = block; + } = block // Find id of the local that expr_end_of_block resolves to - if let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind; - if let Res::Local(expr_res) = expr_path.res; - if let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res); + && let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind + && let Res::Local(expr_res) = expr_path.res + && let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res) // Find id of the local we found in the block - if let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind; + && let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind // If those two are the same hir id - if res_pat.hir_id == local_hir_id; + && res_pat.hir_id == local_hir_id - if let Some(init) = local.init; - then { - return &init.kind; - } + && let Some(init) = local.init + { + return &init.kind; } kind } diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index efb69476b94..753f75d83a8 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::method_chain_args; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -53,13 +52,13 @@ declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]); impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { // check for `impl From<???> for ..` - if_chain! { - if let hir::ItemKind::Impl(impl_) = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id); - then { - lint_impl_body(cx, item.span, impl_.items); - } + if let hir::ItemKind::Impl(impl_) = &item.kind + && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && cx + .tcx + .is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id) + { + lint_impl_body(cx, item.span, impl_.items); } } } @@ -98,34 +97,33 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl } for impl_item in impl_items { - if_chain! { - if impl_item.ident.name == sym::from; - if let ImplItemKind::Fn(_, body_id) = - cx.tcx.hir().impl_item(impl_item.id).kind; - then { - // check the body for `begin_panic` or `unwrap` - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindPanicUnwrap { - lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id), - result: Vec::new(), - }; - fpu.visit_expr(body.value); + if impl_item.ident.name == sym::from + && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind + { + // check the body for `begin_panic` or `unwrap` + let body = cx.tcx.hir().body(body_id); + let mut fpu = FindPanicUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id), + result: Vec::new(), + }; + fpu.visit_expr(body.value); - // if we've found one, lint - if !fpu.result.is_empty() { - span_lint_and_then( - cx, - FALLIBLE_IMPL_FROM, - impl_span, - "consider implementing `TryFrom` instead", - move |diag| { - diag.help( - "`From` is intended for infallible conversions only. \ - Use `TryFrom` if there's a possibility for the conversion to fail"); - diag.span_note(fpu.result, "potential failure(s)"); - }); - } + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + FALLIBLE_IMPL_FROM, + impl_span, + "consider implementing `TryFrom` instead", + move |diag| { + diag.help( + "`From` is intended for infallible conversions only. \ + Use `TryFrom` if there's a possibility for the conversion to fail", + ); + diag.span_note(fpu.result, "potential failure(s)"); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 506a1191747..663c33e8cee 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal; -use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -64,73 +63,70 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if let ty::Float(fty) = *ty.kind(); - if let hir::ExprKind::Lit(lit) = expr.kind; - if let LitKind::Float(sym, lit_float_ty) = lit.node; - then { - let sym_str = sym.as_str(); - let formatter = FloatFormat::new(sym_str); - // Try to bail out if the float is for sure fine. - // If its within the 2 decimal digits of being out of precision we - // check if the parsed representation is the same as the string - // since we'll need the truncated string anyway. - let digits = count_digits(sym_str); - let max = max_digits(fty); - let type_suffix = match lit_float_ty { - LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"), - LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"), - LitFloatType::Unsuffixed => None - }; - let (is_whole, is_inf, mut float_str) = match fty { - FloatTy::F32 => { - let value = sym_str.parse::<f32>().unwrap(); + if let ty::Float(fty) = *ty.kind() + && let hir::ExprKind::Lit(lit) = expr.kind + && let LitKind::Float(sym, lit_float_ty) = lit.node + { + let sym_str = sym.as_str(); + let formatter = FloatFormat::new(sym_str); + // Try to bail out if the float is for sure fine. + // If its within the 2 decimal digits of being out of precision we + // check if the parsed representation is the same as the string + // since we'll need the truncated string anyway. + let digits = count_digits(sym_str); + let max = max_digits(fty); + let type_suffix = match lit_float_ty { + LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"), + LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"), + LitFloatType::Unsuffixed => None, + }; + let (is_whole, is_inf, mut float_str) = match fty { + FloatTy::F32 => { + let value = sym_str.parse::<f32>().unwrap(); - (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) - }, - FloatTy::F64 => { - let value = sym_str.parse::<f64>().unwrap(); + (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) + }, + FloatTy::F64 => { + let value = sym_str.parse::<f64>().unwrap(); + (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) + }, + }; - (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) - }, - }; - - if is_inf { - return; - } - - if is_whole && !sym_str.contains(|c| c == 'e' || c == '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"); - } + if is_inf { + return; + } - span_lint_and_sugg( - 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, - ); + if is_whole && !sym_str.contains(|c| c == 'e' || c == '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"); } - } else if digits > max as usize && float_str.len() < sym_str.len() { + span_lint_and_sugg( cx, - EXCESSIVE_PRECISION, + LOSSY_FLOAT_LITERAL, expr.span, - "float has excessive precision", - "consider changing the type or truncating it to", + "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, ); } + } else if digits > max as usize && float_str.len() < sym_str.len() { + span_lint_and_sugg( + 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, + ); } } } 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 09a9d9924de..d522873472b 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -133,30 +132,25 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su expr = inner_expr; } - if_chain! { + if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind() // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind(); - if let ExprKind::Lit(lit) = &expr.kind; - if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; - then { - let op = format!( - "{suggestion}{}{}", - // Check for float literals without numbers following the decimal - // separator such as `2.` and adds a trailing zero - if sym.as_str().ends_with('.') { - "0" - } else { - "" - }, - float_ty.name_str() - ).into(); - - suggestion = match suggestion { - Sugg::MaybeParen(_) => Sugg::MaybeParen(op), - _ => Sugg::NonParen(op) - }; - } + && let ExprKind::Lit(lit) = &expr.kind + && let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node + { + let op = format!( + "{suggestion}{}{}", + // Check for float literals without numbers following the decimal + // separator such as `2.` and adds a trailing zero + if sym.as_str().ends_with('.') { "0" } else { "" }, + float_ty.name_str() + ) + .into(); + + suggestion = match suggestion { + Sugg::MaybeParen(_) => Sugg::MaybeParen(op), + _ => Sugg::NonParen(op), + }; } suggestion.maybe_par() @@ -359,35 +353,59 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> { ) = receiver.kind { // check if expression of the form x * x + y * y - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind; - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind; - if eq_expr_value(cx, lmul_lhs, lmul_rhs); - if eq_expr_value(cx, rmul_lhs, rmul_rhs); - then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, ".."))); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + lmul_lhs, + lmul_rhs, + ) = add_lhs.kind + && let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + rmul_lhs, + rmul_rhs, + ) = add_rhs.kind + && eq_expr_value(cx, lmul_lhs, lmul_rhs) + && eq_expr_value(cx, rmul_lhs, rmul_rhs) + { + return Some(format!( + "{}.hypot({})", + Sugg::hir(cx, lmul_lhs, "..").maybe_par(), + Sugg::hir(cx, rmul_lhs, "..") + )); } // check if expression of the form x.powi(2) + y.powi(2) - if_chain! { - if let ExprKind::MethodCall( - PathSegment { ident: lmethod_name, .. }, - largs_0, [largs_1, ..], - _ - ) = &add_lhs.kind; - if let ExprKind::MethodCall( - PathSegment { ident: rmethod_name, .. }, - rargs_0, [rargs_1, ..], - _ - ) = &add_rhs.kind; - if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1); - if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1); - if Int(2) == lvalue && Int(2) == rvalue; - then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, ".."))); - } + if let ExprKind::MethodCall( + PathSegment { + ident: lmethod_name, .. + }, + largs_0, + [largs_1, ..], + _, + ) = &add_lhs.kind + && let ExprKind::MethodCall( + PathSegment { + ident: rmethod_name, .. + }, + rargs_0, + [rargs_1, ..], + _, + ) = &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) + && Int(2) == lvalue + && Int(2) == rvalue + { + return Some(format!( + "{}.hypot({})", + Sugg::hir(cx, largs_0, "..").maybe_par(), + Sugg::hir(cx, rargs_0, "..") + )); } } @@ -411,39 +429,44 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; - if cx.typeck_results().expr_ty(lhs).is_floating_point(); - if let Some(value) = constant(cx, cx.typeck_results(), rhs); - if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind; - if cx.typeck_results().expr_ty(self_arg).is_floating_point(); - if path.ident.name.as_str() == "exp"; - then { - span_lint_and_sugg( - cx, - IMPRECISE_FLOPS, - expr.span, - "(e.pow(x) - 1) can be computed more accurately", - "consider using", - format!( - "{}.exp_m1()", - Sugg::hir(cx, self_arg, "..").maybe_par() - ), - Applicability::MachineApplicable, - ); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + && cx.typeck_results().expr_ty(lhs).is_floating_point() + && let Some(value) = constant(cx, cx.typeck_results(), 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() + && path.ident.name.as_str() == "exp" + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "(e.pow(x) - 1) can be computed more accurately", + "consider using", + format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()), + Applicability::MachineApplicable, + ); } } fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind; - if cx.typeck_results().expr_ty(lhs).is_floating_point(); - if cx.typeck_results().expr_ty(rhs).is_floating_point(); - then { - return Some((lhs, rhs)); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + lhs, + rhs, + ) = &expr.kind + && cx.typeck_results().expr_ty(lhs).is_floating_point() + && cx.typeck_results().expr_ty(rhs).is_floating_point() + { + return Some((lhs, rhs)); } None @@ -553,60 +576,72 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a } fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr); - let if_body_expr = peel_blocks(then); - let else_body_expr = peel_blocks(r#else); - if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr); - then { - let positive_abs_sugg = ( - "manual implementation of `abs` method", - format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), - ); - let negative_abs_sugg = ( - "manual implementation of negation of `abs` method", - format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), - ); - let sugg = if is_testing_positive(cx, cond, body) { - if if_expr_positive { - positive_abs_sugg - } else { - negative_abs_sugg - } - } else if is_testing_negative(cx, cond, body) { - if if_expr_positive { - negative_abs_sugg - } else { - positive_abs_sugg - } + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) + && let if_body_expr = peel_blocks(then) + && let else_body_expr = peel_blocks(r#else) + && let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr) + { + let positive_abs_sugg = ( + "manual implementation of `abs` method", + format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + ); + let negative_abs_sugg = ( + "manual implementation of negation of `abs` method", + format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + ); + let sugg = if is_testing_positive(cx, cond, body) { + if if_expr_positive { + positive_abs_sugg } else { - return; - }; - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - sugg.0, - "try", - sugg.1, - Applicability::MachineApplicable, - ); - } + negative_abs_sugg + } + } else if is_testing_negative(cx, cond, body) { + if if_expr_positive { + negative_abs_sugg + } else { + positive_abs_sugg + } + } else { + return; + }; + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + sugg.0, + "try", + sugg.1, + Applicability::MachineApplicable, + ); } } fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind; - if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind; - then { - return method_name_a.as_str() == method_name_b.as_str() && - args_a.len() == args_b.len() && - ( - ["ln", "log2", "log10"].contains(&method_name_a.as_str()) || - method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]) - ); - } + if let ExprKind::MethodCall( + PathSegment { + ident: method_name_a, .. + }, + _, + args_a, + _, + ) = expr_a.kind + && let ExprKind::MethodCall( + PathSegment { + ident: method_name_b, .. + }, + _, + args_b, + _, + ) = expr_b.kind + { + return method_name_a.as_str() == method_name_b.as_str() + && args_a.len() == args_b.len() + && (["ln", "log2", "log10"].contains(&method_name_a.as_str()) + || method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); } false @@ -614,103 +649,98 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_> fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { // check if expression of the form x.logN() / y.logN() - if_chain! { - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Div, .. - }, - lhs, - rhs, - ) = &expr.kind; - if are_same_base_logs(cx, lhs, rhs); - if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind; - if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind; - then { - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "log base can be expressed more clearly", - "consider using", - format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),), - Applicability::MachineApplicable, - ); - } + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + lhs, + rhs, + ) = &expr.kind + && are_same_base_logs(cx, lhs, rhs) + && let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind + && let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind + { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "log base can be expressed more clearly", + "consider using", + format!( + "{}.log({})", + Sugg::hir(cx, largs_self, "..").maybe_par(), + Sugg::hir(cx, rargs_self, ".."), + ), + Applicability::MachineApplicable, + ); } } fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Div, .. - }, - div_lhs, - div_rhs, - ) = &expr.kind; - if let ExprKind::Binary( + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + div_lhs, + div_rhs, + ) = &expr.kind + && let ExprKind::Binary( Spanned { node: BinOpKind::Mul, .. }, mul_lhs, mul_rhs, - ) = &div_lhs.kind; - if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs); - if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs); - then { - // TODO: also check for constant values near PI/180 or 180/PI - if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && - (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + ) = &div_lhs.kind + && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) + && let Some(lvalue) = constant(cx, cx.typeck_results(), 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) + && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + { + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + if let ExprKind::Lit(literal) = mul_lhs.kind + && let ast::LitKind::Float(ref value, float_type) = literal.node + && float_type == ast::LitFloatType::Unsuffixed { - let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); - if_chain! { - if let ExprKind::Lit(literal) = mul_lhs.kind; - if let ast::LitKind::Float(ref value, float_type) = literal.node; - if float_type == ast::LitFloatType::Unsuffixed; - then { - if value.as_str().ends_with('.') { - proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); - } else { - proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); - } - } + if value.as_str().ends_with('.') { + proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); + } else { + proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); } - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "conversion to degrees can be done more accurately", - "consider using", - proposal, - Applicability::MachineApplicable, - ); - } else if - (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && - (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) + } + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "conversion to degrees can be done more accurately", + "consider using", + proposal, + Applicability::MachineApplicable, + ); + } else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue) + && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) + { + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + if let ExprKind::Lit(literal) = mul_lhs.kind + && let ast::LitKind::Float(ref value, float_type) = literal.node + && float_type == ast::LitFloatType::Unsuffixed { - let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); - if_chain! { - if let ExprKind::Lit(literal) = mul_lhs.kind; - if let ast::LitKind::Float(ref value, float_type) = literal.node; - if float_type == ast::LitFloatType::Unsuffixed; - then { - if value.as_str().ends_with('.') { - proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); - } else { - proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); - } - } + if value.as_str().ends_with('.') { + proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + } else { + proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); } - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "conversion to radians can be done more accurately", - "consider using", - proposal, - Applicability::MachineApplicable, - ); } + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "conversion to radians can be done more accurately", + "consider using", + proposal, + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 3c1f2d9d5dc..c9868255dcf 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -8,7 +8,6 @@ use clippy_utils::macros::{ }; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use if_chain::if_chain; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -404,49 +403,43 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb } fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { - if_chain! { - if !value.span.from_expansion(); - if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); - if is_diag_trait_item(cx, method_def_id, sym::ToString); - let receiver_ty = cx.typeck_results().expr_ty(receiver); - if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); - let (n_needed_derefs, target) = - count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()); - if implements_trait(cx, target, display_trait_id, &[]); - if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait(); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); - if n_needed_derefs == 0 && !needs_ref { - span_lint_and_sugg( - cx, - TO_STRING_IN_FORMAT_ARGS, - to_string_span.with_lo(receiver.span.hi()), - &format!( - "`to_string` applied to a type that implements `Display` in `{name}!` args" - ), - "remove this", - String::new(), - Applicability::MachineApplicable, - ); - } else { - span_lint_and_sugg( - cx, - TO_STRING_IN_FORMAT_ARGS, - value.span, - &format!( - "`to_string` applied to a type that implements `Display` in `{name}!` args" - ), - "use this", - format!( - "{}{:*>n_needed_derefs$}{receiver_snippet}", - if needs_ref { "&" } else { "" }, - "" - ), - Applicability::MachineApplicable, - ); - } + if !value.span.from_expansion() + && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) + && is_diag_trait_item(cx, method_def_id, sym::ToString) + && let receiver_ty = cx.typeck_results().expr_ty(receiver) + && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display) + && let (n_needed_derefs, target) = + count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) + && implements_trait(cx, target, display_trait_id, &[]) + && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); + if n_needed_derefs == 0 && !needs_ref { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + to_string_span.with_lo(receiver.span.hi()), + &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span, + &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), + "use this", + format!( + "{}{:*>n_needed_derefs$}{receiver_snippet}", + if needs_ref { "&" } else { "" }, + "" + ), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 08ee7032c09..ec87629a344 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; -use if_chain::if_chain; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -141,27 +140,25 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { } fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { + if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind // Get the hir_id of the object we are calling the method on - if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind; // Is the method to_string() ? - if path.ident.name == sym::to_string; + && path.ident.name == sym::to_string // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, expr_def_id, sym::ToString); + && let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && is_diag_trait_item(cx, expr_def_id, sym::ToString) // Is the method is called on self - if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind; - if let [segment] = path.segments; - if segment.ident.name == kw::SelfLower; - then { - span_lint( - cx, - RECURSIVE_FORMAT_IMPL, - expr.span, - "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion", - ); - } + && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind + && let [segment] = path.segments + && segment.ident.name == kw::SelfLower + { + span_lint( + cx, + RECURSIVE_FORMAT_IMPL, + expr.span, + "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion", + ); } } @@ -215,55 +212,53 @@ fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_ } fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) { - if_chain! { - if let Some(macro_call) = root_macro_call_first_node(cx, expr); - if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id); - then { - let replacement = match name { - sym::print_macro | sym::eprint_macro => "write", - sym::println_macro | sym::eprintln_macro => "writeln", - _ => return, - }; + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + let replacement = match name { + sym::print_macro | sym::eprint_macro => "write", + sym::println_macro | sym::eprintln_macro => "writeln", + _ => return, + }; - let name = name.as_str().strip_suffix("_macro").unwrap(); + let name = name.as_str().strip_suffix("_macro").unwrap(); - span_lint_and_sugg( - cx, - PRINT_IN_FORMAT_IMPL, - macro_call.span, - &format!("use of `{name}!` in `{}` impl", impl_trait.name), - "replace with", - if let Some(formatter_name) = impl_trait.formatter_name { - format!("{replacement}!({formatter_name}, ..)") - } else { - format!("{replacement}!(..)") - }, - Applicability::HasPlaceholders, - ); - } + span_lint_and_sugg( + cx, + PRINT_IN_FORMAT_IMPL, + macro_call.span, + &format!("use of `{name}!` in `{}` impl", impl_trait.name), + "replace with", + if let Some(formatter_name) = impl_trait.formatter_name { + format!("{replacement}!({formatter_name}, ..)") + } else { + format!("{replacement}!(..)") + }, + Applicability::HasPlaceholders, + ); } } fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> { - if_chain! { - if impl_item.ident.name == sym::fmt; - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - if let Some(Impl { of_trait: Some(trait_ref),..}) = get_parent_as_impl(cx.tcx, impl_item.hir_id()); - if let Some(did) = trait_ref.trait_def_id(); - if let Some(name) = cx.tcx.get_diagnostic_name(did); - if matches!(name, sym::Debug | sym::Display); - then { - let body = cx.tcx.hir().body(body_id); - let formatter_name = body.params.get(1) - .and_then(|param| param.pat.simple_ident()) - .map(|ident| ident.name); + if impl_item.ident.name == sym::fmt + && let ImplItemKind::Fn(_, body_id) = impl_item.kind + && let Some(Impl { + of_trait: Some(trait_ref), + .. + }) = get_parent_as_impl(cx.tcx, impl_item.hir_id()) + && let Some(did) = trait_ref.trait_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(did) + && matches!(name, sym::Debug | sym::Display) + { + let body = cx.tcx.hir().body(body_id); + let formatter_name = body + .params + .get(1) + .and_then(|param| param.pat.simple_ident()) + .map(|ident| ident.name); - Some(FormatTraitNames { - name, - formatter_name, - }) - } else { - None - } + Some(FormatTraitNames { name, formatter_name }) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index 2c9c43d3ea7..70892ce608f 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::is_span_if; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -168,93 +167,84 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { /// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint. fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { - if_chain! { - if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind; - if !lhs.span.from_expansion() && !rhs.span.from_expansion(); + if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind + && !lhs.span.from_expansion() && !rhs.span.from_expansion() // span between BinOp LHS and RHS - let binop_span = lhs.span.between(rhs.span); + && let binop_span = lhs.span.between(rhs.span) // if RHS is an UnOp - if let ExprKind::Unary(op, ref un_rhs) = rhs.kind; + && let ExprKind::Unary(op, ref un_rhs) = rhs.kind // from UnOp operator to UnOp operand - let unop_operand_span = rhs.span.until(un_rhs.span); - if let Some(binop_snippet) = snippet_opt(cx, binop_span); - if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span); - let binop_str = BinOpKind::to_string(&binop.node); + && let unop_operand_span = rhs.span.until(un_rhs.span) + && let Some(binop_snippet) = snippet_opt(cx, binop_span) + && let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span) + && let binop_str = BinOpKind::to_string(&binop.node) // no space after BinOp operator and space after UnOp operator - if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' '); - then { - let unop_str = UnOp::to_string(op); - let eqop_span = lhs.span.between(un_rhs.span); - span_lint_and_help( - cx, - SUSPICIOUS_UNARY_OP_FORMATTING, - eqop_span, - &format!( - "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ - `{binop_str}{unop_str}` is a single operator" - ), - None, - &format!( - "put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`" - ), - ); - } + && binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ') + { + let unop_str = UnOp::to_string(op); + let eqop_span = lhs.span.between(un_rhs.span); + span_lint_and_help( + cx, + SUSPICIOUS_UNARY_OP_FORMATTING, + eqop_span, + &format!( + "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ + `{binop_str}{unop_str}` is a single operator" + ), + None, + &format!("put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"), + ); } } /// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`. fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { - if_chain! { - if let ExprKind::If(_, then, Some(else_)) = &expr.kind; - if is_block(else_) || is_if(else_); - if !then.span.from_expansion() && !else_.span.from_expansion(); - if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::If(_, then, Some(else_)) = &expr.kind + && (is_block(else_) || is_if(else_)) + && !then.span.from_expansion() && !else_.span.from_expansion() + && !in_external_macro(cx.sess(), expr.span) // workaround for rust-lang/rust#43081 - if expr.span.lo().0 != 0 && expr.span.hi().0 != 0; + && expr.span.lo().0 != 0 && expr.span.hi().0 != 0 // this will be a span from the closing ‘}’ of the “then” block (excluding) to // the “if” of the “else if” block (excluding) - let else_span = then.span.between(else_.span); + && let else_span = then.span.between(else_.span) // the snippet should look like " else \n " with maybe comments anywhere // it’s bad when there is a ‘\n’ after the “else” - if let Some(else_snippet) = snippet_opt(cx, else_span); - if let Some((pre_else, post_else)) = else_snippet.split_once("else"); - if let Some((_, post_else_post_eol)) = post_else.split_once('\n'); - - then { - // Allow allman style braces `} \n else \n {` - if_chain! { - if is_block(else_); - if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n'); - // Exactly one eol before and after the else - if !pre_else_post_eol.contains('\n'); - if !post_else_post_eol.contains('\n'); - then { - return; - } - } - - // Don't warn if the only thing inside post_else_post_eol is a comment block. - let trimmed_post_else_post_eol = post_else_post_eol.trim(); - if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") { - return - } + && let Some(else_snippet) = snippet_opt(cx, else_span) + && let Some((pre_else, post_else)) = else_snippet.split_once("else") + && let Some((_, post_else_post_eol)) = post_else.split_once('\n') + { + // Allow allman style braces `} \n else \n {` + if is_block(else_) + && let Some((_, pre_else_post_eol)) = pre_else.split_once('\n') + // Exactly one eol before and after the else + && !pre_else_post_eol.contains('\n') + && !post_else_post_eol.contains('\n') + { + return; + } - let else_desc = if is_if(else_) { "if" } else { "{..}" }; - span_lint_and_note( - cx, - SUSPICIOUS_ELSE_FORMATTING, - else_span, - &format!("this is an `else {else_desc}` but the formatting might hide it"), - None, - &format!( - "to remove this lint, remove the `else` or remove the new line between \ - `else` and `{else_desc}`", - ), - ); + // Don't warn if the only thing inside post_else_post_eol is a comment block. + let trimmed_post_else_post_eol = post_else_post_eol.trim(); + if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") { + return; } + + let else_desc = if is_if(else_) { "if" } else { "{..}" }; + span_lint_and_note( + cx, + SUSPICIOUS_ELSE_FORMATTING, + else_span, + &format!("this is an `else {else_desc}` but the formatting might hide it"), + None, + &format!( + "to remove this lint, remove the `else` or remove the new line between \ + `else` and `{else_desc}`", + ), + ); } } @@ -272,61 +262,56 @@ fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize { fn check_array(cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::Array(ref array) = expr.kind { for element in array { - if_chain! { - if let ExprKind::Binary(ref op, ref lhs, _) = element.kind; - if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span); - let space_span = lhs.span.between(op.span); - if let Some(space_snippet) = snippet_opt(cx, space_span); - let lint_span = lhs.span.with_lo(lhs.span.hi()); - if space_snippet.contains('\n'); - if indentation(cx, op.span) <= indentation(cx, lhs.span); - then { - span_lint_and_note( - cx, - POSSIBLE_MISSING_COMMA, - lint_span, - "possibly missing a comma here", - None, - "to remove this lint, add a comma or write the expr in a single line", - ); - } + if let ExprKind::Binary(ref op, ref lhs, _) = element.kind + && has_unary_equivalent(op.node) + && lhs.span.eq_ctxt(op.span) + && let space_span = lhs.span.between(op.span) + && let Some(space_snippet) = snippet_opt(cx, space_span) + && let lint_span = lhs.span.with_lo(lhs.span.hi()) + && space_snippet.contains('\n') + && indentation(cx, op.span) <= indentation(cx, lhs.span) + { + span_lint_and_note( + cx, + POSSIBLE_MISSING_COMMA, + lint_span, + "possibly missing a comma here", + None, + "to remove this lint, add a comma or write the expr in a single line", + ); } } } } fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { - if_chain! { - if !first.span.from_expansion() && !second.span.from_expansion(); - if matches!(first.kind, ExprKind::If(..)); - if is_block(second) || is_if(second); + if !first.span.from_expansion() && !second.span.from_expansion() + && matches!(first.kind, ExprKind::If(..)) + && (is_block(second) || is_if(second)) // Proc-macros can give weird spans. Make sure this is actually an `if`. - if is_span_if(cx, first.span); + && is_span_if(cx, first.span) // If there is a line break between the two expressions, don't lint. // If there is a non-whitespace character, this span came from a proc-macro. - let else_span = first.span.between(second.span); - if let Some(else_snippet) = snippet_opt(cx, else_span); - if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()); - then { - let (looks_like, next_thing) = if is_if(second) { - ("an `else if`", "the second `if`") - } else { - ("an `else {..}`", "the next block") - }; + && let else_span = first.span.between(second.span) + && let Some(else_snippet) = snippet_opt(cx, else_span) + && !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()) + { + let (looks_like, next_thing) = if is_if(second) { + ("an `else if`", "the second `if`") + } else { + ("an `else {..}`", "the next block") + }; - span_lint_and_note( - cx, - SUSPICIOUS_ELSE_FORMATTING, - else_span, - &format!("this looks like {looks_like} but the `else` is missing"), - None, - &format!( - "to remove this lint, add the missing `else` or add a new line before {next_thing}", - ), - ); - } + span_lint_and_note( + cx, + SUSPICIOUS_ELSE_FORMATTING, + else_span, + &format!("this looks like {looks_like} but the `else` is missing"), + None, + &format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",), + ); } } 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 74a60b6a0d2..18d11ccc0b5 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 @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -46,52 +45,41 @@ declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { - if_chain! { - if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind; - if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind; + if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind + && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind // check if the first part of the path is some integer primitive - if let TyKind::Path(ty_qpath) = &ty.kind; - let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); - if let def::Res::PrimTy(prim_ty) = ty_res; - if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)); + && let TyKind::Path(ty_qpath) = &ty.kind + && let ty_res = cx.qpath_res(ty_qpath, ty.hir_id) + && let def::Res::PrimTy(prim_ty) = ty_res + && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) // check if the second part of the path indeed calls the associated // function `from_str_radix` - if pathseg.ident.name.as_str() == "from_str_radix"; + && pathseg.ident.name.as_str() == "from_str_radix" // check if the second argument is a primitive `10` - if is_integer_literal(radix, 10); + && is_integer_literal(radix, 10) + { + let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { + let ty = cx.typeck_results().expr_ty(expr); + if is_ty_stringish(cx, ty) { expr } else { &src } + } else { + &src + }; - then { - let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { - let ty = cx.typeck_results().expr_ty(expr); - if is_ty_stringish(cx, ty) { - expr - } else { - &src - } - } else { - &src - }; + let sugg = + Sugg::hir_with_applicability(cx, expr, "<string>", &mut Applicability::MachineApplicable).maybe_par(); - let sugg = Sugg::hir_with_applicability( - cx, - expr, - "<string>", - &mut Applicability::MachineApplicable - ).maybe_par(); - - span_lint_and_sugg( - cx, - FROM_STR_RADIX_10, - exp.span, - "this call to `from_str_radix` can be replaced with a call to `str::parse`", - "try", - format!("{sugg}.parse::<{}>()", prim_ty.name_str()), - Applicability::MaybeIncorrect - ); - } + span_lint_and_sugg( + cx, + FROM_STR_RADIX_10, + exp.span, + "this call to `from_str_radix` can be replaced with a call to `str::parse`", + "try", + format!("{sugg}.parse::<{}>()", prim_ty.name_str()), + Applicability::MaybeIncorrect, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index ee66c841ed2..8fba41c0e24 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -5,18 +5,10 @@ use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind}; use rustc_lint::LateContext; -use rustc_span::symbol::Ident; -use rustc_span::{BytePos, Span}; use super::IMPL_TRAIT_IN_PARAMS; -fn report( - cx: &LateContext<'_>, - param: &GenericParam<'_>, - ident: &Ident, - generics: &Generics<'_>, - first_param_span: Span, -) { +fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_>) { // No generics with nested generics, and no generics like FnMut(x) span_lint_and_then( cx, @@ -35,12 +27,7 @@ fn report( ); } else { diag.span_suggestion_with_style( - Span::new( - first_param_span.lo() - rustc_span::BytePos(1), - ident.span.hi(), - ident.span.ctxt(), - ident.span.parent(), - ), + generics.span, "add a type parameter", format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), rustc_errors::Applicability::HasPlaceholders, @@ -52,54 +39,47 @@ fn report( } pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) { - if_chain! { - if let FnKind::ItemFn(ident, generics, _) = kind; - if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); - if !is_in_test_function(cx.tcx, hir_id); - then { - for param in generics.params { - if param.is_impl_trait() { - report(cx, param, ident, generics, body.params[0].span); - }; - } + if let FnKind::ItemFn(_, generics, _) = kind + && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() + && !is_in_test_function(cx.tcx, hir_id) + { + for param in generics.params { + if param.is_impl_trait() { + report(cx, param, generics); + }; } } } pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { - if_chain! { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()); - if let hir::ItemKind::Impl(impl_) = item.kind; - if let hir::Impl { of_trait, .. } = *impl_; - if of_trait.is_none(); - let body = cx.tcx.hir().body(body_id); - if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); - if !is_in_test_function(cx.tcx, impl_item.hir_id()); - then { - for param in impl_item.generics.params { - if param.is_impl_trait() { - report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span); - } + if let ImplItemKind::Fn(_, body_id) = impl_item.kind + && let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()) + && let hir::ItemKind::Impl(impl_) = item.kind + && let hir::Impl { of_trait, .. } = *impl_ + && of_trait.is_none() + && let body = cx.tcx.hir().body(body_id) + && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() + && !is_in_test_function(cx.tcx, impl_item.hir_id()) + { + for param in impl_item.generics.params { + if param.is_impl_trait() { + report(cx, param, impl_item.generics); } } } } pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { - if_chain! { - if !avoid_breaking_exported_api; - if let TraitItemKind::Fn(_, _) = trait_item.kind; - if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()); + if !avoid_breaking_exported_api + && let TraitItemKind::Fn(_, _) = trait_item.kind + && let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()) // ^^ (Will always be a trait) - if !item.vis_span.is_empty(); // Is public - if !is_in_test_function(cx.tcx, trait_item.hir_id()); - then { - for param in trait_item.generics.params { - if param.is_impl_trait() { - let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1)); - report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi()); - } + && !item.vis_span.is_empty() // Is public + && !is_in_test_function(cx.tcx, trait_item.hir_id()) + { + for param in trait_item.generics.params { + if param.is_impl_trait() { + report(cx, param, trait_item.generics); } } } diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index 18f7368dafb..bf96c0d62b0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -43,15 +43,13 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: // Body must be &(mut) <self_data>.name // self_data is not necessarily self, to also lint sub-getters, etc… - let block_expr = if_chain! { - if let ExprKind::Block(block,_) = body.value.kind; - if block.stmts.is_empty(); - if let Some(block_expr) = block.expr; - then { - block_expr - } else { - return; - } + let block_expr = if let ExprKind::Block(block, _) = body.value.kind + && block.stmts.is_empty() + && let Some(block_expr) = block.expr + { + block_expr + } else { + return; }; let expr_span = block_expr.span; @@ -61,14 +59,12 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: } else { block_expr }; - let (self_data, used_ident) = if_chain! { - if let ExprKind::Field(self_data, ident) = expr.kind; - if ident.name.as_str() != name; - then { - (self_data, ident) - } else { - return; - } + let (self_data, used_ident) = if let ExprKind::Field(self_data, ident) = expr.kind + && ident.name.as_str() != name + { + (self_data, ident) + } else { + return; }; let mut used_field = None; diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index 485235514de..5e90fcd72eb 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -23,7 +23,7 @@ fn result_err_ty<'tcx>( && let hir::FnRetTy::Return(hir_ty) = decl.output && let ty = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output()) + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().output()) && is_type_diagnostic_item(cx, ty, sym::Result) && let ty::Adt(_, args) = ty.kind() { @@ -86,59 +86,60 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - if_chain! { - if let Adt(adt, subst) = err_ty.kind(); - if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); - if let Some(hir::Node::Item(item)) = cx - .tcx - .hir() - .find_by_def_id(local_def_id); - if let hir::ItemKind::Enum(ref def, _) = item.kind; - then { - let variants_size = AdtVariantInfo::new(cx, *adt, subst); - if let Some((first_variant, variants)) = variants_size.split_first() - && first_variant.size >= large_err_threshold - { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag| { - diag.span_label( - def.variants[first_variant.ind].span, - format!("the largest variant contains at least {} bytes", variants_size[0].size), - ); + if let Adt(adt, subst) = err_ty.kind() + && let Some(local_def_id) = err_ty + .ty_adt_def() + .expect("already checked this is adt") + .did() + .as_local() + && let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(local_def_id) + && let hir::ItemKind::Enum(ref def, _) = item.kind + { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if let Some((first_variant, variants)) = variants_size.split_first() + && first_variant.size >= large_err_threshold + { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[first_variant.ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); - for variant in variants { - if variant.size >= large_err_threshold { - let variant_def = &def.variants[variant.ind]; - diag.span_label( - variant_def.span, - format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), - ); - } + for variant in variants { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!( + "the variant `{}` contains at least {} bytes", + variant_def.ident, variant.size + ), + ); } - - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); } - ); - } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); } - else { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); - } + } else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); } } } 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 e614a8f694f..644b9cdaeb2 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,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, SpanlessEq}; -use if_chain::if_chain; use rustc_errors::Diagnostic; use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; @@ -127,15 +126,13 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { } fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; - if path.ident.as_str() == "lock"; - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Mutex); - then { - Some(self_arg) - } else { - None - } + 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) + { + Some(self_arg) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index eaf80de3857..6594636d884 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -10,10 +10,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; use rustc_span::symbol::sym; - -use if_chain::if_chain; +use rustc_span::Span; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; @@ -337,42 +335,38 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't } fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(fun, args) = e.kind; - if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; - if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; - if let Some(ty_did) = ty_path.res.opt_def_id(); - then { - if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) { - return; - } + if let ExprKind::Call(fun, args) = e.kind + && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind + && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind + && let Some(ty_did) = ty_path.res.opt_def_id() + { + if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) { + return; + } - if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashMap::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } - } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashSet::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } + if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { + if method.ident.name == sym::new { + self.suggestions.insert(e.span, "HashMap::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashMap::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { + if method.ident.name == sym::new { + self.suggestions.insert(e.span, "HashSet::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashSet::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); } } } 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 24f62490f96..f2fac9a29cb 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -2,7 +2,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; @@ -40,42 +39,58 @@ declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]); impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let Some((c, op_node, l)) = get_const(cx, expr1); - if let BinOpKind::Ne | BinOpKind::Lt = op_node; - if let ExprKind::Block(block, None) = then.kind; - if let Block { + if let ExprKind::If(cond, then, None) = expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let Some((c, op_node, l)) = get_const(cx, expr1) + && let BinOpKind::Ne | BinOpKind::Lt = op_node + && let ExprKind::Block(block, None) = then.kind + && let Block { stmts: - [Stmt - { kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }], - expr: None, ..} | - Block { stmts: [], expr: Some(ex), ..} = block; - if let ExprKind::AssignOp(op1, target, value) = ex.kind; - let ty = cx.typeck_results().expr_ty(target); - if Some(c) == get_int_max(ty); - let ctxt = expr.span.ctxt(); - if ex.span.ctxt() == ctxt; - if expr1.span.ctxt() == ctxt; - if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); - if BinOpKind::Add == op1.node; - if let ExprKind::Lit(lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if block.expr.is_none(); - then { - let mut app = Applicability::MachineApplicable; - let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; - let sugg = if let Some(parent) = get_parent_expr(cx, expr) - && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind - && else_.hir_id == expr.hir_id - { - format!("{{{code} = {code}.saturating_add(1); }}") - } else { - format!("{code} = {code}.saturating_add(1);") - }; - span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); + [ + Stmt { + kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), + .. + }, + ], + expr: None, + .. } + | Block { + stmts: [], + expr: Some(ex), + .. + } = block + && let ExprKind::AssignOp(op1, target, value) = ex.kind + && let ty = cx.typeck_results().expr_ty(target) + && Some(c) == get_int_max(ty) + && let ctxt = expr.span.ctxt() + && ex.span.ctxt() == ctxt + && expr1.span.ctxt() == ctxt + && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target) + && BinOpKind::Add == op1.node + && let ExprKind::Lit(lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && block.expr.is_none() + { + let mut app = Applicability::MachineApplicable; + let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; + let sugg = if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind + && else_.hir_id == expr.hir_id + { + format!("{{{code} = {code}.saturating_add(1); }}") + } else { + format!("{code} = {code}.saturating_add(1);") + }; + span_lint_and_sugg( + cx, + IMPLICIT_SATURATING_ADD, + expr.span, + "manual saturating add detected", + "use instead", + sugg, + app, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 859404289d9..fc66f86ae86 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; @@ -46,83 +45,76 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if expr.span.from_expansion() { return; } - if_chain! { - if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr); + if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr) // Check if the conditional expression is a binary operation - if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; + && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind // Ensure that the binary operator is >, !=, or < - if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; + && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node) // Check if assign operation is done - if let Some(target) = subtracts_one(cx, then); + && let Some(target) = subtracts_one(cx, then) // Extracting out the variable name - if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; - - then { - // Handle symmetric conditions in the if statement - let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { - if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_left, cond_right) - } else { - return; - } - } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { - if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_right, cond_left) - } else { - return; - } + && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind + { + // Handle symmetric conditions in the if statement + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { + (cond_left, cond_right) } else { return; - }; - - // Check if the variable in the condition statement is an integer - if !cx.typeck_results().expr_ty(cond_var).is_integral() { + } + } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { + (cond_right, cond_left) + } else { return; } + } else { + return; + }; - // Get the variable name - let var_name = ares_path.segments[0].ident.name.as_str(); - match cond_num_val.kind { - ExprKind::Lit(cond_lit) => { - // Check if the constant is zero - if let LitKind::Int(0, _) = cond_lit.node { - if cx.typeck_results().expr_ty(cond_left).is_signed() { - } else { - print_lint_and_sugg(cx, var_name, expr); - }; - } - }, - ExprKind::Path(QPath::TypeRelative(_, name)) => { - if_chain! { - if name.ident.as_str() == "MIN"; - if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(const_id); - if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl - if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); - then { - print_lint_and_sugg(cx, var_name, expr) - } - } - }, - ExprKind::Call(func, []) => { - if_chain! { - if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind; - if name.ident.as_str() == "min_value"; - if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(func_id); - if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl - if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); - then { - print_lint_and_sugg(cx, var_name, expr) - } - } - }, - _ => (), - } + // Check if the variable in the condition statement is an integer + if !cx.typeck_results().expr_ty(cond_var).is_integral() { + return; + } + + // Get the variable name + let var_name = ares_path.segments[0].ident.name.as_str(); + match cond_num_val.kind { + ExprKind::Lit(cond_lit) => { + // Check if the constant is zero + if let LitKind::Int(0, _) = cond_lit.node { + if cx.typeck_results().expr_ty(cond_left).is_signed() { + } else { + print_lint_and_sugg(cx, var_name, expr); + }; + } + }, + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if name.ident.as_str() == "MIN" + && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(const_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + ExprKind::Call(func, []) => { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind + && name.ident.as_str() == "min_value" + && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(func_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + _ => (), } } } @@ -135,18 +127,14 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { - if_chain! { - if let ExprKind::Binary(ref op1, left1, right1) = value.kind; - if BinOpKind::Sub == op1.node; - - if SpanlessEq::new(cx).eq_expr(left1, target); - - if is_integer_literal(right1, 1); - then { - Some(target) - } else { - None - } + if let ExprKind::Binary(ref op1, left1, right1) = value.kind + && BinOpKind::Sub == op1.node + && SpanlessEq::new(cx).eq_expr(left1, target) + && is_integer_literal(right1, 1) + { + Some(target) + } else { + None } }, _ => None, diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index ff27a5d666d..232d8eeb11b 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// Box::new(123) /// } /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.74.0"] pub IMPLIED_BOUNDS_IN_IMPLS, nursery, "specifying bounds that are implied by other bounds in `impl Trait` type" diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index a84f7351ad6..f6e1281a291 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind}; @@ -66,54 +65,53 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Struct(qpath, fields, base) = expr.kind; - let ty = cx.typeck_results().expr_ty(expr); - if let Some(adt_def) = ty.ty_adt_def(); - if adt_def.is_struct(); - if let Some(variant) = adt_def.variants().iter().next(); - if fields.iter().all(|f| f.is_shorthand); - then { - let mut def_order_map = FxHashMap::default(); - for (idx, field) in variant.fields.iter().enumerate() { - def_order_map.insert(field.name, idx); - } + if !expr.span.from_expansion() + && let ExprKind::Struct(qpath, fields, base) = expr.kind + && let ty = cx.typeck_results().expr_ty(expr) + && let Some(adt_def) = ty.ty_adt_def() + && adt_def.is_struct() + && let Some(variant) = adt_def.variants().iter().next() + && fields.iter().all(|f| f.is_shorthand) + { + let mut def_order_map = FxHashMap::default(); + for (idx, field) in variant.fields.iter().enumerate() { + def_order_map.insert(field.name, idx); + } - if is_consistent_order(fields, &def_order_map) { - return; - } + if is_consistent_order(fields, &def_order_map) { + return; + } - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); + let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); + ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); + let mut fields_snippet = String::new(); + let (last_ident, idents) = ordered_fields.split_last().unwrap(); + for ident in idents { + let _: fmt::Result = write!(fields_snippet, "{ident}, "); + } + fields_snippet.push_str(&last_ident.to_string()); - let base_snippet = if let Some(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; + let base_snippet = if let Some(base) = base { + format!(", ..{}", snippet(cx, base.span, "..")) + } else { + String::new() + }; - let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); + let sugg = format!( + "{} {{ {fields_snippet}{base_snippet} }}", + snippet(cx, qpath.span(), ".."), + ); - span_lint_and_sugg( - cx, - INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, - "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, - ) - } + span_lint_and_sugg( + cx, + INCONSISTENT_STRUCT_CONSTRUCTOR, + expr.span, + "struct constructor field order is inconsistent with struct definition field order", + "try", + sugg, + Applicability::MachineApplicable, + ); } } } 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 c2f1f18e39d..fa6536db796 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; -use if_chain::if_chain; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -70,20 +69,17 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); - if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); - if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if self.msrv.meets(msrvs::SLICE_PATTERNS); - - let found_slices = find_slice_values(cx, let_pat); - if !found_slices.is_empty(); - let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then); - if !filtered_slices.is_empty(); - then { - for slice in filtered_slices.values() { - lint_slice(cx, slice); - } + if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) + && let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) + && self.msrv.meets(msrvs::SLICE_PATTERNS) + && let found_slices = find_slice_values(cx, let_pat) + && !found_slices.is_empty() + && let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then) + && !filtered_slices.is_empty() + { + for slice in filtered_slices.values() { + lint_slice(cx, slice); } } } @@ -245,28 +241,26 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { max_suggested_slice, } = *self; - if_chain! { + if let Some(use_info) = slice_lint_info.get_mut(&local_id) // Check if this is even a local we're interested in - if let Some(use_info) = slice_lint_info.get_mut(&local_id); - let map = cx.tcx.hir(); + && let map = cx.tcx.hir() // Checking for slice indexing - let parent_id = map.parent_id(expr.hir_id); - if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); - if let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind; - if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr); - if let Ok(index_value) = index_value.try_into(); - if index_value < max_suggested_slice; + && let parent_id = map.parent_id(expr.hir_id) + && let Some(hir::Node::Expr(parent_expr)) = map.find(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 Ok(index_value) = index_value.try_into() + && index_value < max_suggested_slice // Make sure that this slice index is read only - let maybe_addrof_id = map.parent_id(parent_id); - if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id); - if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind; - then { - use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); - return; - } + && let maybe_addrof_id = map.parent_id(parent_id) + && let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id) + && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind + { + use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); + return; } // The slice was used for something other than indexing diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 32b2cb4385f..8e84c73666f 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -89,27 +89,17 @@ impl LateLintPass<'_> for InstantSubtraction { rhs, ) = expr.kind { - if_chain! { - if is_instant_now_call(cx, lhs); - - if is_an_instant(cx, rhs); - if let Some(sugg) = Sugg::hir_opt(cx, rhs); - - then { - print_manual_instant_elapsed_sugg(cx, expr, sugg) - } else { - if_chain! { - if !expr.span.from_expansion(); - if self.msrv.meets(msrvs::TRY_FROM); - - if is_an_instant(cx, lhs); - if is_a_duration(cx, rhs); - - then { - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) - } - } - } + if is_instant_now_call(cx, lhs) + && is_an_instant(cx, rhs) + && let Some(sugg) = Sugg::hir_opt(cx, rhs) + { + print_manual_instant_elapsed_sugg(cx, expr, sugg); + } else if !expr.span.from_expansion() + && self.msrv.meets(msrvs::TRY_FROM) + && is_an_instant(cx, lhs) + && is_a_duration(cx, rhs) + { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } } diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 90048d96c9c..2b131d27b7b 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -7,8 +7,8 @@ use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_sta use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::symbol::Symbol; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 505aadd1a11..fce3b0e18b7 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -71,7 +71,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI if sig.decl.implicit_self.has_implicit_self() { let ret_ty = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).instantiate_identity().output()); + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(fn_id).instantiate_identity().output()); let ret_ty = cx .tcx .try_normalize_erasing_regions(cx.param_env, ret_ty) diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs new file mode 100644 index 00000000000..7755adc4c1d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -0,0 +1,78 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::higher::ForLoop; +use clippy_utils::match_any_def_paths; +use clippy_utils::paths::{ + HASHMAP_DRAIN, HASHMAP_ITER, HASHMAP_ITER_MUT, HASHMAP_KEYS, HASHMAP_VALUES, HASHMAP_VALUES_MUT, HASHSET_DRAIN, + HASHSET_ITER_TY, +}; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// This is a restriction lint which prevents the use of hash types (i.e., `HashSet` and `HashMap`) in for loops. + /// + /// ### Why is this bad? + /// Because hash types are unordered, when iterated through such as in a for loop, the values are returned in + /// an undefined order. As a result, on redundant systems this may cause inconsistencies and anomalies. + /// In addition, the unknown order of the elements may reduce readability or introduce other undesired + /// side effects. + /// + /// ### Example + /// ```no_run + /// let my_map = std::collections::HashMap::<i32, String>::new(); + /// for (key, value) in my_map { /* ... */ } + /// ``` + /// Use instead: + /// ```no_run + /// let my_map = std::collections::HashMap::<i32, String>::new(); + /// let mut keys = my_map.keys().clone().collect::<Vec<_>>(); + /// keys.sort(); + /// for key in keys { + /// let value = &my_map[key]; + /// } + /// ``` + #[clippy::version = "1.75.0"] + pub ITER_OVER_HASH_TYPE, + restriction, + "iterating over unordered hash-based types (`HashMap` and `HashSet`)" +} + +declare_lint_pass!(IterOverHashType => [ITER_OVER_HASH_TYPE]); + +impl LateLintPass<'_> for IterOverHashType { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) { + if let Some(for_loop) = ForLoop::hir(expr) + && !for_loop.body.span.from_expansion() + && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() + && let Some(adt) = ty.ty_adt_def() + && let did = adt.did() + && (match_any_def_paths( + cx, + did, + &[ + &HASHMAP_KEYS, + &HASHMAP_VALUES, + &HASHMAP_VALUES_MUT, + &HASHMAP_ITER, + &HASHMAP_ITER_MUT, + &HASHMAP_DRAIN, + &HASHSET_ITER_TY, + &HASHSET_DRAIN, + ], + ) + .is_some() + || is_type_diagnostic_item(cx, ty, sym::HashMap) + || is_type_diagnostic_item(cx, ty, sym::HashSet)) + { + span_lint( + cx, + ITER_OVER_HASH_TYPE, + expr.span, + "iteration over unordered hash-based type", + ); + }; + } +} diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index a4f3d498345..7db088f986f 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,43 +46,40 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if !item.span.from_expansion(); - if let ItemKind::Const(_, generics, _) = &item.kind; + if !item.span.from_expansion() + && let ItemKind::Const(_, generics, _) = &item.kind // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. - if generics.params.is_empty() && !generics.has_where_clause_predicates; - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - if let ty::Array(element_type, cst) = ty.kind(); - if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); - if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx); - if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); - - then { - let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); - let sugg_span = Span::new( - hi_pos - BytePos::from_usize("const".len()), - hi_pos, - item.span.ctxt(), - item.span.parent(), - ); - span_lint_and_then( - cx, - LARGE_CONST_ARRAYS, - item.span, - "large array defined as const", - |diag| { - diag.span_suggestion( - sugg_span, - "make this a static item", - "static", - Applicability::MachineApplicable, - ); - } - ); - } + && generics.params.is_empty() && !generics.has_where_clause_predicates + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let ty::Array(element_type, cst) = ty.kind() + && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) + && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) + && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) + { + let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); + let sugg_span = Span::new( + hi_pos - BytePos::from_usize("const".len()), + hi_pos, + item.span.ctxt(), + item.span.parent(), + ); + span_lint_and_then( + cx, + LARGE_CONST_ARRAYS, + item.span, + "large array defined as const", + |diag| { + diag.span_suggestion( + sugg_span, + "make this a static item", + "static", + Applicability::MachineApplicable, + ); + }, + ); } } } 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 566901de347..902b72ba5e4 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -50,37 +50,35 @@ impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if_chain! { - if let Some(macro_call) = root_macro_call_first_node(cx, expr); - if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); - if 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); - if let ExprKind::Lit(lit) = &expr.kind; - then { - let len = match &lit.node { - // include_bytes - LitKind::ByteStr(bstr, _) => bstr.len(), - // include_str - LitKind::Str(sym, _) => sym.as_str().len(), - _ => return, - }; + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id) + && (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)) + && let ExprKind::Lit(lit) = &expr.kind + { + let len = match &lit.node { + // include_bytes + LitKind::ByteStr(bstr, _) => bstr.len(), + // include_str + LitKind::Str(sym, _) => sym.as_str().len(), + _ => return, + }; - if len as u64 <= self.max_file_size { - return; - } - - span_lint_and_note( - cx, - LARGE_INCLUDE_FILE, - expr.span, - "attempted to include a large file", - None, - &format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - ), - ); + if len as u64 <= self.max_file_size { + return; } + + span_lint_and_note( + cx, + LARGE_INCLUDE_FILE, + expr.span, + "attempted to include a large file", + None, + &format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + ), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 0f17d26764c..6fc14dd8941 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -15,9 +14,9 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{Span, Symbol}; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -132,37 +131,33 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if_chain! { - if item.ident.name == sym::len; - if let ImplItemKind::Fn(sig, _) = &item.kind; - if sig.decl.implicit_self.has_implicit_self(); - if sig.decl.inputs.len() == 1; - if cx.effective_visibilities.is_exported(item.owner_id.def_id); - if matches!(sig.decl.output, FnRetTy::Return(_)); - if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); - if imp.of_trait.is_none(); - if let TyKind::Path(ty_path) = &imp.self_ty.kind; - if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id(); - if let Some(local_id) = ty_id.as_local(); - let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); - if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); - if let Some(output) = parse_len_output( - cx, - cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder() - ); - then { - let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { - Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), - Some(Node::Item(x)) => match x.kind { - ItemKind::Struct(..) => (x.ident.name, "struct"), - ItemKind::Enum(..) => (x.ident.name, "enum"), - ItemKind::Union(..) => (x.ident.name, "union"), - _ => (x.ident.name, "type"), - } - _ => return, - }; - check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind) - } + if item.ident.name == sym::len + && let ImplItemKind::Fn(sig, _) = &item.kind + && sig.decl.implicit_self.has_implicit_self() + && sig.decl.inputs.len() == 1 + && cx.effective_visibilities.is_exported(item.owner_id.def_id) + && matches!(sig.decl.output, FnRetTy::Return(_)) + && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) + && imp.of_trait.is_none() + && let TyKind::Path(ty_path) = &imp.self_ty.kind + && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() + && let Some(local_id) = ty_id.as_local() + && let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id) + && !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id) + && let Some(output) = + parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) + { + let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { + Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), + Some(Node::Item(x)) => match x.kind { + ItemKind::Struct(..) => (x.ident.name, "struct"), + ItemKind::Enum(..) => (x.ident.name, "enum"), + ItemKind::Union(..) => (x.ident.name, "union"), + _ => (x.ident.name, "type"), + }, + _ => return, + }; + check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind); } } diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index 2f6f36c3960..da269ec61ff 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local_id; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingAnnotation, Mutability}; @@ -61,76 +60,85 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { let mut it = block.stmts.iter().peekable(); while let Some(stmt) = it.next() { - if_chain! { - if let Some(expr) = it.peek(); - if let hir::StmtKind::Local(local) = stmt.kind; - if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; - if let hir::StmtKind::Expr(if_) = expr.kind; - if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; - if !is_local_used(cx, *cond, canonical_id); - if let hir::ExprKind::Block(then, _) = then.kind; - if let Some(value) = check_assign(cx, canonical_id, then); - if !is_local_used(cx, value, canonical_id); - then { - let span = stmt.span.to(if_.span); + if let Some(expr) = it.peek() + && let hir::StmtKind::Local(local) = stmt.kind + && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind + && let hir::StmtKind::Expr(if_) = expr.kind + && let hir::ExprKind::If( + hir::Expr { + kind: hir::ExprKind::DropTemps(cond), + .. + }, + then, + else_, + ) = if_.kind + && !is_local_used(cx, *cond, canonical_id) + && let hir::ExprKind::Block(then, _) = then.kind + && let Some(value) = check_assign(cx, canonical_id, then) + && !is_local_used(cx, value, canonical_id) + { + let span = stmt.span.to(if_.span); - let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze( - cx.tcx, - cx.param_env, - ); - if has_interior_mutability { return; } + let has_interior_mutability = !cx + .typeck_results() + .node_type(canonical_id) + .is_freeze(cx.tcx, cx.param_env); + if has_interior_mutability { + return; + } - let (default_multi_stmts, default) = if let Some(else_) = else_ { - if let hir::ExprKind::Block(else_, _) = else_.kind { - if let Some(default) = check_assign(cx, canonical_id, else_) { - (else_.stmts.len() > 1, default) - } else if let Some(default) = local.init { - (true, default) - } else { - continue; - } + let (default_multi_stmts, default) = if let Some(else_) = else_ { + if let hir::ExprKind::Block(else_, _) = else_.kind { + if let Some(default) = check_assign(cx, canonical_id, else_) { + (else_.stmts.len() > 1, default) + } else if let Some(default) = local.init { + (true, default) } else { continue; } - } else if let Some(default) = local.init { - (false, default) } else { continue; - }; + } + } else if let Some(default) = local.init { + (false, default) + } else { + continue; + }; - let mutability = match mode { - BindingAnnotation(_, Mutability::Mut) => "<mut> ", - _ => "", - }; + let mutability = match mode { + BindingAnnotation(_, Mutability::Mut) => "<mut> ", + _ => "", + }; - // FIXME: this should not suggest `mut` if we can detect that the variable is not - // use mutably after the `if` + // FIXME: this should not suggest `mut` if we can detect that the variable is not + // use mutably after the `if` - let sug = format!( - "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", - name=ident.name, - cond=snippet(cx, cond.span, "_"), - then=if then.stmts.len() > 1 { " ..;" } else { "" }, - else=if default_multi_stmts { " ..;" } else { "" }, - value=snippet(cx, value.span, "<value>"), - default=snippet(cx, default.span, "<default>"), - ); - span_lint_and_then(cx, - USELESS_LET_IF_SEQ, - span, - "`if _ { .. } else { .. }` is an expression", - |diag| { - diag.span_suggestion( - span, - "it is more idiomatic to write", - sug, - Applicability::HasPlaceholders, - ); - if !mutability.is_empty() { - diag.note("you might not need `mut` at all"); - } - }); - } + let sug = format!( + "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", + name=ident.name, + cond=snippet(cx, cond.span, "_"), + then=if then.stmts.len() > 1 { " ..;" } else { "" }, + else=if default_multi_stmts { " ..;" } else { "" }, + value=snippet(cx, value.span, "<value>"), + default=snippet(cx, default.span, "<default>"), + ); + span_lint_and_then( + cx, + USELESS_LET_IF_SEQ, + span, + "`if _ { .. } else { .. }` is an expression", + |diag| { + diag.span_suggestion( + span, + "it is more idiomatic to write", + sug, + Applicability::HasPlaceholders, + ); + if !mutability.is_empty() { + diag.note("you might not need `mut` at all"); + } + }, + ); } } } @@ -141,20 +149,23 @@ fn check_assign<'tcx>( decl: hir::HirId, block: &'tcx hir::Block<'_>, ) -> Option<&'tcx hir::Expr<'tcx>> { - if_chain! { - if block.expr.is_none(); - if let Some(expr) = block.stmts.iter().last(); - if let hir::StmtKind::Semi(expr) = expr.kind; - if let hir::ExprKind::Assign(var, value, _) = expr.kind; - if path_to_local_id(var, decl); - then { - if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) { - None - } else { - Some(value) - } - } else { + if block.expr.is_none() + && let Some(expr) = block.stmts.iter().last() + && let hir::StmtKind::Semi(expr) = expr.kind + && let hir::ExprKind::Assign(var, value, _) = expr.kind + && path_to_local_id(var, decl) + { + if block + .stmts + .iter() + .take(block.stmts.len() - 1) + .any(|stmt| is_local_used(cx, stmt, decl)) + { None + } else { + Some(value) } + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 79d728a021c..d4f410de957 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -27,27 +27,25 @@ declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); impl LateLintPass<'_> for UnderscoreTyped { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, local.span); - if let Some(ty) = local.ty; // Ensure that it has a type defined - if let TyKind::Infer = &ty.kind; // that type is '_' - if local.span.eq_ctxt(ty.span); - then { - // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized, - // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty` - if snippet(cx, ty.span, "_").trim() != "_" { - return; - } - - span_lint_and_help( - cx, - LET_WITH_TYPE_UNDERSCORE, - local.span, - "variable declared with type underscore", - Some(ty.span.with_lo(local.pat.span.hi())), - "remove the explicit type `_` declaration" - ) + if !in_external_macro(cx.tcx.sess, local.span) + && let Some(ty) = local.ty // Ensure that it has a type defined + && let TyKind::Infer = &ty.kind // that type is '_' + && local.span.eq_ctxt(ty.span) + { + // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized, + // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty` + if snippet(cx, ty.span, "_").trim() != "_" { + return; } + + span_lint_and_help( + cx, + LET_WITH_TYPE_UNDERSCORE, + local.span, + "variable declared with type underscore", + Some(ty.span.with_lo(local.pat.span.hi())), + "remove the explicit type `_` declaration", + ); }; } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index ab978a677c2..c462c933082 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -52,7 +52,6 @@ extern crate declare_clippy_lint; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use rustc_session::Session; #[cfg(feature = "internal")] pub mod deprecated_lints; @@ -165,6 +164,7 @@ mod item_name_repetitions; mod items_after_statements; mod items_after_test_module; mod iter_not_returning_iterator; +mod iter_over_hash_type; mod iter_without_into_iter; mod large_const_arrays; mod large_enum_variant; @@ -308,7 +308,6 @@ mod slow_vector_initialization; mod std_instead_of_core; mod strings; mod strlen_on_c_strings; -mod suspicious_doc_comments; mod suspicious_operation_groupings; mod suspicious_trait_impl; mod suspicious_xor_used_as_pow; @@ -492,11 +491,83 @@ fn register_categories(store: &mut rustc_lint::LintStore) { groups.register(store); } -/// Register all lints and lint groups with the rustc plugin registry +/// Register all lints and lint groups with the rustc lint store /// /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] -pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &'static Conf) { +pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { + let Conf { + ref absolute_paths_allowed_crates, + absolute_paths_max_segments, + accept_comment_above_attributes, + accept_comment_above_statement, + allow_dbg_in_tests, + allow_expect_in_tests, + allow_mixed_uninlined_format_args, + allow_one_hash_in_raw_strings, + allow_print_in_tests, + allow_private_module_inception, + allow_unwrap_in_tests, + ref allowed_dotfiles, + ref allowed_idents_below_min_chars, + ref allowed_scripts, + ref arithmetic_side_effects_allowed_binary, + ref arithmetic_side_effects_allowed_unary, + ref arithmetic_side_effects_allowed, + array_size_threshold, + avoid_breaking_exported_api, + ref await_holding_invalid_types, + cargo_ignore_publish, + cognitive_complexity_threshold, + ref disallowed_macros, + ref disallowed_methods, + ref disallowed_names, + ref disallowed_types, + ref doc_valid_idents, + enable_raw_pointer_heuristic_for_send, + enforce_iter_loop_reborrow, + ref enforced_import_renames, + enum_variant_name_threshold, + enum_variant_size_threshold, + excessive_nesting_threshold, + future_size_threshold, + ref ignore_interior_mutability, + large_error_threshold, + literal_representation_threshold, + matches_for_let_else, + max_fn_params_bools, + max_include_file_size, + max_struct_bools, + max_suggested_slice_pattern_length, + max_trait_bounds, + min_ident_chars_threshold, + missing_docs_in_crate_items, + ref msrv, + pass_by_value_size_limit, + semicolon_inside_block_ignore_singleline, + semicolon_outside_block_ignore_multiline, + single_char_binding_names_threshold, + stack_size_threshold, + ref standard_macro_braces, + struct_field_name_threshold, + suppress_restriction_lint_in_const, + too_large_for_stack, + too_many_arguments_threshold, + too_many_lines_threshold, + trivial_copy_size_limit, + type_complexity_threshold, + unnecessary_box_size, + unreadable_literal_lint_fractions, + upper_case_acronyms_aggressive, + vec_box_size_threshold, + verbose_bit_mask_threshold, + warn_on_all_wildcard_imports, + + blacklisted_names: _, + cyclomatic_complexity_threshold: _, + } = *conf; + let msrv = || msrv.clone(); + register_removed_non_tool_lints(store); register_categories(store); @@ -521,7 +592,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| { Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) }); - store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle)); store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); store.register_late_pass(|_| { Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default() @@ -537,9 +607,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); } - let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); - let arithmetic_side_effects_allowed_binary = conf.arithmetic_side_effects_allowed_binary.clone(); - let arithmetic_side_effects_allowed_unary = conf.arithmetic_side_effects_allowed_unary.clone(); store.register_late_pass(move |_| { Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( arithmetic_side_effects_allowed @@ -557,16 +624,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); - let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); store.register_late_pass(move |_| { Box::new(await_holding_invalid::AwaitHolding::new( await_holding_invalid_types.clone(), )) }); store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); - let vec_box_size_threshold = conf.vec_box_size_threshold; - let type_complexity_threshold = conf.type_complexity_threshold; - let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; store.register_late_pass(move |_| { Box::new(types::Types::new( vec_box_size_threshold, @@ -599,19 +662,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - - let msrv = || conf.msrv.clone(); - let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; - let allow_expect_in_tests = conf.allow_expect_in_tests; - let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; - let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); - let allowed_dotfiles = conf - .allowed_dotfiles - .iter() - .cloned() - .chain(methods::DEFAULT_ALLOWED_DOTFILES.iter().copied().map(ToOwned::to_owned)) - .collect::<FxHashSet<_>>(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, @@ -622,7 +673,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); - let matches_for_let_else = conf.matches_for_let_else; store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); @@ -639,7 +689,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); - let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; store.register_late_pass(move |_| { Box::new(index_refutable_slice::IndexRefutableSlice::new( max_suggested_slice_pattern_length, @@ -648,7 +697,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|_| Box::<shadow::Shadow>::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); - let enforce_iter_loop_reborrow = conf.enforce_iter_loop_reborrow; store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow))); store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); @@ -662,13 +710,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(no_effect::NoEffect)); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); - let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; store.register_late_pass(move |_| { Box::new(cognitive_complexity::CognitiveComplexity::new( cognitive_complexity_threshold, )) }); - let too_large_for_stack = conf.too_large_for_stack; store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack })); store.register_late_pass(move |_| { Box::new(vec::UselessVec { @@ -684,18 +730,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::<regex::Regex>::default()); - let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default()); - let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>(); - store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone()))); - let too_many_arguments_threshold = conf.too_many_arguments_threshold; - let too_many_lines_threshold = conf.too_many_lines_threshold; - let large_error_threshold = conf.large_error_threshold; + store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names))); store.register_late_pass(move |_| { Box::new(functions::Functions::new( too_many_arguments_threshold, @@ -704,9 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: avoid_breaking_exported_api, )) }); - let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); - let missing_docs_in_crate_items = conf.missing_docs_in_crate_items; - store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); + store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents))); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); @@ -716,17 +755,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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)); - let enum_variant_size_threshold = conf.enum_variant_size_threshold; store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite)); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); - let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( - conf.trivial_copy_size_limit, - conf.pass_by_value_size_limit, - conf.avoid_breaking_exported_api, - &sess.target, - ); - store.register_late_pass(move |_| Box::new(pass_by_ref_or_value)); + store.register_late_pass(move |tcx| { + Box::new(pass_by_ref_or_value::PassByRefOrValue::new( + trivial_copy_size_limit, + pass_by_value_size_limit, + avoid_breaking_exported_api, + tcx.sess.target.pointer_width, + )) + }); store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); @@ -746,7 +785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: suppress_restriction_lint_in_const, )) }); - let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); @@ -755,10 +793,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); - let max_trait_bounds = conf.max_trait_bounds; store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv()))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); @@ -779,21 +815,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); - let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; store.register_early_pass(move || { Box::new(literal_representation::LiteralDigitGrouping::new( - literal_representation_lint_fraction_readability, + unreadable_literal_lint_fractions, )) }); - let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || { Box::new(literal_representation::DecimalLiteralRepresentation::new( literal_representation_threshold, )) }); - let enum_variant_name_threshold = conf.enum_variant_name_threshold; - let struct_field_name_threshold = conf.struct_field_name_threshold; - let allow_private_module_inception = conf.allow_private_module_inception; store.register_late_pass(move |_| { Box::new(item_name_repetitions::ItemNameRepetitions::new( enum_variant_name_threshold, @@ -803,7 +834,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); - let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; store.register_late_pass(move |_| { Box::new(upper_case_acronyms::UpperCaseAcronyms::new( avoid_breaking_exported_api, @@ -815,15 +845,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); - let array_size_threshold = u128::from(conf.array_size_threshold); - store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold))); - store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold))); + store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold.into()))); + store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold.into()))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_late_pass(|_| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()); - let max_fn_params_bools = conf.max_fn_params_bools; - let max_struct_bools = conf.max_struct_bools; store.register_late_pass(move |_| { Box::new(excessive_bools::ExcessiveBools::new( max_struct_bools, @@ -831,36 +858,30 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); - let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default()); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); - let future_size_threshold = conf.future_size_threshold; store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold))); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); store.register_late_pass(|_| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn)); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || { Box::new(non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }) }); - let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>(); - store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher))); + store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(standard_macro_braces))); store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); - let disallowed_macros = conf.disallowed_macros.clone(); store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); - let disallowed_methods = conf.disallowed_methods.clone(); store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); @@ -875,36 +896,30 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default()); - let disallowed_types = conf.disallowed_types.clone(); store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone()))); - let import_renames = conf.enforced_import_renames.clone(); store.register_late_pass(move |_| { Box::new(missing_enforced_import_rename::ImportRename::new( - import_renames.clone(), + enforced_import_renames.clone(), )) }); - let scripts = conf.allowed_scripts.clone(); - store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts))); + store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(allowed_scripts))); store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)); store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors)); store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert)); - let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send; store.register_late_pass(move |_| { Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new( enable_raw_pointer_heuristic_for_send, )) }); - let accept_comment_above_statement = conf.accept_comment_above_statement; - let accept_comment_above_attributes = conf.accept_comment_above_attributes; store.register_late_pass(move |_| { Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new( accept_comment_above_statement, accept_comment_above_attributes, )) }); - let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args; - store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined))); + store + .register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -914,11 +929,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); - let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); - let allow_print_in_tests = conf.allow_print_in_tests; store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); - let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, @@ -929,7 +941,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); - let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); @@ -942,7 +953,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); - let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); @@ -959,8 +969,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); - let semicolon_inside_block_ignore_singleline = conf.semicolon_inside_block_ignore_singleline; - let semicolon_outside_block_ignore_multiline = conf.semicolon_outside_block_ignore_multiline; store.register_late_pass(move |_| { Box::new(semicolon_block::SemicolonBlock::new( semicolon_inside_block_ignore_singleline, @@ -983,7 +991,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv()))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); - let unnecessary_box_size = conf.unnecessary_box_size; store.register_late_pass(move |_| { Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new( avoid_breaking_exported_api, @@ -993,8 +1000,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk)); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); - store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments)); - let excessive_nesting_threshold = conf.excessive_nesting_threshold; store.register_early_pass(move || { Box::new(excessive_nesting::ExcessiveNesting { excessive_nesting_threshold, @@ -1010,15 +1015,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)); store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)); store.register_late_pass(|_| Box::new(needless_if::NeedlessIf)); - let allowed_idents_below_min_chars = conf.allowed_idents_below_min_chars.clone(); - let min_ident_chars_threshold = conf.min_ident_chars_threshold; store.register_late_pass(move |_| { Box::new(min_ident_chars::MinIdentChars { allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(), min_ident_chars_threshold, }) }); - let stack_size_threshold = conf.stack_size_threshold; store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); store.register_late_pass(move |_| { @@ -1033,10 +1035,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: def_id_to_usage: rustc_data_structures::fx::FxHashMap::default(), }) }); - let needless_raw_string_hashes_allow_one = conf.allow_one_hash_in_raw_strings; store.register_early_pass(move || { Box::new(raw_strings::RawStrings { - needless_raw_string_hashes_allow_one, + allow_one_hash_in_raw_strings, }) }); store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); @@ -1045,8 +1046,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); - let absolute_paths_max_segments = conf.absolute_paths_max_segments; - let absolute_paths_allowed_crates = conf.absolute_paths_allowed_crates.clone(); store.register_late_pass(move |_| { Box::new(absolute_paths::AbsolutePaths { absolute_paths_max_segments, @@ -1066,6 +1065,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); + store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 7517003be23..bb0edec3373 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -20,8 +20,8 @@ use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; -use rustc_span::Span; use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -310,20 +310,17 @@ fn elision_suggestions( // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool { - if_chain! { - if let Some(ident) = ident; - if ident.name == kw::SelfLower; - if !func.implicit_self.has_implicit_self(); - - if let Some(self_ty) = func.inputs.first(); - then { - let mut visitor = RefVisitor::new(cx); - visitor.visit_ty(self_ty); - - !visitor.all_lts().is_empty() - } else { - false - } + if let Some(ident) = ident + && ident.name == kw::SelfLower + && !func.implicit_self.has_implicit_self() + && let Some(self_ty) = func.inputs.first() + { + let mut visitor = RefVisitor::new(cx); + visitor.visit_ty(self_ty); + + !visitor.all_lts().is_empty() + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 2c14bb72a9e..8f34a9b1fed 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -255,56 +254,48 @@ impl LiteralDigitGrouping { } fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if_chain! { - if let Some(src) = snippet_opt(cx, span); - if let Ok(lit_kind) = LitKind::from_token_lit(lit); - if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); - then { - if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { - return; - } + if let Some(src) = snippet_opt(cx, span) + && let Ok(lit_kind) = LitKind::from_token_lit(lit) + && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) + { + if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { + return; + } + + if Self::is_literal_uuid_formatted(&num_lit) { + return; + } - if Self::is_literal_uuid_formatted(&num_lit) { - return; + let result = (|| { + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; + if let Some(fraction) = num_lit.fraction { + let fractional_group_size = + Self::get_group_size(fraction.rsplit('_'), num_lit.radix, self.lint_fraction_readability)?; + + let consistent = Self::parts_consistent( + integral_group_size, + fractional_group_size, + num_lit.integer.len(), + fraction.len(), + ); + if !consistent { + return Err(WarningType::InconsistentDigitGrouping); + }; } - let result = (|| { - - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; - if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size( - fraction.rsplit('_'), - num_lit.radix, - self.lint_fraction_readability)?; - - let consistent = Self::parts_consistent(integral_group_size, - fractional_group_size, - num_lit.integer.len(), - fraction.len()); - if !consistent { - return Err(WarningType::InconsistentDigitGrouping); - }; - } + Ok(()) + })(); - Ok(()) - })(); - - - if let Err(warning_type) = result { - let should_warn = match warning_type { - | WarningType::UnreadableLiteral - | WarningType::InconsistentDigitGrouping - | WarningType::UnusualByteGroupings - | WarningType::LargeDigitGroups => { - !span.from_expansion() - } - WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { - true - } - }; - if should_warn { - warning_type.display(num_lit.format(), cx, span); - } + if let Err(warning_type) = result { + let should_warn = match warning_type { + WarningType::UnreadableLiteral + | WarningType::InconsistentDigitGrouping + | WarningType::UnusualByteGroupings + | WarningType::LargeDigitGroups => !span.from_expansion(), + WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true, + }; + if should_warn { + warning_type.display(num_lit.format(), cx, span); } } } @@ -478,20 +469,18 @@ impl DecimalLiteralRepresentation { } fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. - if_chain! { - if let Ok(lit_kind) = LitKind::from_token_lit(lit); - if let LitKind::Int(val, _) = lit_kind; - if let Some(src) = snippet_opt(cx, span); - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); - if num_lit.radix == Radix::Decimal; - if val >= u128::from(self.threshold); - then { - 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); - }); - } + if let Ok(lit_kind) = LitKind::from_token_lit(lit) + && let LitKind::Int(val, _) = lit_kind + && let Some(src) = snippet_opt(cx, span) + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) + && num_lit.radix == Radix::Decimal + && val >= u128::from(self.threshold) + { + 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); + }); } } 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 1953ee8a717..277062a8490 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,7 +2,6 @@ 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 if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::{Expr, Pat}; @@ -30,59 +29,57 @@ pub(super) fn check<'tcx>( let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); walk_block(&mut initialize_visitor, block); - if_chain! { - if let Some((name, ty, initializer)) = initialize_visitor.get_result(); - if is_integer_const(cx, initializer, 0); - then { - let mut applicability = Applicability::MaybeIncorrect; - let span = expr.span.with_hi(arg.span.hi()); + if let Some((name, ty, initializer)) = initialize_visitor.get_result() + && is_integer_const(cx, initializer, 0) + { + let mut applicability = Applicability::MaybeIncorrect; + let span = expr.span.with_hi(arg.span.hi()); - let int_name = match ty.map(Ty::kind) { - // usize or inferred - Some(ty::Uint(UintTy::Usize)) | None => { - span_lint_and_sugg( - cx, - EXPLICIT_COUNTER_LOOP, - span, - &format!("the variable `{name}` is used as a loop counter"), - "consider using", - format!( - "for ({name}, {}) in {}.enumerate()", - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), - ), - applicability, - ); - return; - } - Some(ty::Int(int_ty)) => int_ty.name_str(), - Some(ty::Uint(uint_ty)) => uint_ty.name_str(), - _ => return, - }; + let int_name = match ty.map(Ty::kind) { + // usize or inferred + Some(ty::Uint(UintTy::Usize)) | None => { + span_lint_and_sugg( + cx, + EXPLICIT_COUNTER_LOOP, + span, + &format!("the variable `{name}` is used as a loop counter"), + "consider using", + format!( + "for ({name}, {}) in {}.enumerate()", + snippet_with_applicability(cx, pat.span, "item", &mut applicability), + make_iterator_snippet(cx, arg, &mut applicability), + ), + applicability, + ); + return; + }, + Some(ty::Int(int_ty)) => int_ty.name_str(), + Some(ty::Uint(uint_ty)) => uint_ty.name_str(), + _ => return, + }; - span_lint_and_then( - cx, - EXPLICIT_COUNTER_LOOP, - span, - &format!("the variable `{name}` is used as a loop counter"), - |diag| { - diag.span_suggestion( - span, - "consider using", - format!( - "for ({name}, {}) in (0_{int_name}..).zip({})", - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), - ), - applicability, - ); + span_lint_and_then( + cx, + EXPLICIT_COUNTER_LOOP, + span, + &format!("the variable `{name}` is used as a loop counter"), + |diag| { + diag.span_suggestion( + span, + "consider using", + format!( + "for ({name}, {}) in (0_{int_name}..).zip({})", + snippet_with_applicability(cx, pat.span, "item", &mut applicability), + make_iterator_snippet(cx, arg, &mut applicability), + ), + applicability, + ); - diag.note(format!( - "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" - )); - }, - ); - } + diag.note(format!( + "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" + )); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index a9a9058c93f..d484ce40d78 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::lang_items::LangItem; @@ -23,77 +22,79 @@ pub(super) fn check<'tcx>( let inner_expr = peel_blocks_with_stmt(body); // Check for the specific case that the result is returned and optimize suggestion for that (more // cases can be added later) - if_chain! { - if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr); - if let Some(binding_id) = get_binding(pat); - if let ExprKind::Block(block, _) = then.kind; - if let [stmt] = block.stmts; - if let StmtKind::Semi(semi) = stmt.kind; - if let ExprKind::Ret(Some(ret_value)) = semi.kind; - if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind; - if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome); - if path_res(cx, inner_ret) == Res::Local(binding_id); - if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); - then { - let mut applicability = Applicability::MachineApplicable; - let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); - // Checks if `pat` is a single reference to a binding (`&x`) - let is_ref_to_binding = - matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..))); - // If `pat` is not a binding or a reference to a binding (`x` or `&x`) - // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`) - if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) { - snippet.push_str( - &format!( - ".map(|{}| {})", - snippet_with_applicability(cx, pat.span, "..", &mut applicability), - snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), - )[..], - ); - } - let ty = cx.typeck_results().expr_ty(inner_ret); - if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) { - snippet.push_str( - &format!( - ".find(|{}{}| {})", - "&".repeat(1 + usize::from(is_ref_to_binding)), - snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), - snippet_with_applicability(cx, cond.span, "..", &mut applicability), - )[..], - ); - if is_ref_to_binding { - snippet.push_str(".copied()"); - } - } else { - applicability = Applicability::MaybeIncorrect; - snippet.push_str( - &format!( - ".find(|{}| {})", - snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), - snippet_with_applicability(cx, cond.span, "..", &mut applicability), - )[..], - ); + if let Some(higher::If { + cond, + then, + r#else: None, + }) = higher::If::hir(inner_expr) + && let Some(binding_id) = get_binding(pat) + && let ExprKind::Block(block, _) = then.kind + && let [stmt] = block.stmts + && let StmtKind::Semi(semi) = stmt.kind + && let ExprKind::Ret(Some(ret_value)) = semi.kind + && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind + && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) + && path_res(cx, inner_ret) == Res::Local(binding_id) + && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); + // Checks if `pat` is a single reference to a binding (`&x`) + let is_ref_to_binding = + matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..))); + // If `pat` is not a binding or a reference to a binding (`x` or `&x`) + // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`) + if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) { + snippet.push_str( + &format!( + ".map(|{}| {})", + snippet_with_applicability(cx, pat.span, "..", &mut applicability), + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + )[..], + ); + } + let ty = cx.typeck_results().expr_ty(inner_ret); + if cx + .tcx + .lang_items() + .copy_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + snippet.push_str( + &format!( + ".find(|{}{}| {})", + "&".repeat(1 + usize::from(is_ref_to_binding)), + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + snippet_with_applicability(cx, cond.span, "..", &mut applicability), + )[..], + ); + if is_ref_to_binding { + snippet.push_str(".copied()"); } - // Extends to `last_stmt` to include semicolon in case of `return None;` - let lint_span = span.to(last_stmt.span).to(last_ret.span); - span_lint_and_then( - cx, - MANUAL_FIND, - lint_span, - "manual implementation of `Iterator::find`", - |diag| { - if applicability == Applicability::MaybeIncorrect { - diag.note("you may need to dereference some variables"); - } - diag.span_suggestion( - lint_span, - "replace with an iterator", - snippet, - applicability, - ); - }, + } else { + applicability = Applicability::MaybeIncorrect; + snippet.push_str( + &format!( + ".find(|{}| {})", + snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability), + snippet_with_applicability(cx, cond.span, "..", &mut applicability), + )[..], ); } + // Extends to `last_stmt` to include semicolon in case of `return None;` + let lint_span = span.to(last_stmt.span).to(last_ret.span); + span_lint_and_then( + cx, + MANUAL_FIND, + lint_span, + "manual implementation of `Iterator::find`", + |diag| { + if applicability == Applicability::MaybeIncorrect { + diag.note("you may need to dereference some variables"); + } + diag.span_suggestion(lint_span, "replace with an iterator", snippet, applicability); + }, + ); } } @@ -124,34 +125,30 @@ fn last_stmt_and_ret<'tcx>( if let Some(ret) = block.expr { return Some((last_stmt, ret)); } - if_chain! { - if let [.., snd_last, _] = block.stmts; - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let ExprKind::Ret(Some(ret)) = last_expr.kind; - then { - return Some((snd_last, ret)); - } + if let [.., snd_last, _] = block.stmts + && let StmtKind::Semi(last_expr) = last_stmt.kind + && let ExprKind::Ret(Some(ret)) = last_expr.kind + { + return Some((snd_last, ret)); } } None } let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id); - if_chain! { + if let Some((node_hir, Node::Stmt(..))) = parent_iter.next() // This should be the loop - if let Some((node_hir, Node::Stmt(..))) = parent_iter.next(); // This should be the function body - if let Some((_, Node::Block(block))) = parent_iter.next(); - if let Some((last_stmt, last_ret)) = extract(block); - if last_stmt.hir_id == node_hir; - if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone); - if let Some((_, Node::Expr(_block))) = parent_iter.next(); + && let Some((_, Node::Block(block))) = parent_iter.next() + && let Some((last_stmt, last_ret)) = extract(block) + && last_stmt.hir_id == node_hir + && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone) + && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header - if let Some((_, func)) = parent_iter.next(); - if func.fn_kind().is_some(); - then { - Some((block.stmts.last().unwrap(), last_ret)) - } else { - None - } + && let Some((_, func)) = parent_iter.next() + && func.fn_kind().is_some() + { + Some((block.stmts.last().unwrap(), last_ret)) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index 124a35f8f54..a726b116956 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -3,7 +3,6 @@ use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -21,66 +20,51 @@ pub(super) fn check<'tcx>( span: Span, ) { let inner_expr = peel_blocks_with_stmt(body); - if_chain! { - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) - = higher::IfLet::hir(cx, inner_expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) + = higher::IfLet::hir(cx, inner_expr) // Ensure match_expr in `if let` statement is the same as the pat from the for-loop - if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; - if path_to_local_id(let_expr, pat_hir_id); + && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind + && path_to_local_id(let_expr, pat_hir_id) // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id); - if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); - let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id); - let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id); - if some_ctor || ok_ctor; + && let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind + && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + && let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id) + && let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id) + && (some_ctor || ok_ctor) // Ensure expr in `if let` is not used afterwards - if !is_local_used(cx, if_then, pat_hir_id); - then { - let if_let_type = if some_ctor { "Some" } else { "Ok" }; - // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); + && !is_local_used(cx, if_then, pat_hir_id) + { + let if_let_type = if some_ctor { "Some" } else { "Ok" }; + // Prepare the error message + let msg = + format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); - // Prepare the help message - let mut applicability = Applicability::MaybeIncorrect; - let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); - let copied = match cx.typeck_results().expr_ty(let_expr).kind() { - ty::Ref(_, inner, _) => match inner.kind() { - ty::Ref(..) => ".copied()", - _ => "" - } - _ => "" - }; + // Prepare the help message + let mut applicability = Applicability::MaybeIncorrect; + let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); + let copied = match cx.typeck_results().expr_ty(let_expr).kind() { + ty::Ref(_, inner, _) => match inner.kind() { + ty::Ref(..) => ".copied()", + _ => "", + }, + _ => "", + }; - let sugg = format!("{arg_snippet}{copied}.flatten()"); + let sugg = format!("{arg_snippet}{copied}.flatten()"); - // If suggestion is not a one-liner, it won't be shown inline within the error message. In that case, - // it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs - // to refer to the correct relative position of the suggestion. - let help_msg = if sugg.contains('\n') { - "remove the `if let` statement in the for loop and then..." - } else { - "...and remove the `if let` statement in the for loop" - }; + // If suggestion is not a one-liner, it won't be shown inline within the error message. In that + // case, it will be shown in the extra `help` message at the end, which is why the first + // `help_msg` needs to refer to the correct relative position of the suggestion. + let help_msg = if sugg.contains('\n') { + "remove the `if let` statement in the for loop and then..." + } else { + "...and remove the `if let` statement in the for loop" + }; - span_lint_and_then( - cx, - MANUAL_FLATTEN, - span, - &msg, - |diag| { - diag.span_suggestion( - arg.span, - "try", - sugg, - applicability, - ); - diag.span_help( - inner_expr.span, - help_msg, - ); - } - ); - } + span_lint_and_then(cx, MANUAL_FLATTEN, span, &msg, |diag| { + diag.span_suggestion(arg.span, "try", sugg, applicability); + diag.span_help(inner_expr.span, help_msg); + }); } } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index d3fd0e8639e..40d56240b9d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -4,7 +4,6 @@ use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; @@ -59,22 +58,31 @@ pub(super) fn check<'tcx>( .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); - if_chain! { - if let ExprKind::Index(base_left, idx_left, _) = lhs.kind; - if let ExprKind::Index(base_right, idx_right, _) = rhs.kind; - if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); - if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); - if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); - if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); + if let ExprKind::Index(base_left, idx_left, _) = lhs.kind + && let ExprKind::Index(base_right, idx_right, _) = rhs.kind + && let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)) + && get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some() + && let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts) + && let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts) // Source and destination must be different - if path_to_local(base_left) != path_to_local(base_right); - then { - Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, - IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) - } else { - None - } + && path_to_local(base_left) != path_to_local(base_right) + { + Some(( + ty, + IndexExpr { + base: base_left, + idx: start_left, + idx_offset: offset_left, + }, + IndexExpr { + base: base_right, + idx: start_right, + idx_offset: offset_right, + }, + )) + } else { + None } }) }) @@ -118,23 +126,19 @@ fn build_manual_memcpy_suggestion<'tcx>( } let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { - if_chain! { - if let ExprKind::MethodCall(method, recv, [], _) = end.kind; - if method.ident.name == sym::len; - if path_to_local(recv) == path_to_local(base); - then { - if sugg.to_string() == end_str { - sugg::EMPTY.into() - } else { - sugg - } + if let ExprKind::MethodCall(method, recv, [], _) = end.kind + && method.ident.name == sym::len + && path_to_local(recv) == path_to_local(base) + { + if sugg.to_string() == end_str { + sugg::EMPTY.into() } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &sugg::ONE.into() - }, - ast::RangeLimits::HalfOpen => sugg, - } + sugg + } + } else { + match limits { + ast::RangeLimits::Closed => sugg + &sugg::ONE.into(), + ast::RangeLimits::HalfOpen => sugg, } } }; @@ -174,7 +178,9 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst_base_str = snippet(cx, dst.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???"); - let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { + let dst = if (dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY) + || is_array_length_equal_to_range(cx, start, end, dst.base) + { dst_base_str } else { format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() @@ -186,11 +192,13 @@ fn build_manual_memcpy_suggestion<'tcx>( "clone_from_slice" }; - format!( - "{dst}.{method_str}(&{src_base_str}[{}..{}]);", - src_offset.maybe_par(), - src_limit.maybe_par() - ) + let src = if is_array_length_equal_to_range(cx, start, end, src.base) { + src_base_str + } else { + format!("{src_base_str}[{}..{}]", src_offset.maybe_par(), src_limit.maybe_par()).into() + }; + + format!("{dst}.{method_str}(&{src});") } /// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; @@ -331,10 +339,12 @@ fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Opti } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { - if_chain! { - if let ExprKind::MethodCall(method, arg, [], _) = expr.kind; - if method.ident.name == sym::clone; - then { arg } else { expr } + if let ExprKind::MethodCall(method, arg, [], _) = expr.kind + && method.ident.name == sym::clone + { + arg + } else { + expr } } @@ -446,3 +456,34 @@ fn get_loop_counters<'a, 'tcx>( .into() }) } + +fn is_array_length_equal_to_range(cx: &LateContext<'_>, start: &Expr<'_>, end: &Expr<'_>, arr: &Expr<'_>) -> bool { + fn extract_lit_value(expr: &Expr<'_>) -> Option<u128> { + if let ExprKind::Lit(lit) = expr.kind + && let ast::LitKind::Int(value, _) = lit.node + { + Some(value) + } else { + None + } + } + + let arr_ty = cx.typeck_results().expr_ty(arr).peel_refs(); + + if let ty::Array(_, s) = arr_ty.kind() { + let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) { + size.into() + } else { + return false; + }; + + let range = match (extract_lit_value(start), extract_lit_value(end)) { + (Some(start_value), Some(end_value)) => end_value - start_value, + _ => return false, + }; + + size == range + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs index 7b7d19c753f..e405829b2f4 100644 --- a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs @@ -31,26 +31,30 @@ fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind; - if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind; - if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name); - if let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind(); - if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()); - then { - span_lint_and_sugg( - cx, - MISSING_SPIN_LOOP, - body.span, - "busy-waiting loop should at least have a spin loop hint", - "try", - (if is_no_std_crate(cx) { - "{ core::hint::spin_loop() }" - } else { - "{ std::hint::spin_loop() }" - }).into(), - Applicability::MachineApplicable - ); - } + if let ExprKind::Block( + Block { + stmts: [], expr: None, .. + }, + _, + ) = body.kind + && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind + && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) + && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() + && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + { + span_lint_and_sugg( + cx, + MISSING_SPIN_LOOP, + body.span, + "busy-waiting loop should at least have a spin loop hint", + "try", + (if is_no_std_crate(cx) { + "{ core::hint::spin_loop() }" + } else { + "{ std::hint::spin_loop() }" + }) + .into(), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 2c12d9582d6..227096251a5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -1,7 +1,6 @@ use super::MUT_RANGE_BOUND; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::{get_enclosing_block, higher, path_to_local}; -use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -12,19 +11,17 @@ use rustc_middle::ty; use rustc_span::Span; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { - if_chain! { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - .. - }) = higher::Range::hir(arg); - let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)); - if mut_id_start.is_some() || mut_id_end.is_some(); - then { - let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); - mut_warn_with_span(cx, span_low); - mut_warn_with_span(cx, span_high); - } + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::Range::hir(arg) + && let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)) + && (mut_id_start.is_some() || mut_id_end.is_some()) + { + let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); + mut_warn_with_span(cx, span_low); + mut_warn_with_span(cx, span_high); } } @@ -42,13 +39,11 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> { - if_chain! { - if let Some(hir_id) = path_to_local(bound); - if let Node::Pat(pat) = cx.tcx.hir().get(hir_id); - if let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind; - then { - return Some(hir_id); - } + if let Some(hir_id) = path_to_local(bound) + && let Node::Pat(pat) = cx.tcx.hir().get(hir_id) + && let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind + { + return Some(hir_id); } None } 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 c4af46b8fd3..e2be861a708 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 @@ -4,7 +4,6 @@ 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 if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; @@ -187,15 +186,13 @@ pub(super) fn check<'tcx>( } fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { - if_chain! { - if let ExprKind::MethodCall(method, recv, [], _) = expr.kind; - if method.ident.name == sym::len; - if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind; - if path.segments.len() == 1; - if path.segments[0].ident.name == var; - then { - return true; - } + if let ExprKind::MethodCall(method, recv, [], _) = expr.kind + && method.ident.name == sym::len + && let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind + && path.segments.len() == 1 + && path.segments[0].ident.name == var + { + return true; } false @@ -207,17 +204,15 @@ fn is_end_eq_array_len<'tcx>( limits: ast::RangeLimits, indexed_ty: Ty<'tcx>, ) -> bool { - if_chain! { - if let ExprKind::Lit(lit) = end.kind; - if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind(); - if let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env); - then { - return match limits { - ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), - ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), - }; - } + if let ExprKind::Lit(lit) = end.kind + && let ast::LitKind::Int(end_int, _) = lit.node + && let ty::Array(_, arr_len_const) = indexed_ty.kind() + && let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env) + { + return match limits { + ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), + ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), + }; } false @@ -248,51 +243,49 @@ struct VarVisitor<'a, 'tcx> { impl<'a, 'tcx> VarVisitor<'a, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { - if_chain! { + if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name - if let ExprKind::Path(ref seqpath) = seqexpr.kind; - if let QPath::Resolved(None, seqvar) = *seqpath; - if seqvar.segments.len() == 1; - if is_local_used(self.cx, idx, self.var); - then { - if self.prefer_mutable { - self.indexed_mut.insert(seqvar.segments[0].ident.name); - } - let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); - let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); - match res { - Res::Local(hir_id) => { - let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self - .cx - .tcx - .region_scope_tree(parent_def_id) - .var_scope(hir_id.local_id) - .unwrap(); - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } else { - self.indexed_indirectly - .insert(seqvar.segments[0].ident.name, Some(extent)); - } - return false; // no need to walk further *on the variable* - }, - Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); - } - return false; // no need to walk further *on the variable* - }, - _ => (), - } + && let QPath::Resolved(None, seqvar) = *seqpath + && seqvar.segments.len() == 1 + && is_local_used(self.cx, idx, self.var) + { + if self.prefer_mutable { + self.indexed_mut.insert(seqvar.segments[0].ident.name); + } + let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); + let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); + match res { + Res::Local(hir_id) => { + let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); + let extent = self + .cx + .tcx + .region_scope_tree(parent_def_id) + .var_scope(hir_id.local_id) + .unwrap(); + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } else { + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); + } + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } else { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); + } + return false; // no need to walk further *on the variable* + }, + _ => (), } } true @@ -301,42 +294,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { + if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind // a range index op - if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; - if let Some(trait_id) = self + && let Some(trait_id) = self .cx .typeck_results() .type_dependent_def_id(expr.hir_id) - .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); - if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) - || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); - if !self.check(args_1, args_0, expr); - then { - return; - } + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)) + && ((meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id))) + && !self.check(args_1, args_0, expr) + { + return; } - if_chain! { + if let ExprKind::Index(seqexpr, idx, _) = expr.kind // an index op - if let ExprKind::Index(seqexpr, idx, _) = expr.kind; - if !self.check(idx, seqexpr, expr); - then { - return; - } + && !self.check(idx, seqexpr, expr) + { + return; } - if_chain! { + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind // directly using a variable - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind; - if let Res::Local(local_id) = path.res; - then { - if local_id == self.var { - self.nonindex = true; - } else { - // not the correct variable, but still a variable - self.referenced.insert(path.segments[0].ident.name); - } + && let Res::Local(local_id) = path.res + { + if local_id == self.var { + self.nonindex = true; + } else { + // not the correct variable, but still a variable + self.referenced.insert(path.segments[0].ident.name); } } diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 5fffb27cda2..15e11fd386c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -44,54 +43,50 @@ pub(super) fn check<'tcx>( // Determine whether it is safe to lint the body let mut same_item_push_visitor = SameItemPushVisitor::new(cx); walk_expr(&mut same_item_push_visitor, body); - if_chain! { - if same_item_push_visitor.should_lint(); - if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push; - let vec_ty = cx.typeck_results().expr_ty(vec); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); - if cx + if same_item_push_visitor.should_lint() + && let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push + && let vec_ty = cx.typeck_results().expr_ty(vec) + && let ty = vec_ty.walk().nth(1).unwrap().expect_ty() + && cx .tcx .lang_items() .clone_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])); - then { - // Make sure that the push does not involve possibly mutating values - match pushed_item.kind { - ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, pushed_item.hir_id) { - // immutable bindings that are initialized with literal or constant - Res::Local(hir_id) => { - let node = cx.tcx.hir().get(hir_id); - if_chain! { - if let Node::Pat(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)); - let parent_node = cx.tcx.hir().parent_id(hir_id); - if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let Some(init) = parent_let_expr.init; - then { - match init.kind { - // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), - // immutable bindings that are initialized with constant - ExprKind::Path(ref path) => { - if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item, ctxt); - } - } - _ => {}, + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + match pushed_item.kind { + ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + let node = cx.tcx.hir().get(hir_id); + if let Node::Pat(pat) = node + && let PatKind::Binding(bind_ann, ..) = pat.kind + && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) + && let parent_node = cx.tcx.hir().parent_id(hir_id) + && let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node) + && let Some(init) = parent_let_expr.init + { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { + emit_lint(cx, vec, pushed_item, ctxt); } - } + }, + _ => {}, } - }, - // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), - _ => {}, - } - }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), - _ => {}, - } + } + }, + // constant + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), + _ => {}, + } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + _ => {}, } } } @@ -118,16 +113,14 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { } fn should_lint(&self) -> bool { - if_chain! { - if !self.non_deterministic_expr; - if !self.multiple_pushes; - if let Some((vec, _, _)) = self.vec_push; - if let Some(hir_id) = path_to_local(vec); - then { - !self.used_locals.contains(&hir_id) - } else { - false - } + if !self.non_deterministic_expr + && !self.multiple_pushes + && let Some((vec, _, _)) = self.vec_push + && let Some(hir_id) = path_to_local(vec) + { + !self.used_locals.contains(&hir_id) + } else { + false } } } @@ -180,18 +173,16 @@ fn get_vec_push<'tcx>( cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> { - if_chain! { + if let StmtKind::Semi(semi_stmt) = &stmt.kind // Extract method being called - if let StmtKind::Semi(semi_stmt) = &stmt.kind; - if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind; + && let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind // Figure out the parameters for the method call - if let Some(pushed_item) = args.first(); + && let Some(pushed_item) = args.first() // Check that the method being called is push() on a Vec - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec); - if path.ident.name.as_str() == "push"; - then { - return Some((self_expr, pushed_item, semi_stmt.span.ctxt())) - } + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) + && path.ident.name.as_str() == "push" + { + return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } None } diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index dfb800ccf71..d860b297a02 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -2,7 +2,6 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; use clippy_utils::visitors::contains_break_or_continue; -use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; use rustc_errors::Applicability; @@ -66,36 +65,36 @@ pub(super) fn check<'tcx>( ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), _ => return, }; - if_chain! { - if let ExprKind::Block(block, _) = body.kind; - if !block.stmts.is_empty(); - if !contains_break_or_continue(body); - then { - let mut applicability = Applicability::MachineApplicable; - let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); - let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); - let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); - block_str.remove(0); - block_str.pop(); - let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); + if let ExprKind::Block(block, _) = body.kind + && !block.stmts.is_empty() + && !contains_break_or_continue(body) + { + let mut applicability = Applicability::MachineApplicable; + let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); + let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); + let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); + block_str.remove(0); + block_str.pop(); + let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); - // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. - if !prefix.is_empty() && ( + // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. + if !prefix.is_empty() + && ( // Precedence of internal expression is less than or equal to precedence of `&expr`. arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) - ) { - arg_snip = format!("({arg_snip})").into(); - } - - span_lint_and_sugg( - cx, - SINGLE_ELEMENT_LOOP, - expr.span, - "for loop over a single element", - "try", - format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), - applicability, ) + { + arg_snip = format!("({arg_snip})").into(); } + + span_lint_and_sugg( + cx, + SINGLE_ELEMENT_LOOP, + expr.span, + "for loop over a single element", + "try", + format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), + applicability, + ); } } 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 62a2ab1ccb4..dd7fae79d9b 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 @@ -9,7 +9,7 @@ use rustc_middle::ty; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { - let PatKind::Tuple(tuple, _) = pat.kind else { + let PatKind::Tuple([index, elem], _) = pat.kind else { return; }; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx let ty = cx.typeck_results().expr_ty(arg); - if !pat_is_wild(cx, &tuple[0].kind, body) { + if !pat_is_wild(cx, &index.kind, body) { return; } @@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx diag, "remove the `.enumerate()` call", vec![ - (pat.span, snippet(cx, tuple[1].span, "..").into_owned()), + (pat.span, snippet(cx, elem.span, "..").into_owned()), (arg.span, base_iter.to_string()), ], ); diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 0a2bd89eb3c..38fdca573c6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -1,6 +1,5 @@ use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; -use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; @@ -145,20 +144,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_local(&mut self, l: &'tcx Local<'_>) { // Look for declarations of the variable - if_chain! { - if l.pat.hir_id == self.var_id; - if let PatKind::Binding(.., ident, _) = l.pat.kind; - then { - let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); + if l.pat.hir_id == self.var_id + && let PatKind::Binding(.., ident, _) = l.pat.kind + { + let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); - self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { - InitializeVisitorState::Initialized { - initializer: init, - ty, - name: ident.name, - } - }) - } + self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { + InitializeVisitorState::Initialized { + initializer: init, + ty, + name: ident.name, + } + }); } walk_local(self, l); 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 7f24f3c5dc2..9fd9b7a1631 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 @@ -2,7 +2,6 @@ use super::WHILE_IMMUTABLE_CONDITION; use clippy_utils::consts::constant; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; -use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -95,20 +94,18 @@ struct VarCollectorVisitor<'a, 'tcx> { impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Path(ref qpath) = ex.kind; - if let QPath::Resolved(None, _) = *qpath; - then { - match self.cx.qpath_res(qpath, ex.hir_id) { - Res::Local(hir_id) => { - self.ids.insert(hir_id); - }, - Res::Def(DefKind::Static(_), def_id) => { - let mutable = self.cx.tcx.is_mutable_static(def_id); - self.def_ids.insert(def_id, mutable); - }, - _ => {}, - } + if let ExprKind::Path(ref qpath) = ex.kind + && let QPath::Resolved(None, _) = *qpath + { + match self.cx.qpath_res(qpath, ex.hir_id) { + Res::Local(hir_id) => { + self.ids.insert(hir_id); + }, + Res::Def(DefKind::Static(_), def_id) => { + let mutable = self.cx.tcx.is_mutable_static(def_id); + self.def_ids.insert(def_id, mutable); + }, + _ => {}, } } } 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 5153070cfe6..21b9efba54c 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 @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -15,59 +14,53 @@ use rustc_span::symbol::sym; use rustc_span::Symbol; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { - 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 }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern - if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind; - if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); + && 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) // check for call to `Iterator::next` - if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind; - if method_name.ident.name == sym::next; - if is_trait_method(cx, let_expr, sym::Iterator); - if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr); + && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind + && method_name.ident.name == sym::next + && is_trait_method(cx, let_expr, sym::Iterator) + && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr) // get the loop containing the match expression - if !uses_iter(cx, &iter_expr_struct, if_then); - then { - (let_expr, iter_expr_struct, iter_expr, some_pat, expr) + && !uses_iter(cx, &iter_expr_struct, if_then) + { + let mut applicability = Applicability::MachineApplicable; + 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. + return; + } + snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) } else { - return; - } - }; - - let mut applicability = Applicability::MachineApplicable; - 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. - return; - } - snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) - } else { - "_".into() - }; + "_".into() + }; - // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be - // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used - // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) - || !iter_expr_struct.can_move - || !iter_expr_struct.fields.is_empty() - || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) - { - ".by_ref()" - } else { - "" - }; + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used + // afterwards a mutable borrow of a field isn't necessary. + let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + || !iter_expr_struct.can_move + || !iter_expr_struct.fields.is_empty() + || needs_mutable_borrow(cx, &iter_expr_struct, expr) + { + ".by_ref()" + } else { + "" + }; - let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(scrutinee_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {loop_var} in {iterator}{by_ref}"), - applicability, - ); + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + 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}"), + applicability, + ); + } } #[derive(Debug)] diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 9b158f18f62..9b2e02058a6 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -89,30 +88,26 @@ impl MacroUseImports { impl<'tcx> LateLintPass<'tcx> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if cx.sess().opts.edition >= Edition::Edition2018; - if let hir::ItemKind::Use(path, _kind) = &item.kind; - let hir_id = item.hir_id(); - let attrs = cx.tcx.hir().attrs(hir_id); - if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); - if let Some(id) = path.res.iter().find_map(|res| match res { + if cx.sess().opts.edition >= Edition::Edition2018 + && let hir::ItemKind::Use(path, _kind) = &item.kind + && let hir_id = item.hir_id() + && let attrs = cx.tcx.hir().attrs(hir_id) + && let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)) + && let Some(id) = path.res.iter().find_map(|res| match res { Res::Def(DefKind::Mod, id) => Some(id), _ => None, - }); - if !id.is_local(); - then { - for kid in cx.tcx.module_children(id) { - if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span; - let def_path = cx.tcx.def_path_str(mac_id); - self.imports.push((def_path, span, hir_id)); - } - } - } else { - if item.span.from_expansion() { - self.push_unique_macro_pat_ty(cx, item.span); + }) + && !id.is_local() + { + for kid in cx.tcx.module_children(id) { + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span; + let def_path = cx.tcx.def_path_str(mac_id); + self.imports.push((def_path, span, hir_id)); } } + } else if item.span.from_expansion() { + self.push_unique_macro_pat_ty(cx, item.span); } } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs index 20333c150e3..ea1d25d80e1 100644 --- a/src/tools/clippy/clippy_lints/src/main_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::{is_entrypoint_fn, is_no_std_crate}; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -43,21 +42,19 @@ impl LateLintPass<'_> for MainRecursion { return; } - if_chain! { - if let ExprKind::Call(func, _) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind; - if let Some(def_id) = path.res.opt_def_id(); - if is_entrypoint_fn(cx, def_id); - then { - span_lint_and_help( - cx, - MAIN_RECURSION, - func.span, - &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), - None, - "consider using another function for this recursion" - ) - } + if let ExprKind::Call(func, _) = &expr.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind + && let Some(def_id) = path.res.opt_def_id() + && is_entrypoint_fn(cx, def_id) + { + span_lint_and_help( + cx, + MAIN_RECURSION, + func.span, + &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), + None, + "consider using another function for this recursion", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 998de38a995..a5d91c949bc 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -47,61 +46,57 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { span: Span, def_id: LocalDefId, ) { - if_chain! { - if let Some(header) = kind.header(); - if !header.asyncness.is_async(); + if let Some(header) = kind.header() + && !header.asyncness.is_async() // Check that this function returns `impl Future` - if let FnRetTy::Return(ret_ty) = decl.output; - if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); - if let Some(output) = future_output_ty(trait_ref); - if captures_all_lifetimes(decl.inputs, &output_lifetimes); + && let FnRetTy::Return(ret_ty) = decl.output + && let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty) + && let Some(output) = future_output_ty(trait_ref) + && captures_all_lifetimes(decl.inputs, &output_lifetimes) // Check that the body of the function consists of one async block - if let ExprKind::Block(block, _) = body.value.kind; - if block.stmts.is_empty(); - if let Some(closure_body) = desugared_async_block(cx, block); - if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = - cx.tcx.hir().get_by_def_id(def_id); - then { - let header_span = span.with_hi(ret_ty.span.hi()); - - span_lint_and_then( - cx, - MANUAL_ASYNC_FN, - header_span, - "this function can be simplified using the `async fn` syntax", - |diag| { - if_chain! { - if let Some(vis_snip) = snippet_opt(cx, *vis_span); - if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = position_before_rarrow(&header_snip); - if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); - then { - let header_snip = if vis_snip.is_empty() { - format!("async {}", &header_snip[..ret_pos]) - } else { - format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) - }; - - let help = format!("make the function `async` and {ret_sugg}"); - diag.span_suggestion( - header_span, - help, - format!("{header_snip}{ret_snip}"), - Applicability::MachineApplicable - ); - - let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); - diag.span_suggestion( - block.span, - "move the body of the async block to the enclosing function", - body_snip, - Applicability::MachineApplicable - ); - } - } - }, - ); - } + && let ExprKind::Block(block, _) = body.value.kind + && block.stmts.is_empty() + && let Some(closure_body) = desugared_async_block(cx, block) + && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = + cx.tcx.hir().get_by_def_id(def_id) + { + let header_span = span.with_hi(ret_ty.span.hi()); + + span_lint_and_then( + cx, + MANUAL_ASYNC_FN, + header_span, + "this function can be simplified using the `async fn` syntax", + |diag| { + if let Some(vis_snip) = snippet_opt(cx, *vis_span) + && let Some(header_snip) = snippet_opt(cx, header_span) + && let Some(ret_pos) = position_before_rarrow(&header_snip) + && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output) + { + let header_snip = if vis_snip.is_empty() { + format!("async {}", &header_snip[..ret_pos]) + } else { + format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) + }; + + let help = format!("make the function `async` and {ret_sugg}"); + diag.span_suggestion( + header_span, + help, + format!("{header_snip}{ret_snip}"), + Applicability::MachineApplicable, + ); + + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); + diag.span_suggestion( + block.span, + "move the body of the async block to the enclosing function", + body_snip, + Applicability::MachineApplicable, + ); + } + }, + ); } } } @@ -110,48 +105,44 @@ fn future_trait_ref<'tcx>( cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>, ) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> { - if_chain! { - if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind; - let item = cx.tcx.hir().item(item_id); - if let ItemKind::OpaqueTy(opaque) = &item.kind; - if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { + if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind + && let item = cx.tcx.hir().item(item_id) + && let ItemKind::OpaqueTy(opaque) = &item.kind + && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { if let GenericBound::Trait(poly, _) = bound { Some(&poly.trait_ref) } else { None } - }); - if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); - then { - let output_lifetimes = bounds - .iter() - .filter_map(|bound| { - if let GenericArg::Lifetime(lt) = bound { - Some(lt.res) - } else { - None - } - }) - .collect(); - - return Some((trait_ref, output_lifetimes)); - } + }) + && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait() + { + let output_lifetimes = bounds + .iter() + .filter_map(|bound| { + if let GenericArg::Lifetime(lt) = bound { + Some(lt.res) + } else { + None + } + }) + .collect(); + + return Some((trait_ref, output_lifetimes)); } None } fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { - if_chain! { - if let Some(segment) = trait_ref.path.segments.last(); - if let Some(args) = segment.args; - if args.bindings.len() == 1; - let binding = &args.bindings[0]; - if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; - then { - return Some(output); - } + if let Some(segment) = trait_ref.path.segments.last() + && let Some(args) = segment.args + && args.bindings.len() == 1 + && let binding = &args.bindings[0] + && binding.ident.name == sym::Output + && let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind + { + return Some(output); } None @@ -181,17 +172,15 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) } fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { - if_chain! { - if let Some(block_expr) = block.expr; - if let Expr { + if let Some(block_expr) = block.expr + && let Expr { kind: ExprKind::Closure(&Closure { body, .. }), .. - } = block_expr; - let closure_body = cx.tcx.hir().body(body); - if closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block)); - then { - return Some(closure_body); - } + } = block_expr + && let closure_body = cx.tcx.hir().body(body) + && closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block)) + { + return Some(closure_body); } None diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index cd614c8951c..69c65cf305c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -53,32 +53,30 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { return; } - if_chain! { - if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; - if let BinOpKind::Mul = &bin_op.node; - if !in_external_macro(cx.sess(), expr.span); - let ctxt = expr.span.ctxt(); - if left_expr.span.ctxt() == ctxt; - if right_expr.span.ctxt() == ctxt; - if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); - if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); - if let ExprKind::Lit(lit) = &other_expr.kind; - if let LitKind::Int(8, _) = lit.node; - then { - let mut app = Applicability::MachineApplicable; - let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; - let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); - - span_lint_and_sugg( - cx, - MANUAL_BITS, - expr.span, - "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits", - "consider using", - sugg, - app, - ); - } + if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind + && let BinOpKind::Mul = &bin_op.node + && !in_external_macro(cx.sess(), expr.span) + && let ctxt = expr.span.ctxt() + && left_expr.span.ctxt() == ctxt + && right_expr.span.ctxt() == ctxt + && let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr) + && matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)) + && let ExprKind::Lit(lit) = &other_expr.kind + && let LitKind::Int(8, _) = lit.node + { + let mut app = Applicability::MachineApplicable; + let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; + let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); + + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits", + "consider using", + sugg, + app, + ); } } @@ -98,22 +96,22 @@ fn get_one_size_of_ty<'tcx>( } fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { - if_chain! { - if let ExprKind::Call(count_func, _func_args) = expr.kind; - if let ExprKind::Path(ref count_func_qpath) = count_func.kind; - - if let QPath::Resolved(_, count_func_path) = count_func_qpath; - if let Some(segment_zero) = count_func_path.segments.first(); - if let Some(args) = segment_zero.args; - if let Some(GenericArg::Type(real_ty)) = args.args.first(); - - if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id); - then { - cx.typeck_results().node_args(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty)) - } else { - None - } + if let ExprKind::Call(count_func, _func_args) = expr.kind + && let ExprKind::Path(ref count_func_qpath) = count_func.kind + && let QPath::Resolved(_, count_func_path) = count_func_qpath + && let Some(segment_zero) = count_func_path.segments.first() + && let Some(args) = segment_zero.args + && let Some(GenericArg::Type(real_ty)) = args.args.first() + && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) + { + cx.typeck_results() + .node_args(count_func.hir_id) + .types() + .next() + .map(|resolved_ty| (*real_ty, resolved_ty)) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 170a040d4ae..01eccb56a0a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -5,18 +5,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::{Descend, Visitable}; -use clippy_utils::{is_lint_allowed, pat_and_expr_can_be_question_mark, peel_blocks}; +use clippy_utils::{is_lint_allowed, is_never_expr, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_tool_lint; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; -use std::ops::ControlFlow; use std::slice; declare_clippy_lint! { @@ -51,7 +48,7 @@ declare_clippy_lint! { } impl<'tcx> QuestionMark { - pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { + pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) { if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) { return; } @@ -67,7 +64,7 @@ impl<'tcx> QuestionMark { IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => { if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then) && let Some(if_else) = if_else - && expr_diverges(cx, if_else) + && is_never_expr(cx, if_else).is_some() && let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id) && (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none()) { @@ -91,10 +88,9 @@ impl<'tcx> QuestionMark { return; } let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; - let diverging_arm_opt = arms - .iter() - .enumerate() - .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); + let diverging_arm_opt = arms.iter().enumerate().find(|(_, arm)| { + is_never_expr(cx, arm.body).is_some() && pat_allowed_for_else(cx, arm.pat, check_types) + }); let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; @@ -272,104 +268,6 @@ fn replace_in_pattern( sn_pat.into_owned() } -/// Check whether an expression is divergent. May give false negatives. -fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, - res: ControlFlow<(), Descend>, - } - impl<'tcx> Visitor<'tcx> for V<'_, '_> { - fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { - fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { - if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) { - return ty.is_never(); - } - false - } - - if self.res.is_break() { - return; - } - - // We can't just call is_never on expr and be done, because the type system - // sometimes coerces the ! type to something different before we can get - // our hands on it. So instead, we do a manual search. We do fall back to - // is_never in some places when there is no better alternative. - self.res = match e.kind { - ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()), - ExprKind::Call(call, _) => { - if is_never(self.cx, e) || is_never(self.cx, call) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(Descend::Yes) - } - }, - ExprKind::MethodCall(..) => { - if is_never(self.cx, e) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(Descend::Yes) - } - }, - ExprKind::If(if_expr, if_then, if_else) => { - let else_diverges = if_else.map_or(false, |ex| expr_diverges(self.cx, ex)); - let diverges = - expr_diverges(self.cx, if_expr) || (else_diverges && expr_diverges(self.cx, if_then)); - if diverges { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(Descend::No) - } - }, - ExprKind::Match(match_expr, match_arms, _) => { - let diverges = expr_diverges(self.cx, match_expr) - || match_arms.iter().all(|arm| { - let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(self.cx, g.body())); - guard_diverges || expr_diverges(self.cx, arm.body) - }); - if diverges { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(Descend::No) - } - }, - - // Don't continue into loops or labeled blocks, as they are breakable, - // and we'd have to start checking labels. - ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), - - // Default: descend - _ => ControlFlow::Continue(Descend::Yes), - }; - if let ControlFlow::Continue(Descend::Yes) = self.res { - walk_expr(self, e); - } - } - - fn visit_local(&mut self, local: &'tcx Local<'_>) { - // Don't visit the else block of a let/else statement as it will not make - // the statement divergent even though the else block is divergent. - if let Some(init) = local.init { - self.visit_expr(init); - } - } - - // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx Ty<'tcx>) {} - fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} - fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} - // Avoid monomorphising all `visit_*` functions. - fn visit_nested_item(&mut self, _: ItemId) {} - } - - let mut v = V { - cx, - res: ControlFlow::Continue(Descend::Yes), - }; - expr.visit(&mut v); - v.res.is_break() -} - fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool { // Check whether the pattern contains any bindings, as the // binding might potentially be used in the body. diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 9a9e6af5084..b41bf2d767e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::{multispan_sugg, 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 if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -71,55 +70,61 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { return; } - if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id); - if let ExprKind::Path(target_path) = &target_arg.kind; - then { - let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { - StripKind::Prefix - } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { - StripKind::Suffix - } else { - return; - }; - let target_res = cx.qpath_res(target_path, target_arg.hir_id); - if target_res == Res::Err { - return; - }; - - if_chain! { - if let Res::Local(hir_id) = target_res; - if let Some(used_mutably) = mutated_variables(then, cx); - if used_mutably.contains(&hir_id); - then { - return; - } - } - - let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); - if !strippings.is_empty() { + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) + && let ExprKind::Path(target_path) = &target_arg.kind + { + let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { + StripKind::Prefix + } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { + StripKind::Suffix + } else { + return; + }; + let target_res = cx.qpath_res(target_path, target_arg.hir_id); + if target_res == Res::Err { + return; + }; + + if let Res::Local(hir_id) = target_res + && let Some(used_mutably) = mutated_variables(then, cx) + && used_mutably.contains(&hir_id) + { + return; + } - let kind_word = match strip_kind { - StripKind::Prefix => "prefix", - StripKind::Suffix => "suffix", - }; + let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); + if !strippings.is_empty() { + let kind_word = match strip_kind { + StripKind::Prefix => "prefix", + StripKind::Suffix => "suffix", + }; - let test_span = expr.span.until(then.span); - span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { + let test_span = expr.span.until(then.span); + span_lint_and_then( + cx, + MANUAL_STRIP, + strippings[0], + &format!("stripping a {kind_word} manually"), + |diag| { diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, &format!("try using the `strip_{kind_word}` method"), - vec![(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()))), + vec![( + 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()))), ); - }); - } + }, + ); } } } @@ -129,15 +134,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::MethodCall(_, arg, [], _) = expr.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, method_def_id, &paths::STR_LEN); - then { - Some(arg) - } else { - None - } + if let ExprKind::MethodCall(_, arg, [], _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && match_def_path(cx, method_def_id, &paths::STR_LEN) + { + Some(arg) + } else { + None } } @@ -201,36 +204,38 @@ fn find_stripping<'tcx>( impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - if_chain! { - if is_ref_str(self.cx, ex); - let unref = peel_ref(ex); - if let ExprKind::Index(indexed, index, _) = &unref.kind; - if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index); - if let ExprKind::Path(path) = &indexed.kind; - if self.cx.qpath_res(path, ex.hir_id) == self.target; - then { - match (self.strip_kind, start, end) { - (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start) { - self.results.push(ex.span); - return; - } - }, - (StripKind::Suffix, None, Some(end)) => { - if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; - if let Some(left_arg) = len_arg(self.cx, left); - if let ExprKind::Path(left_path) = &left_arg.kind; - if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target; - if eq_pattern_length(self.cx, self.pattern, right); - then { - self.results.push(ex.span); - return; - } - } - }, - _ => {} - } + if is_ref_str(self.cx, ex) + && let unref = peel_ref(ex) + && let ExprKind::Index(indexed, index, _) = &unref.kind + && let Some(higher::Range { start, end, .. }) = higher::Range::hir(index) + && let ExprKind::Path(path) = &indexed.kind + && self.cx.qpath_res(path, ex.hir_id) == self.target + { + match (self.strip_kind, start, end) { + (StripKind::Prefix, Some(start), None) => { + if eq_pattern_length(self.cx, self.pattern, start) { + self.results.push(ex.span); + return; + } + }, + (StripKind::Suffix, None, Some(end)) => { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + left, + right, + ) = end.kind + && let Some(left_arg) = len_arg(self.cx, left) + && let ExprKind::Path(left_path) = &left_arg.kind + && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target + && eq_pattern_length(self.cx, self.pattern, right) + { + self.results.push(ex.span); + return; + } + }, + _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 817d072b9b3..147e72ea894 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -163,16 +162,14 @@ fn unit_closure<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { - if_chain! { - if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind; - let body = cx.tcx.hir().body(body); - let body_expr = &body.value; - if fn_decl.inputs.len() == 1; - if is_unit_expression(cx, body_expr); - if let Some(binding) = iter_input_pats(fn_decl, body).next(); - then { - return Some((binding, body_expr)); - } + if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind + && let body = cx.tcx.hir().body(body) + && let body_expr = &body.value + && fn_decl.inputs.len() == 1 + && is_unit_expression(cx, body_expr) + && let Some(binding) = iter_input_pats(fn_decl, body).next() + { + return Some((binding, body_expr)); } None } diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index 841c020f2d3..bf035969477 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_res_lang_ctor}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -56,33 +55,31 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { return; }; - if_chain! { - if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _) - if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation - if ok_path.ident.as_str() == "ok"; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); - let ctxt = expr.span.ctxt(); - if let_expr.span.ctxt() == ctxt; - if let_pat.span.ctxt() == ctxt; - then { - let mut applicability = Applicability::MachineApplicable; - let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; - let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; - let sugg = format!( - "{ifwhile} let Ok({some_expr_string}) = {}", - trimmed_ok.trim().trim_end_matches('.'), - ); - span_lint_and_sugg( - cx, - MATCH_RESULT_OK, - expr.span.with_hi(let_expr.span.hi()), - "matching on `Some` with `ok()` is redundant", - &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), - sugg, - applicability, - ); - } + if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _) + && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation + && ok_path.ident.as_str() == "ok" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && let ctxt = expr.span.ctxt() + && let_expr.span.ctxt() == ctxt + && let_pat.span.ctxt() == ctxt + { + let mut applicability = Applicability::MachineApplicable; + let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; + let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; + let sugg = format!( + "{ifwhile} let Ok({some_expr_string}) = {}", + trimmed_ok.trim().trim_end_matches('.'), + ); + span_lint_and_sugg( + cx, + MATCH_RESULT_OK, + expr.span.with_hi(let_expr.span.hi()), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), + sugg, + applicability, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 29b935fb61a..48fc5746b3c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -5,7 +5,6 @@ use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; @@ -40,76 +39,73 @@ fn check_arm<'tcx>( outer_else_body: Option<&'tcx Expr<'tcx>>, ) { let inner_expr = peel_blocks_with_stmt(outer_then_body); - if_chain! { - if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr); - if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { + if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) + && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)), - IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! { + IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) // if there are more than two arms, collapsing would be non-trivial - if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()); // one of the arms must be "wild-like" - if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)); - then { - let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); - Some((scrutinee, then.pat, Some(els.body))) - } else { - None - } + && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) + { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None }, - }; - if outer_pat.span.eq_ctxt(inner_scrutinee.span); + } + && outer_pat.span.eq_ctxt(inner_scrutinee.span) // match expression must be a local binding // match <local> { .. } - if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)); - if !pat_contains_or(inner_then_pat); + && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) + && !pat_contains_or(inner_then_pat) // the binding must come from the pattern of the containing match arm // ..<local>.. => match <local> { .. } - if let (Some(binding_span), is_innermost_parent_pat_struct) - = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); + && let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal - if match (outer_else_body, inner_else_body) { + && match (outer_else_body, inner_else_body) { (None, None) => true, (None, Some(e)) | (Some(e), None) => is_unit_expr(e), (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), - }; + } // the binding must not be used in the if guard - if outer_guard.map_or( + && outer_guard.map_or( true, |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id) - ); + ) // ...or anywhere in the inner expression - if match inner { + && match inner { IfLetOrMatch::IfLet(_, _, body, els) => { !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id)) }, IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), - }; - then { - let msg = format!( - "this `{}` can be collapsed into the outer `{}`", - if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, - if outer_is_match { "match" } else { "if let" }, - ); - // collapsing patterns need an explicit field name in struct pattern matching - // ex: Struct {x: Some(1)} - let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } + { + let msg = format!( + "this `{}` can be collapsed into the outer `{}`", + if matches!(inner, IfLetOrMatch::Match(..)) { + "match" } else { - String::new() - }; - span_lint_and_then( - cx, - COLLAPSIBLE_MATCH, - inner_expr.span, - &msg, - |diag| { - let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); - help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); - diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); - }, + "if let" + }, + if outer_is_match { "match" } else { "if let" }, + ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; + span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, &msg, |diag| { + let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); + help_span.push_span_label(binding_span, "replace this binding"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); + diag.span_help( + help_span, + "the outer pattern can be modified to include the inner pattern", ); - } + }); } } diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs index 3329f93b73c..c8a48246e67 100644 --- a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -8,38 +8,35 @@ use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { - if_chain! { - if !local.span.from_expansion(); - if let Some(expr) = local.init; - if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind; - if arms.len() == 1 && arms[0].guard.is_none(); - if let PatKind::TupleStruct( - QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; - if args.len() == 1; - if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; - let body = peel_blocks(arms[0].body); - if path_to_local_id(body, arg); - - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - INFALLIBLE_DESTRUCTURING_MATCH, - local.span, - "you seem to be trying to use `match` to destructure a single infallible pattern. \ - Consider using `let`", - "try", - format!( - "let {}({}{}) = {};", - snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), - if binding.0 == ByRef::Yes { "ref " } else { "" }, - snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), - snippet_with_applicability(cx, target.span, "..", &mut applicability), - ), - applicability, - ); - return true; - } + if !local.span.from_expansion() + && let Some(expr) = local.init + && let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind + && arms.len() == 1 + && arms[0].guard.is_none() + && let PatKind::TupleStruct(QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind + && args.len() == 1 + && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind + && let body = peel_blocks(arms[0].body) + && path_to_local_id(body, arg) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + INFALLIBLE_DESTRUCTURING_MATCH, + local.span, + "you seem to be trying to use `match` to destructure a single infallible pattern. \ + Consider using `let`", + "try", + format!( + "let {}({}{}) = {};", + snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, + snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), + snippet_with_applicability(cx, target.span, "..", &mut applicability), + ), + applicability, + ); + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index cdb51c33aaf..619ec83127a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -21,19 +21,19 @@ fn get_cond_expr<'tcx>( expr: &'tcx Expr<'_>, ctxt: SyntaxContext, ) -> Option<SomeExpr<'tcx>> { - if_chain! { - if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); - if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; - if let PatKind::Binding(_,target, ..) = pat.kind; - if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) - || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` - then { - return Some(SomeExpr { - expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), - needs_unsafe_block: contains_unsafe_block(cx, expr), - needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond - }) - } + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr) + && let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind + && let PatKind::Binding(_, target, ..) = pat.kind + && (is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) + || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr)) + // check that one expr resolves to `Some(x)`, the other to `None` + { + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: contains_unsafe_block(cx, expr), + needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the + * cond */ + }); }; None } 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 b94501bf0ad..3e79cabd795 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 @@ -4,7 +4,6 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{is_res_lang_ctor, path_to_local_id, sugg}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{OptionNone, ResultErr}; @@ -16,65 +15,57 @@ use super::MANUAL_UNWRAP_OR; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { let ty = cx.typeck_results().expr_ty(scrutinee); - if_chain! { - if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None - }; - if let Some(or_arm) = applicable_or_arm(cx, arms); - if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); - if let Some(indent) = indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); - then { - let reindented_or_body = - reindent_multiline(or_body_snippet.into(), true, Some(indent)); + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { + Some("Option") + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + Some("Result") + } else { + None + } && let Some(or_arm) = applicable_or_arm(cx, arms) + && let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span) + && let Some(indent) = indent_of(cx, expr.span) + && constant_simple(cx, cx.typeck_results(), or_arm.body).is_some() + { + let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let mut app = Applicability::MachineApplicable; - let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{ty_name}::unwrap_or`"), - "replace with", - format!( - "{suggestion}.unwrap_or({reindented_or_body})", - ), - app, - ); - } + let mut app = Applicability::MachineApplicable; + let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, + expr.span, + &format!("this pattern reimplements `{ty_name}::unwrap_or`"), + "replace with", + format!("{suggestion}.unwrap_or({reindented_or_body})",), + app, + ); } } fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { - if_chain! { - if arms.len() == 2; - if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { - match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr), - _ => false, - } - }); - let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id); - if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); - if cx.tcx.lang_items().option_some_variant() == Some(variant_id) - || cx.tcx.lang_items().result_ok_variant() == Some(variant_id); - if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; - if path_to_local_id(unwrap_arm.body, binding_hir_id); - if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); - if !contains_return_break_continue_macro(or_arm.body); - then { - Some(or_arm) - } else { - None - } + if arms.len() == 2 + && arms.iter().all(|arm| arm.guard.is_none()) + && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::TupleStruct(ref qpath, [pat], _) => { + matches!(pat.kind, PatKind::Wild) + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + }, + _ => false, + }) + && let unwrap_arm = &arms[1 - idx] + && let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind + && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + && (cx.tcx.lang_items().option_some_variant() == Some(variant_id) + || cx.tcx.lang_items().result_ok_variant() == Some(variant_id)) + && let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind + && path_to_local_id(unwrap_arm.body, binding_hir_id) + && cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty() + && !contains_return_break_continue_macro(or_arm.body) + { + Some(or_arm) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 781ee138c76..0627e458dfe 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -127,32 +127,30 @@ where let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.eq_ctxt(some_expr.expr.span); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return None; - } + if !some_expr.needs_unsafe_block + && let Some(func) = can_pass_as_func(cx, id, some_expr.expr) + && func.span.eq_ctxt(some_expr.expr.span) + { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::MUT) { - "mut " - } else { - "" - }; + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " + } else { + "" + }; - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {closure_expr_snip}") - } + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") } } } else if !is_wild_none && explicit_ref.is_none() { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index d51cca040a2..3f737da92c0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -26,18 +26,16 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let output_ty = cx.typeck_results().expr_ty(expr); let input_ty = cx.typeck_results().expr_ty(ex); - let cast = if_chain! { - if let ty::Adt(_, args) = input_ty.kind(); - let input_ty = args.type_at(0); - if let ty::Adt(_, args) = output_ty.kind(); - let output_ty = args.type_at(0); - if let ty::Ref(_, output_ty, _) = *output_ty.kind(); - if input_ty != output_ty; - then { - ".map(|x| x as _)" - } else { - "" - } + let cast = if let ty::Adt(_, args) = input_ty.kind() + && let input_ty = args.type_at(0) + && let ty::Adt(_, args) = output_ty.kind() + && let output_ty = args.type_at(0) + && let ty::Ref(_, output_ty, _) = *output_ty.kind() + && input_ty != output_ty + { + ".map(|x| x as _)" + } else { + "" }; let mut applicability = Applicability::MachineApplicable; @@ -67,17 +65,16 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> { - if_chain! { - if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; - if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome); - if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind; - if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind; - if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome); - if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind; - if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; - then { - return Some(mutabl) - } + if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) + && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind + && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind + && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) + && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind + && path2.segments.len() == 1 + && ident.name == path2.segments[0].ident.name + { + return Some(mutabl); } None } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index e2ddf11abe2..56123326fe4 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -76,79 +76,80 @@ where ), >, { - if_chain! { - if !span_contains_comment(cx.sess().source_map(), expr.span); - if iter.len() >= 2; - if cx.typeck_results().expr_ty(expr).is_bool(); - if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back(); - let iter_without_last = iter.clone(); - if let Some((first_attrs, _, first_expr, first_guard)) = iter.next(); - if let Some(b0) = find_bool_lit(&first_expr.kind); - if let Some(b1) = find_bool_lit(&last_expr.kind); - if b0 != b1; - if first_guard.is_none() || iter.len() == 0; - if first_attrs.is_empty(); - if iter - .all(|arm| { - find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty() - }); - then { - if let Some(last_pat) = last_pat_opt { - if !is_wild(last_pat) { - return false; - } + if !span_contains_comment(cx.sess().source_map(), expr.span) + && iter.len() >= 2 + && cx.typeck_results().expr_ty(expr).is_bool() + && let Some((_, last_pat_opt, last_expr, _)) = iter.next_back() + && let iter_without_last = iter.clone() + && let Some((first_attrs, _, first_expr, first_guard)) = iter.next() + && let Some(b0) = find_bool_lit(&first_expr.kind) + && let Some(b1) = find_bool_lit(&last_expr.kind) + && b0 != b1 + && (first_guard.is_none() || iter.len() == 0) + && first_attrs.is_empty() + && iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + { + if let Some(last_pat) = last_pat_opt { + if !is_wild(last_pat) { + return false; } + } - for arm in iter_without_last.clone() { - if let Some(pat) = arm.1 { - if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { - return false; - } + for arm in iter_without_last.clone() { + if let Some(pat) = arm.1 { + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { + return false; } } + } - // The suggestion may be incorrect, because some arms can have `cfg` attributes - // evaluated into `false` and so such arms will be stripped before. - let mut applicability = Applicability::MaybeIncorrect; - let pat = { - use itertools::Itertools as _; - iter_without_last - .filter_map(|arm| { - let pat_span = arm.1?.span; - Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) - }) - .join(" | ") - }; - let pat_and_guard = if let Some(Guard::If(g)) = first_guard { - format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability)) - } else { - pat - }; - - // strip potential borrows (#6503), but only if the type is a reference - let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { - ex_new = ex_inner; - } - }; - span_lint_and_sugg( - cx, - MATCH_LIKE_MATCHES_MACRO, - expr.span, - &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), - "try", - format!( - "{}matches!({}, {pat_and_guard})", - if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - ), - applicability, - ); - true + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; + let pat = { + use itertools::Itertools as _; + iter_without_last + .filter_map(|arm| { + let pat_span = arm.1?.span; + Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) + }) + .join(" | ") + }; + let pat_and_guard = if let Some(Guard::If(g)) = first_guard { + format!( + "{pat} if {}", + snippet_with_applicability(cx, g.span, "..", &mut applicability) + ) } else { - false - } + pat + }; + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = ex; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { + if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { + ex_new = ex_inner; + } + }; + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + &format!( + "{} expression looks like `matches!` macro", + if is_if_let { "if let .. else" } else { "match" } + ), + "try", + format!( + "{}matches!({}, {pat_and_guard})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs index bd53ebd48c8..dd71560e169 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -10,39 +9,29 @@ use rustc_span::sym; use super::MATCH_ON_VEC_ITEMS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { - if_chain! { - if let Some(idx_expr) = is_vec_indexing(cx, scrutinee); - if let ExprKind::Index(vec, idx, _) = idx_expr.kind; - - then { - // FIXME: could be improved to suggest surrounding every pattern with Some(_), - // but only when `or_patterns` are stabilized. - span_lint_and_sugg( - cx, - MATCH_ON_VEC_ITEMS, - scrutinee.span, - "indexing into a vector may panic", - "try", - format!( - "{}.get({})", - snippet(cx, vec.span, ".."), - snippet(cx, idx.span, "..") - ), - Applicability::MaybeIncorrect - ); - } + if let Some(idx_expr) = is_vec_indexing(cx, scrutinee) + && let ExprKind::Index(vec, idx, _) = idx_expr.kind + { + // FIXME: could be improved to suggest surrounding every pattern with Some(_), + // but only when `or_patterns` are stabilized. + span_lint_and_sugg( + cx, + MATCH_ON_VEC_ITEMS, + scrutinee.span, + "indexing into a vector may panic", + "try", + format!("{}.get({})", snippet(cx, vec.span, ".."), snippet(cx, idx.span, "..")), + Applicability::MaybeIncorrect, + ); } } fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::Index(array, index, _) = expr.kind; - if is_vector(cx, array); - if !is_full_range(cx, index); - - then { - return Some(expr); - } + if let ExprKind::Index(array, index, _) = expr.kind + && is_vector(cx, array) + && !is_full_range(cx, index) + { + return Some(expr); } None diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 6fc79faddbe..745be671b86 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -66,25 +66,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let mut local_map: HirIdMap<HirId> = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - if_chain! { - if let Some(a_id) = path_to_local(a); - if let Some(b_id) = path_to_local(b); - let entry = match local_map.entry(a_id) { + if let Some(a_id) = path_to_local(a) + && let Some(b_id) = path_to_local(b) + && let entry = match local_map.entry(a_id) { HirIdMapEntry::Vacant(entry) => entry, // check if using the same bindings as before HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id, - }; - // the names technically don't have to match; this makes the lint more conservative - if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id); - if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b); - if pat_contains_local(lhs.pat, a_id); - if pat_contains_local(rhs.pat, b_id); - then { - entry.insert(b_id); - true - } else { - false } + // the names technically don't have to match; this makes the lint more conservative + && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) + && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) + && pat_contains_local(lhs.pat, a_id) + && pat_contains_local(rhs.pat, b_id) + { + entry.insert(b_id); + true + } else { + false } }; // Arms with a guard are ignored, those can’t always be merged together diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 675a85ae555..bd38648bcf1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -20,21 +20,16 @@ enum CaseMethod { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { - if_chain! { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind(); - if let ty::Str = ty.kind(); - then { - let mut visitor = MatchExprVisitor { - cx, - case_method: None, - }; - - visitor.visit_expr(scrutinee); - - if let Some(case_method) = visitor.case_method { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind() + && let ty::Str = ty.kind() + { + let mut visitor = MatchExprVisitor { cx, case_method: None }; + + visitor.visit_expr(scrutinee); + + if let Some(case_method) = visitor.case_method { + if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } @@ -88,17 +83,15 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if_chain! { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), - .. - }) = arm.pat.kind; - if let LitKind::Str(symbol, _) = lit.node; - let input = symbol.as_str(); - if !case_check(input); - then { - return Some((lit.span, symbol)); - } + if let PatKind::Lit(Expr { + kind: ExprKind::Lit(lit), + .. + }) = arm.pat.kind + && let LitKind::Str(symbol, _) = lit.node + && let input = symbol.as_str() + && !case_check(input) + { + return Some((lit.span, symbol)); } } 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 a2903e52ae0..8a4c0ab9062 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 @@ -34,20 +34,19 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' } } } - if_chain! { - if matching_wild; - if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span); - if is_panic(cx, macro_call.def_id); - then { - // `Err(_)` or `Err(_e)` arm with `panic!` found - span_lint_and_note(cx, - MATCH_WILD_ERR_ARM, - arm.pat.span, - &format!("`Err({ident_bind_name})` matches all errors"), - None, - "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", - ); - } + if matching_wild + && let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span) + && is_panic(cx, macro_call.def_id) + { + // `Err(_)` or `Err(_e)` arm with `panic!` found + span_lint_and_note( + cx, + MATCH_WILD_ERR_ARM, + arm.pat.span, + &format!("`Err({ident_bind_name})` matches all errors"), + None, + "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", + ); } } } 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 8f0083f812c..8199366d175 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -150,7 +150,7 @@ where #[test] fn test_overlapping() { - use rustc_span::source_map::DUMMY_SP; + use rustc_span::DUMMY_SP; let sp = |s, e| SpannedRange { span: DUMMY_SP, diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 9a7c00823b6..2582f7edcf6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -5,7 +5,6 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr}; use clippy_utils::{higher, is_expn_of, is_trait_method}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -35,15 +34,13 @@ pub(super) fn check_if_let<'tcx>( // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> { - if_chain! { - if let ty::Adt(_, subs) = ty.kind(); - if let Some(sub) = subs.get(index); - if let GenericArgKind::Type(sub_ty) = sub.unpack(); - then { - Some(sub_ty) - } else { - None - } + if let ty::Adt(_, subs) = ty.kind() + && let Some(sub) = subs.get(index) + && let GenericArgKind::Type(sub_ty) = sub.unpack() + { + Some(sub_ty) + } else { + None } } @@ -142,14 +139,12 @@ fn find_sugg_for_if_let<'tcx>( let needs_drop = needs_ordered_drop(cx, check_ty) || any_temporaries_need_ordered_drop(cx, let_expr); // check that `while_let_on_iterator` lint does not trigger - if_chain! { - if keyword == "while"; - if let ExprKind::MethodCall(method_path, ..) = let_expr.kind; - if method_path.ident.name == sym::next; - if is_trait_method(cx, let_expr, sym::Iterator); - then { - return; - } + if keyword == "while" + && let ExprKind::MethodCall(method_path, ..) = let_expr.kind + && method_path.ident.name == sym::next + && is_trait_method(cx, let_expr, sym::Iterator) + { + return; } let result_expr = match &let_expr.kind { 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 4efe93d4b54..316b2f63e4e 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 @@ -6,25 +6,22 @@ use rustc_middle::ty; use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { - if_chain! { - if !pat.span.from_expansion(); - if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind; - if let Some(def_id) = path.res.opt_def_id(); - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - if let ty::Adt(def, _) = ty.kind(); - if def.is_struct() || def.is_union(); - if fields.len() == def.non_enum_variant().fields.len(); - if !def.non_enum_variant().is_field_list_non_exhaustive(); - - then { - span_lint_and_help( - 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", - ); - } + if !pat.span.from_expansion() + && let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind + && let Some(def_id) = path.res.opt_def_id() + && let ty = cx.tcx.type_of(def_id).instantiate_identity() + && let ty::Adt(def, _) = ty.kind() + && (def.is_struct() || def.is_union()) + && fields.len() == def.non_enum_variant().fields.len() + && !def.non_enum_variant().is_field_list_non_exhaustive() + { + span_lint_and_help( + 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", + ); } } 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 48efd023017..86c414dafcc 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -89,50 +89,52 @@ fn report_single_pattern( }); let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); - let (msg, sugg) = if_chain! { - if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; - let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); - if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait(); - if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait(); - if ty.is_integral() || ty.is_char() || ty.is_str() - || (implements_trait(cx, ty, spe_trait_id, &[]) - && implements_trait(cx, ty, pe_trait_id, &[ty.into()])); - then { - // scrutinee derives PartialEq and the pattern is a constant. - let pat_ref_count = match pat.kind { - // string literals are already a reference. - PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1, - _ => pat_ref_count, - }; - // References are only implicitly added to the pattern, so no overflow here. - // e.g. will work: match &Some(_) { Some(_) => () } - // will not: match Some(_) { &Some(_) => () } - let ref_count_diff = ty_ref_count - pat_ref_count; - - // Try to remove address of expressions first. - let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); - let ref_count_diff = ref_count_diff - removed; - - let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; - let sugg = format!( - "if {} == {}{} {}{els_str}", - 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), - ); - (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, ex.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), - ); - (msg, sugg) - } + 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 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() + || ty.is_char() + || ty.is_str() + || (implements_trait(cx, ty, spe_trait_id, &[]) && implements_trait(cx, ty, pe_trait_id, &[ty.into()]))) + { + // scrutinee derives PartialEq and the pattern is a constant. + let pat_ref_count = match pat.kind { + // string literals are already a reference. + PatKind::Lit(Expr { + kind: ExprKind::Lit(lit), + .. + }) if lit.node.is_str() => pat_ref_count + 1, + _ => pat_ref_count, + }; + // References are only implicitly added to the pattern, so no overflow here. + // e.g. will work: match &Some(_) { Some(_) => () } + // will not: match Some(_) { &Some(_) => () } + let ref_count_diff = ty_ref_count - pat_ref_count; + + // Try to remove address of expressions first. + let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); + let ref_count_diff = ref_count_diff - removed; + + let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; + let sugg = format!( + "if {} == {}{} {}{els_str}", + 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), + ); + (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, ex.span, ".."), + expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + ); + (msg, sugg) }; span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); 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 0fd6f533db0..dd489fc250b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; 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}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -22,59 +21,57 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine // #[allow(unreachable_code)] // val, // }; - if_chain! { - if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind; - if let ExprKind::Path(ref match_fun_path) = match_fun.kind; - if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); - if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind; - if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr); - if let Some(return_ty) = find_return_type(cx, &expr.kind); - then { - let prefix; - let suffix; - let err_ty; + if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind + && let ExprKind::Path(ref match_fun_path) = match_fun.kind + && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) + && let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind + && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) + && let Some(return_ty) = find_return_type(cx, &expr.kind) + { + let prefix; + let suffix; + let err_ty; - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; - } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; - } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; - } else { - return; - }; + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + 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}") - }; + 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( - cx, - TRY_ERR, - expr.span, - "returning an `Err(_)` with the `?` operator", - "try", - suggestion, - applicability, - ); - } + span_lint_and_sugg( + cx, + TRY_ERR, + expr.span, + "returning an `Err(_)` with the `?` operator", + "try", + suggestion, + applicability, + ); } } @@ -92,51 +89,42 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O /// Extracts the error type from Result<T, E>. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - if_chain! { - if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym::Result); - then { - Some(subst.type_at(1)) - } else { - None - } + if let ty::Adt(_, subst) = ty.kind() + && is_type_diagnostic_item(cx, ty, sym::Result) + { + Some(subst.type_at(1)) + } else { + None } } /// Extracts the error type from Poll<Result<T, E>>. fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); - then { - Some(ready_subst.type_at(1)) - } else { - None - } + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) + && let ready_ty = subst.type_at(0) + && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()) + { + Some(ready_subst.type_at(1)) + } else { + None } } /// Extracts the error type from Poll<Option<Result<T, E>>>. fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); - let some_ty = ready_subst.type_at(0); - - if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); - then { - Some(some_subst.type_at(1)) - } else { - None - } + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) + && let ready_ty = subst.type_at(0) + && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()) + && let some_ty = ready_subst.type_at(0) + && let ty::Adt(some_def, some_subst) = some_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, some_def.did()) + { + Some(some_subst.type_at(1)) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 760c8f59cec..1517223ab9a 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -4,15 +4,14 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -125,17 +124,37 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_sp } fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if_chain! { + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id) // check if replacement is mem::MaybeUninit::uninit().assume_init() - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id); - if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id); - then { + && cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::MaybeUninit::uninit().assume_init()`", + "consider using", + format!( + "std::ptr::read({})", + snippet_with_applicability(cx, dest.span, "", &mut applicability) + ), + applicability, + ); + return; + } + + if let ExprKind::Call(repl_func, []) = src.kind + && let ExprKind::Path(ref repl_func_qpath) = repl_func.kind + && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() + { + if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, MEM_REPLACE_WITH_UNINIT, expr_span, - "replacing with `mem::MaybeUninit::uninit().assume_init()`", + "replacing with `mem::uninitialized()`", "consider using", format!( "std::ptr::read({})", @@ -143,40 +162,17 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' ), applicability, ); - return; - } - } - - if_chain! { - if let ExprKind::Call(repl_func, []) = src.kind; - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - then { - if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MEM_REPLACE_WITH_UNINIT, - expr_span, - "replacing with `mem::uninitialized()`", - "consider using", - format!( - "std::ptr::read({})", - snippet_with_applicability(cx, dest.span, "", &mut applicability) - ), - applicability, - ); - } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) && - !cx.typeck_results().expr_ty(src).is_primitive() { - span_lint_and_help( - cx, - MEM_REPLACE_WITH_UNINIT, - expr_span, - "replacing with `mem::zeroed()`", - None, - "consider using a default value or the `take_mut` crate instead", - ); - } + } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) + && !cx.typeck_results().expr_ty(src).is_primitive() + { + span_lint_and_help( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::zeroed()`", + None, + "consider using a default value or the `take_mut` crate instead", + ); } } } @@ -222,21 +218,19 @@ impl MemReplace { impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { + if let ExprKind::Call(func, [dest, src]) = expr.kind // Check that `expr` is a call to `mem::replace()` - if let ExprKind::Call(func, [dest, src]) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id); - then { - // Check that second argument is `Option::None` - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { - check_replace_option_with_none(cx, dest, expr.span); - } else if self.msrv.meets(msrvs::MEM_TAKE) { - check_replace_with_default(cx, src, dest, expr.span); - } - check_replace_with_uninit(cx, src, dest, expr.span); + && let ExprKind::Path(ref func_qpath) = func.kind + && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_replace, def_id) + { + // Check that second argument is `Option::None` + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + check_replace_option_with_none(cx, dest, expr.span); + } else if self.msrv.meets(msrvs::MEM_TAKE) { + check_replace_with_default(cx, src, dest, expr.span); } + check_replace_with_uninit(cx, src, dest, expr.span); } } extract_msrv_attr!(LateContext); 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 3a8cc41748e..08bfa2e009b 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 @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::visitors::find_all_ret_expressions; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -70,57 +69,50 @@ pub(crate) trait BindInsteadOfMap { closure_expr: &hir::Expr<'_>, closure_args_span: Span, ) -> bool { - if_chain! { - if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind; - if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind; - if Self::is_variant(cx, path.res); - if !contains_return(inner_expr); - if let Some(msg) = Self::lint_msg(cx); - then { - let mut app = Applicability::MachineApplicable; - let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; - - let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, recv.span, ".."); - let note = format!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME); - span_lint_and_sugg( - cx, - BIND_INSTEAD_OF_MAP, - expr.span, - &msg, - "try", - note, - app, - ); - true - } else { - false - } + if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind + && Self::is_variant(cx, path.res) + && !contains_return(inner_expr) + && let Some(msg) = Self::lint_msg(cx) + { + let mut app = Applicability::MachineApplicable; + let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; + + let closure_args_snip = snippet(cx, closure_args_span, ".."); + let option_snip = snippet(cx, recv.span, ".."); + let note = format!( + "{option_snip}.{}({closure_args_snip} {some_inner_snip})", + Self::GOOD_METHOD_NAME + ); + span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, "try", note, app); + true + } else { + false } } fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { - if_chain! { - if !ret_expr.span.from_expansion(); - if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind; - if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind; - if Self::is_variant(cx, path.res); - if !contains_return(arg); - then { - suggs.push((ret_expr.span, arg.span.source_callsite())); - true - } else { - false - } + if !ret_expr.span.from_expansion() + && let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind + && Self::is_variant(cx, path.res) + && !contains_return(arg) + { + suggs.push((ret_expr.span, arg.span.source_callsite())); + true + } else { + false } }); - let (span, msg) = if_chain! { - if can_sugg; - if let hir::ExprKind::MethodCall(segment, ..) = expr.kind; - if let Some(msg) = Self::lint_msg(cx); - then { (segment.ident.span, msg) } else { return false; } + let (span, msg) = if can_sugg + && let hir::ExprKind::MethodCall(segment, ..) = expr.kind + && let Some(msg) = Self::lint_msg(cx) + { + (segment.ident.span, msg) + } else { + return false; }; span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| { multispan_sugg_with_applicability( @@ -139,11 +131,12 @@ pub(crate) trait BindInsteadOfMap { /// Lint use of `_.and_then(|x| Some(y))` for `Option`s fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { - if_chain! { - if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); - if let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM); - if adt.did() == cx.tcx.parent(vid); - then {} else { return false; } + if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) + && adt.did() == cx.tcx.parent(vid) + { + } else { + return false; } match arg.kind { diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs index 35370355f83..4a2124c74a8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs @@ -3,7 +3,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -18,53 +17,50 @@ pub(super) fn check<'tcx>( filter_recv: &'tcx Expr<'_>, filter_arg: &'tcx Expr<'_>, ) { - if_chain! { - if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind; - let body = cx.tcx.hir().body(body); - if let [param] = body.params; - if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind; - if let ExprKind::Binary(ref op, l, r) = body.value.kind; - if op.node == BinOpKind::Eq; - if is_type_diagnostic_item(cx, - cx.typeck_results().expr_ty(filter_recv).peel_refs(), - sym::SliceIter); - let operand_is_arg = |expr| { + if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind + && let body = cx.tcx.hir().body(body) + && let [param] = body.params + && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind + && let ExprKind::Binary(ref op, l, r) = body.value.kind + && op.node == BinOpKind::Eq + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter) + && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); path_to_local_id(expr, arg_id) - }; - let needle = if operand_is_arg(l) { + }) + && let needle = if operand_is_arg(l) { r } else if operand_is_arg(r) { l } else { return; - }; - if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); - if !is_local_used(cx, needle, arg_id); - then { - let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = - filter_recv.kind { - let p = path.ident.name; - if p == sym::iter || p == sym::iter_mut { - receiver - } else { - filter_recv - } + } + && ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind() + && !is_local_used(cx, needle, arg_id) + { + let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = filter_recv.kind { + let p = path.ident.name; + if p == sym::iter || p == sym::iter_mut { + receiver } else { filter_recv - }; - let mut applicability = Applicability::MaybeIncorrect; - span_lint_and_sugg( - cx, - NAIVE_BYTECOUNT, - expr.span, - "you appear to be counting bytes the naive way", - "consider using the bytecount crate", - format!("bytecount::count({}, {})", - snippet_with_applicability(cx, haystack.span, "..", &mut applicability), - snippet_with_applicability(cx, needle.span, "..", &mut applicability)), - applicability, - ); - } + } + } else { + filter_recv + }; + let mut applicability = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + NAIVE_BYTECOUNT, + expr.span, + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", + format!( + "bytecount::count({}, {})", + snippet_with_applicability(cx, haystack.span, "..", &mut applicability), + snippet_with_applicability(cx, needle.span, "..", &mut applicability) + ), + applicability, + ); }; } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs index 649fc46e4ad..34159f2d150 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,23 +13,24 @@ pub(super) fn check<'tcx>( count_recv: &'tcx hir::Expr<'_>, bytes_recv: &'tcx hir::Expr<'_>, ) { - if_chain! { - if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); - if cx.tcx.type_of(impl_id).instantiate_identity().is_str(); - let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); - if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_COUNT_TO_LEN, - expr.span, - "using long and hard to read `.bytes().count()`", - "consider calling `.len()` instead", - format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)), - applicability - ); - } + if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(bytes_id) + && cx.tcx.type_of(impl_id).instantiate_identity().is_str() + && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() + && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!( + "{}.len()", + snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability) + ), + applicability, + ); }; } diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index d5897822eda..a37087d0abf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_lang_item; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -27,51 +26,54 @@ pub(super) fn check<'tcx>( } } - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).instantiate_identity().is_str(); - if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind; - if (2..=6).contains(&ext_literal.as_str().len()); - let ext_str = ext_literal.as_str(); - if ext_str.starts_with('.'); - if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) - || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); - let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); - then { - span_lint_and_then( - cx, - CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - recv.span.to(call_span), - "case-sensitive file extension comparison", - |diag| { - diag.help("consider using a case-insensitive comparison instead"); - if let Some(mut recv_source) = snippet_opt(cx, recv.span) { - - if !cx.typeck_results().expr_ty(recv).is_ref() { - recv_source = format!("&{recv_source}"); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && cx.tcx.type_of(impl_id).instantiate_identity().is_str() + && let ExprKind::Lit(Spanned { + node: LitKind::Str(ext_literal, ..), + .. + }) = arg.kind + && (2..=6).contains(&ext_literal.as_str().len()) + && let ext_str = ext_literal.as_str() + && ext_str.starts_with('.') + && (ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) + && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() + && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) + { + span_lint_and_then( + cx, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + recv.span.to(call_span), + "case-sensitive file extension comparison", + |diag| { + diag.help("consider using a case-insensitive comparison instead"); + if let Some(mut recv_source) = snippet_opt(cx, recv.span) { + if !cx.typeck_results().expr_ty(recv).is_ref() { + recv_source = format!("&{recv_source}"); + } - let suggestion_source = reindent_multiline( - format!( - "std::path::Path::new({}) - .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_source, ext_str.strip_prefix('.').unwrap()).into(), - true, - Some(indent_of(cx, call_span).unwrap_or(0) + 4) - ); + let suggestion_source = reindent_multiline( + format!( + "std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_source, + ext_str.strip_prefix('.').unwrap() + ) + .into(), + true, + Some(indent_of(cx, call_span).unwrap_or(0) + 4), + ); - diag.span_suggestion( - recv.span.to(call_span), - "use std::path::Path", - suggestion_source, - Applicability::MaybeIncorrect, - ); - } + diag.span_suggestion( + recv.span.to(call_span), + "use std::path::Path", + suggestion_source, + Applicability::MaybeIncorrect, + ); } - ); - } + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index 0e41f3c2107..c99cec067bf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{method_chain_args, path_def_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; @@ -15,34 +14,34 @@ pub(super) fn check( lint: &'static Lint, suggest: &str, ) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind; - if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)); - if Some(id) == cx.tcx.lang_items().option_some_variant(); - then { - let mut applicability = Applicability::MachineApplicable; - let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs(); + if let Some(args) = method_chain_args(info.chain, chain_methods) + && let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind + && let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)) + && Some(id) == cx.tcx.lang_items().option_some_variant() + { + let mut applicability = Applicability::MachineApplicable; + let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs(); - if *self_ty.kind() != ty::Str { - return false; - } + if *self_ty.kind() != ty::Str { + return false; + } - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{suggest}` method"), - "like this", - format!("{}{}.{suggest}({})", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)), - applicability, - ); + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{suggest}` method"), + "like this", + format!( + "{}{}.{suggest}({})", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), + snippet_with_applicability(cx, arg_char.span, "..", &mut applicability) + ), + applicability, + ); - return true; - } + return true; } false diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index c9d50a5b03d..d07e45434a7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -15,28 +14,28 @@ pub(super) fn check( lint: &'static Lint, suggest: &str, ) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Lit(lit) = info.other.kind; - if let ast::LitKind::Char(c) = lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{suggest}` method"), - "like this", - format!("{}{}.{suggest}('{}')", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - c.escape_default()), - applicability, - ); + if let Some(args) = method_chain_args(info.chain, chain_methods) + && let hir::ExprKind::Lit(lit) = info.other.kind + && let ast::LitKind::Char(c) = lit.node + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{suggest}` method"), + "like this", + format!( + "{}{}.{suggest}('{}')", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), + c.escape_default() + ), + applicability, + ); - true - } else { - false - } + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index a8d4dd5e4f3..dc978c8a584 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -16,30 +16,27 @@ pub(super) fn check( err_span: Span, msrv: &Msrv, ) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if msrv.meets(msrvs::EXPECT_ERR); + && msrv.meets(msrvs::EXPECT_ERR) // Grabs the `Result<T, E>` type - let result_type = cx.typeck_results().expr_ty(recv); + && let result_type = cx.typeck_results().expr_ty(recv) // Tests if the T type in a `Result<T, E>` is not None - if let Some(data_type) = get_data_type(cx, result_type); + && let Some(data_type) = get_data_type(cx, result_type) // Tests if the T type in a `Result<T, E>` implements debug - if has_debug_impl(cx, data_type); - - then { - span_lint_and_sugg( - cx, - ERR_EXPECT, - err_span.to(expect_span), - "called `.err().expect()` on a `Result` value", - "try", - "expect_err".to_string(), - Applicability::MachineApplicable + && has_debug_impl(cx, data_type) + { + span_lint_and_sugg( + cx, + ERR_EXPECT, + err_span.to(expect_span), + "called `.err().expect()` on a `Result` value", + "try", + "expect_err".to_string(), + Applicability::MachineApplicable, ); - } }; } diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index a49970b5351..f0fc925799a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::Span; use std::borrow::Cow; use super::EXPECT_FUN_CALL; diff --git a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs index 495b266529b..460ec7b3640 100644 --- a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -11,35 +10,33 @@ use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Vec); + if is_type_diagnostic_item(cx, ty, sym::Vec) //check source object - if let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind; - if src_method.ident.as_str() == "drain"; - let src_ty = cx.typeck_results().expr_ty(drain_vec); + && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind + && src_method.ident.as_str() == "drain" + && let src_ty = cx.typeck_results().expr_ty(drain_vec) //check if actual src type is mutable for code suggestion - let immutable = src_ty.is_mutable_ptr(); - let src_ty = src_ty.peel_refs(); - if is_type_diagnostic_item(cx, src_ty, sym::Vec); + && let immutable = src_ty.is_mutable_ptr() + && let src_ty = src_ty.peel_refs() + && is_type_diagnostic_item(cx, src_ty, sym::Vec) //check drain range - if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs(); - if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - EXTEND_WITH_DRAIN, - expr.span, - "use of `extend` instead of `append` for adding the full range of a second vector", - "try", - format!( - "{}.append({}{})", - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - if immutable { "" } else { "&mut " }, - snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) - ), - applicability, - ); - } + && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() + && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + EXTEND_WITH_DRAIN, + expr.span, + "use of `extend` instead of `append` for adding the full range of a second vector", + "try", + format!( + "{}.append({}{})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + if immutable { "" } else { "&mut " }, + snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) + ), + applicability, + ); } } 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 7818be81119..b05361ab212 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,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::get_parent_expr; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{sym, Span}; @@ -19,21 +18,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let verb: &str; let lint_unary: &str; let help_unary: &str; - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::Unary(op, _) = parent.kind; - if op == hir::UnOp::Not; - then { - lint_unary = "!"; - verb = "denies"; - help_unary = ""; - span = parent.span; - } else { - lint_unary = ""; - verb = "covers"; - help_unary = "!"; - span = expr.span; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let hir::ExprKind::Unary(op, _) = parent.kind + && op == hir::UnOp::Not + { + lint_unary = "!"; + verb = "denies"; + help_unary = ""; + span = parent.span; + } else { + lint_unary = ""; + verb = "covers"; + help_unary = "!"; + 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"); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 5bb8c7a6b94..844ab40cab1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -4,15 +4,14 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; use hir::{Body, HirId, MatchSource, Pat}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::Span; use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; use std::borrow::Cow; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP}; @@ -29,13 +28,11 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { - if_chain! { - if ident.name == method_name; - if let hir::ExprKind::Path(path) = &receiver.kind; - if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); - then { - return arg_id == *local - } + if ident.name == method_name + && let hir::ExprKind::Path(path) = &receiver.kind + && let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id) + { + return arg_id == *local; } false }, @@ -139,11 +136,9 @@ impl<'tcx> OffendingFilterExpr<'tcx> { && path_to_local_id(map_arg_peeled, map_param_id)) && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; + let a_path = if !is_filter_param_ref + && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind + { expr_path } else { a }; // let the filter closure arg and the map closure arg be equal path_to_local_id(a_path, filter_param_id) && path_to_local_id(b, map_param_id) @@ -305,87 +300,98 @@ pub(super) fn check( return; } - if_chain! { - if is_trait_method(cx, map_recv, sym::Iterator); + if is_trait_method(cx, map_recv, sym::Iterator) // filter(|x| ...is_some())... - if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; + && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind + && let filter_body = cx.tcx.hir().body(filter_body_id) + && let [filter_param] = filter_body.params // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + && let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { (ref_pat, true) } else { (filter_param.pat, false) - }; - - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id); + } - if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + && let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind + && let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id) - if let Some(check_result) = - offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref); + && let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind + && let map_body = cx.tcx.hir().body(map_body_id) + && let [map_param] = map_body.params + && let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind - then { - let span = filter_span.with_hi(expr.span.hi()); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); + && let Some(check_result) = + offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref) + { + let span = filter_span.with_hi(expr.span.hi()); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); - let (sugg, note_and_span, applicability) = match check_result { - CheckResult::Method { map_arg, method, side_effect_expr_span } => { - let (to_opt, deref) = match method { - CalledMethod::ResultIsOk => (".ok()", String::new()), - CalledMethod::OptionIsSome => { - let derefs = cx.typeck_results() - .expr_adjustments(map_arg) - .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) - .count(); + let (sugg, note_and_span, applicability) = match check_result { + CheckResult::Method { + map_arg, + method, + side_effect_expr_span, + } => { + let (to_opt, deref) = match method { + CalledMethod::ResultIsOk => (".ok()", String::new()), + CalledMethod::OptionIsSome => { + let derefs = cx + .typeck_results() + .expr_adjustments(map_arg) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); - ("", "*".repeat(derefs)) - } - }; + ("", "*".repeat(derefs)) + }, + }; - let sugg = format!( - "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", - snippet(cx, map_arg.span, ".."), - ); - let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span { - let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \ - because this expression potentially contains side effects and will only execute once"; + let sugg = format!( + "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", + snippet(cx, map_arg.span, ".."), + ); + let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span { + let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \ + because this expression potentially contains side effects and will only execute once"; - (Some((note, span)), Applicability::MaybeIncorrect) - } else { - (None, Applicability::MachineApplicable) - }; + (Some((note, span)), Applicability::MaybeIncorrect) + } else { + (None, Applicability::MachineApplicable) + }; - (sugg, note_and_span, applicability) - } - CheckResult::PatternMatching { variant_span, variant_ident } => { - let pat = snippet(cx, variant_span, "<pattern>"); + (sugg, note_and_span, applicability) + }, + CheckResult::PatternMatching { + variant_span, + variant_ident, + } => { + let pat = snippet(cx, variant_span, "<pattern>"); - (format!("{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \ - {pat} => Some({variant_ident}), \ - _ => None \ - }})"), None, Applicability::MachineApplicable) - } - }; - span_lint_and_then(cx, lint, span, &msg, |diag| { - diag.span_suggestion(span, "try", sugg, applicability); + ( + format!( + "{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \ + {pat} => Some({variant_ident}), \ + _ => None \ + }})" + ), + None, + Applicability::MachineApplicable, + ) + }, + }; + span_lint_and_then(cx, lint, span, &msg, |diag| { + diag.span_suggestion(span, "try", sugg, applicability); - if let Some((note, span)) = note_and_span { - diag.span_note(span, note); - } - }); - } + if let Some((note, span)) = note_and_span { + diag.span_note(span, note); + } + }); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 4040d3a5fe1..917a8e33eb9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,28 +11,25 @@ use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { - if_chain! { - if is_path_diagnostic_item(cx, func, sym::from_iter_fn); - let ty = cx.typeck_results().expr_ty(expr); - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - - if implements_trait(cx, arg_ty, iter_id, &[]); - then { - // `expr` implements `FromIterator` trait - let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); - let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); - span_lint_and_sugg( - cx, - FROM_ITER_INSTEAD_OF_COLLECT, - expr.span, - "usage of `FromIterator::from_iter`", - "use `.collect()` instead of `::from_iter()`", - sugg, - Applicability::MaybeIncorrect, - ); - } + if is_path_diagnostic_item(cx, func, sym::from_iter_fn) + && let ty = cx.typeck_results().expr_ty(expr) + && let arg_ty = cx.typeck_results().expr_ty(&args[0]) + && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && implements_trait(cx, arg_ty, iter_id, &[]) + { + // `expr` implements `FromIterator` trait + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let turbofish = extract_turbofish(cx, expr, ty); + let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); + span_lint_and_sugg( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "usage of `FromIterator::from_iter`", + "use `.collect()` instead of `::from_iter()`", + sugg, + Applicability::MaybeIncorrect, + ); } } @@ -43,41 +39,43 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> } let call_site = expr.span.source_callsite(); - if_chain! { - if let Some(snippet) = snippet_opt(cx, call_site); - let snippet_split = snippet.split("::").collect::<Vec<_>>(); - if let Some((_, elements)) = snippet_split.split_last(); - - then { - if_chain! { - if let [type_specifier, _] = snippet_split.as_slice(); - if let Some(type_specifier) = strip_angle_brackets(type_specifier); - if let Some((type_specifier, ..)) = type_specifier.split_once(" as "); - then { - type_specifier.to_string() - } else { - // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { - // remove the type specifier from the path elements - let without_ts = elements.iter().filter_map(|e| { - if e == type_specifier { None } else { Some((*e).to_string()) } - }).collect::<Vec<_>>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`) - format!("{}{type_specifier}", without_ts.join("::")) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or(ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{wildcards}>", elements.join("::")) - } - } - } + if let Some(snippet) = snippet_opt(cx, call_site) + && let snippet_split = snippet.split("::").collect::<Vec<_>>() + && let Some((_, elements)) = snippet_split.split_last() + { + if let [type_specifier, _] = snippet_split.as_slice() + && let Some(type_specifier) = strip_angle_brackets(type_specifier) + && let Some((type_specifier, ..)) = type_specifier.split_once(" as ") + { + type_specifier.to_string() } else { - ty.to_string() + // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`) + if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { + // remove the type specifier from the path elements + let without_ts = elements + .iter() + .filter_map(|e| { + if e == type_specifier { + None + } else { + Some((*e).to_string()) + } + }) + .collect::<Vec<_>>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`) + format!("{}{type_specifier}", without_ts.join("::")) + } else { + // type is not explicitly specified so wildcards are needed + // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` + let ty_str = ty.to_string(); + let start = ty_str.find('<').unwrap_or(0); + let end = ty_str.find('>').unwrap_or(ty_str.len()); + let nb_wildcard = ty_str[start..end].split(',').count(); + let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); + format!("{}<{wildcards}>", elements.join("::")) + } } + } else { + ty.to_string() } } diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs index 2e1dd3ec649..e1f1e489355 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -17,37 +16,38 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, ) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - let identity = cx.tcx.type_of(impl_id).instantiate_identity(); - if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind; - then { - if identity.is_slice() { - let mut app = Applicability::MachineApplicable; - let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); - span_lint_and_sugg( - cx, - GET_FIRST, - expr.span, - &format!("accessing first element with `{slice_name}.get(0)`"), - "try", - format!("{slice_name}.first()"), - app, - ); - } else if is_type_diagnostic_item(cx, identity, sym::VecDeque){ - let mut app = Applicability::MachineApplicable; - let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); - span_lint_and_sugg( - cx, - GET_FIRST, - expr.span, - &format!("accessing first element with `{slice_name}.get(0)`"), - "try", - format!("{slice_name}.front()"), - app, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let identity = cx.tcx.type_of(impl_id).instantiate_identity() + && let hir::ExprKind::Lit(Spanned { + node: LitKind::Int(0, _), + .. + }) = arg.kind + { + if identity.is_slice() { + let mut app = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{slice_name}.get(0)`"), + "try", + format!("{slice_name}.first()"), + app, + ); + } else if is_type_diagnostic_item(cx, identity, sym::VecDeque) { + let mut app = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{slice_name}.get(0)`"), + "try", + format!("{slice_name}.front()"), + app, + ); } } } 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 e91ce64d8c8..78a553eb8c0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -2,7 +2,6 @@ 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 if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -11,34 +10,32 @@ use rustc_span::sym; use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if 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); - if !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)); - if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())); - if return_type == input_type; - if let Some(clone_trait) = cx.tcx.lang_items().clone_trait(); - if implements_trait(cx, return_type, clone_trait, &[]); - then { - let mut app = Applicability::MachineApplicable; - let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( - cx, - IMPLICIT_CLONE, - expr.span, - &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), - "consider using", - if ref_count > 1 { - format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) - } else { - format!("{recv_snip}.clone()") - }, - app, - ); - } + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && 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) + && !(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 + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, return_type, clone_trait, &[]) + { + let mut app = Applicability::MachineApplicable; + let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; + span_lint_and_sugg( + cx, + IMPLICIT_CLONE, + expr.span, + &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), + "consider using", + if ref_count > 1 { + format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) + } else { + format!("{recv_snip}.clone()") + }, + app, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index 6686d42c95f..efc3ddd20b4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,37 +17,36 @@ pub fn check( receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>], ) { - if_chain! { - if args.is_empty() && method_name == sym::to_string; - if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did); - if let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id); - let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver); - let self_ty = args.type_at(0); - let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); - if deref_count >= 1; - if specializes_tostring(cx, deref_self_ty); - then { - span_lint_and_then( - cx, - INEFFICIENT_TO_STRING, - expr.span, - &format!("calling `to_string` on `{arg_ty}`"), - |diag| { - diag.help(format!( - "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" - )); - let mut applicability = Applicability::MachineApplicable; - let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); - diag.span_suggestion( - expr.span, - "try dereferencing the receiver", - format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), - applicability, - ); - }, - ); - } + if args.is_empty() + && method_name == sym::to_string + && let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did) + && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) + && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) + && let self_ty = args.type_at(0) + && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty) + && deref_count >= 1 + && specializes_tostring(cx, deref_self_ty) + { + span_lint_and_then( + cx, + INEFFICIENT_TO_STRING, + expr.span, + &format!("calling `to_string` on `{arg_ty}`"), + |diag| { + diag.help(format!( + "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" + )); + let mut applicability = Applicability::MachineApplicable; + let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); + diag.span_suggestion( + expr.span, + "try dereferencing the receiver", + format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), + applicability, + ); + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs index bbd964c1099..80160d17c82 100644 --- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; use clippy_utils::ty::has_iter_method; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::Span; use super::INTO_ITER_ON_REF; @@ -19,24 +18,20 @@ pub(super) fn check( receiver: &hir::Expr<'_>, ) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); - if_chain! { - if let ty::Ref(..) = self_ty.kind(); - if method_name == sym::into_iter; - if is_trait_method(cx, expr, sym::IntoIterator); - if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty); - then { - span_lint_and_sugg( - cx, - INTO_ITER_ON_REF, - method_span, - &format!( - "this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`", - ), - "call directly", - method_name.to_string(), - Applicability::MachineApplicable, - ); - } + if let ty::Ref(..) = self_ty.kind() + && method_name == sym::into_iter + && is_trait_method(cx, expr, sym::IntoIterator) + && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) + { + span_lint_and_sugg( + cx, + INTO_ITER_ON_REF, + method_span, + &format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",), + "call directly", + method_name.to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs index bde6f92b076..dd741cd43f9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,7 +1,6 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,22 +9,21 @@ use rustc_span::sym; use super::ITER_CLONED_COLLECT; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec); - if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)); - if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); - - then { - span_lint_and_sugg( - cx, - ITER_CLONED_COLLECT, - to_replace, - &format!("called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable"), - "try", - ".to_vec()".to_string(), - Applicability::MachineApplicable, - ); - } + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) + && let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()) + { + span_lint_and_sugg( + cx, + ITER_CLONED_COLLECT, + to_replace, + &format!( + "called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable" + ), + "try", + ".to_vec()".to_string(), + Applicability::MachineApplicable, + ); } } 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 625325d4cf5..c5dbb6ad98b 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 @@ -22,64 +22,54 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v ) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Closure(c) = m_arg.kind; - if let Body {params: [p], value: body_expr, coroutine_kind: _ } = cx.tcx.hir().body(c.body); - if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; - - let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { + if !expr.span.from_expansion() + && let ExprKind::Closure(c) = m_arg.kind + && let Body { + params: [p], + value: body_expr, + coroutine_kind: _, + } = cx.tcx.hir().body(c.body) + && let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind + && let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value), (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, - }; - - let ty = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap); - - then { - let mut applicability = rustc_errors::Applicability::MachineApplicable; - let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); - let into_prefix = if map_type == "into_iter" {"into_"} else {""}; - - if_chain! { - if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind; - if let [local_ident] = path.segments; - if local_ident.ident.as_str() == bound_ident.as_str(); + } + && let ty = cx.typeck_results().expr_ty(recv) + && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + { + let mut applicability = rustc_errors::Applicability::MachineApplicable; + let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); + let into_prefix = if map_type == "into_iter" { "into_" } else { "" }; - then { - span_lint_and_sugg( - cx, - ITER_KV_MAP, - expr.span, - &format!("iterating on a map's {replacement_kind}s"), - "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), - applicability, - ); - } else { - let ref_annotation = if annotation.0 == ByRef::Yes { - "ref " - } else { - "" - }; - let mut_annotation = if annotation.1 == Mutability::Mut { - "mut " - } else { - "" - }; - span_lint_and_sugg( - cx, - ITER_KV_MAP, - expr.span, - &format!("iterating on a map's {replacement_kind}s"), - "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", - snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), - applicability, - ); - } - } + if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind + && let [local_ident] = path.segments + && local_ident.ident.as_str() == bound_ident.as_str() + { + span_lint_and_sugg( + cx, + ITER_KV_MAP, + expr.span, + &format!("iterating on a map's {replacement_kind}s"), + "try", + format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), + applicability, + ); + } else { + let ref_annotation = if annotation.0 == ByRef::Yes { "ref " } else { "" }; + let mut_annotation = if annotation.1 == Mutability::Mut { "mut " } else { "" }; + span_lint_and_sugg( + cx, + ITER_KV_MAP, + expr.span, + &format!("iterating on a map's {replacement_kind}s"), + "try", + format!( + "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", + snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability) + ), + applicability, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs index 8f885e9f729..fd4650e1e45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, higher}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -26,29 +25,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() { // caller is a Slice - if_chain! { - if let hir::ExprKind::Index(caller_var, index_expr, _) = &caller_expr.kind; - if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) - = higher::Range::hir(index_expr); - if let hir::ExprKind::Lit(start_lit) = &start_expr.kind; - if let ast::LitKind::Int(start_idx, _) = start_lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - let suggest = if start_idx == 0 { - format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) - } else { - format!("{}.get({start_idx})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) - }; - span_lint_and_sugg( - cx, - ITER_NEXT_SLICE, - expr.span, - "using `.iter().next()` on a Slice without end index", - "try calling", - suggest, - applicability, - ); - } + if let hir::ExprKind::Index(caller_var, index_expr, _) = &caller_expr.kind + && let Some(higher::Range { + start: Some(start_expr), + end: None, + limits: ast::RangeLimits::HalfOpen, + }) = higher::Range::hir(index_expr) + && let hir::ExprKind::Lit(start_lit) = &start_expr.kind + && let ast::LitKind::Int(start_idx, _) = start_lit.node + { + let mut applicability = Applicability::MachineApplicable; + let suggest = if start_idx == 0 { + format!( + "{}.first()", + snippet_with_applicability(cx, caller_var.span, "..", &mut applicability) + ) + } else { + format!( + "{}.get({start_idx})", + snippet_with_applicability(cx, caller_var.span, "..", &mut applicability) + ) + }; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "using `.iter().next()` on a Slice without end index", + "try calling", + suggest, + applicability, + ); } } else if is_vec_or_array(cx, caller_expr) { // caller is a Vec or an Array diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs index 39af52141bb..fbe20dfe54e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs @@ -19,18 +19,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", |diag| { - if_chain! { - if let Some(id) = path_to_local(recv); - if let Node::Pat(pat) = cx.tcx.hir().get(id); - if let PatKind::Binding(ann, _, _, _) = pat.kind; - if ann != BindingAnnotation::MUT; - then { - application = Applicability::Unspecified; - diag.span_help( - pat.span, - format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), - ); - } + if let Some(id) = path_to_local(recv) + && let Node::Pat(pat) = cx.tcx.hir().get(id) + && let PatKind::Binding(ann, _, _, _) = pat.kind + && ann != BindingAnnotation::MUT + { + application = Applicability::Unspecified; + diag.span_help( + pat.span, + format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + ); } diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index 3031193e531..b1af0083e65 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -2,7 +2,6 @@ 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; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -18,30 +17,26 @@ pub(super) fn check<'tcx>( or_expr: &'tcx Expr<'_>, map_expr: &'tcx Expr<'_>, ) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option); - if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind; - if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr); - if is_ok_wrapping(cx, map_expr); - if let Some(recv_snippet) = snippet_opt(cx, recv.span); - if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); - if let Some(indent) = indent_of(cx, expr.span); - then { - let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); - span_lint_and_sugg( - cx, - MANUAL_OK_OR, - expr.span, - "this pattern reimplements `Option::ok_or`", - "replace with", - format!( - "{recv_snippet}.ok_or({reindented_err_arg_snippet})" - ), - Applicability::MachineApplicable, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) + && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind + && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) + && is_ok_wrapping(cx, map_expr) + && let Some(recv_snippet) = snippet_opt(cx, recv.span) + && let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span) + && let Some(indent) = indent_of(cx, expr.span) + { + let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + span_lint_and_sugg( + cx, + MANUAL_OK_OR, + expr.span, + "this pattern reimplements `Option::ok_or`", + "replace with", + format!("{recv_snippet}.ok_or({reindented_err_arg_snippet})"), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 540425eef8c..04bdbc1ea25 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{match_def_path, path_def_id}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -69,16 +68,14 @@ enum MinMax { fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> { // `T::max_value()` `T::min_value()` inherent methods - if_chain! { - if let hir::ExprKind::Call(func, args) = &expr.kind; - if args.is_empty(); - if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind; - then { - match segment.ident.as_str() { - "max_value" => return Some(MinMax::Max), - "min_value" => return Some(MinMax::Min), - _ => {} - } + if let hir::ExprKind::Call(func, args) = &expr.kind + && args.is_empty() + && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind + { + match segment.ident.as_str() { + "max_value" => return Some(MinMax::Max), + "min_value" => return Some(MinMax::Min), + _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs index ab13d30d845..61e74369cb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs @@ -3,7 +3,6 @@ use clippy_utils::is_path_diagnostic_item; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -55,43 +54,42 @@ pub(super) fn check( take_self_arg: &Expr<'_>, take_arg: &Expr<'_>, ) { - if_chain! { - if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; - if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); - if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); - if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); - if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); - if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); - let ctxt = collect_expr.span.ctxt(); - if ctxt == take_expr.span.ctxt(); - if ctxt == take_self_arg.span.ctxt(); - then { - let mut app = Applicability::MachineApplicable; - let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; + if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind + && is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat) + && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) + && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) + && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && cx.tcx.trait_of_item(take_id) == Some(iter_trait_id) + && let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg) + && let ctxt = collect_expr.span.ctxt() + && ctxt == take_expr.span.ctxt() + && ctxt == take_self_arg.span.ctxt() + { + let mut app = Applicability::MachineApplicable; + let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; - let val_str = match repeat_kind { - RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return, - RepeatKind::Char('\'') => r#""'""#.into(), - RepeatKind::Char('"') => r#""\"""#.into(), - RepeatKind::Char(_) => - match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) { - Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])), - s @ Cow::Borrowed(_) => s, - }, - RepeatKind::String => - Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(), - }; + let val_str = match repeat_kind { + RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return, + RepeatKind::Char('\'') => r#""'""#.into(), + RepeatKind::Char('"') => r#""\"""#.into(), + RepeatKind::Char(_) => match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) { + Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])), + s @ Cow::Borrowed(_) => s, + }, + RepeatKind::String => Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app) + .maybe_par() + .to_string() + .into(), + }; - span_lint_and_sugg( - cx, - MANUAL_STR_REPEAT, - collect_expr.span, - "manual implementation of `str::repeat` using iterators", - "try", - format!("{val_str}.repeat({count_snip})"), - app - ) - } + span_lint_and_sugg( + cx, + MANUAL_STR_REPEAT, + collect_expr.span, + "manual implementation of `str::repeat` using iterators", + "try", + format!("{val_str}.repeat({count_snip})"), + app, + ); } } 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 e0f8cb1b955..cc6eeaa86e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -3,7 +3,6 @@ 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}; use clippy_utils::{is_diag_trait_item, peel_blocks}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,57 +15,55 @@ use rustc_span::{sym, Span}; use super::MAP_CLONE; pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); - if cx.tcx.impl_of_method(method_id) - .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option)) - || is_diag_trait_item(cx, method_id, sym::Iterator); - if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind; - then { - let closure_body = cx.tcx.hir().body(body); - let closure_expr = peel_blocks(closure_body.value); - match closure_body.params[0].pat.kind { - hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( - hir::BindingAnnotation::NONE, .., name, None - ) = inner.kind { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && (cx.tcx.impl_of_method(method_id).map_or(false, |id| { + is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option) + }) || is_diag_trait_item(cx, method_id, sym::Iterator)) + && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind + { + let closure_body = cx.tcx.hir().body(body); + let closure_expr = peel_blocks(closure_body.value); + match closure_body.params[0].pat.kind { + hir::PatKind::Ref(inner, hir::Mutability::Not) => { + if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind { if ident_eq(name, closure_expr) { lint_explicit_closure(cx, e.span, recv.span, true, msrv); } - }, - hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => { - match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { - if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + } + }, + hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => { + match closure_expr.kind { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { + if ident_eq(name, inner) { + if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } - }, - hir::ExprKind::MethodCall(method, obj, [], _) => if_chain! { - if ident_eq(name, obj) && method.ident.name == sym::clone; - if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); - if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); - if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id); - // no autoderefs - if !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); - then { - let obj_ty = cx.typeck_results().expr_ty(obj); - if let ty::Ref(_, ty, mutability) = obj_ty.kind() { - if matches!(mutability, Mutability::Not) { - let copy = is_copy(cx, *ty); - lint_explicit_closure(cx, e.span, recv.span, copy, msrv); - } - } else { - lint_needless_cloning(cx, e.span, recv.span); + } + }, + hir::ExprKind::MethodCall(method, obj, [], _) => { + if ident_eq(name, obj) && method.ident.name == sym::clone + && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + // no autoderefs + && !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + let obj_ty = cx.typeck_results().expr_ty(obj); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, *ty); + lint_explicit_closure(cx, e.span, recv.span, copy, msrv); } + } else { + lint_needless_cloning(cx, e.span, recv.span); } - }, - _ => {}, - } - }, - _ => {}, - } + } + }, + _ => {}, + } + }, + _ => {}, } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs index 01cdd02e602..e944eac91e7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -13,26 +12,24 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if_chain! { - if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); - if let ty::Adt(_, args) = collect_ret_ty.kind(); - if let Some(result_t) = args.types().next(); - if result_t.is_unit(); - // get parts for snippet - then { - span_lint_and_sugg( - cx, - MAP_COLLECT_RESULT_UNIT, - expr.span, - "`.map().collect()` can be replaced with `.try_for_each()`", - "try", - format!( - "{}.try_for_each({})", - snippet(cx, iter.span, ".."), - snippet(cx, map_fn.span, "..") - ), - Applicability::MachineApplicable, - ); - } + if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result) + && let ty::Adt(_, args) = collect_ret_ty.kind() + && let Some(result_t) = args.types().next() + && result_t.is_unit() + // get parts for snippet + { + span_lint_and_sugg( + cx, + MAP_COLLECT_RESULT_UNIT, + expr.span, + "`.map().collect()` can be replaced with `.try_for_each()`", + "try", + format!( + "{}.try_for_each({})", + snippet(cx, iter.span, ".."), + snippet(cx, map_fn.span, "..") + ), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index e74a764551c..26ef0d10fed 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -63,7 +63,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { ty::Closure(_, args) => args.as_closure().sig(), _ => map_closure_ty.fn_sig(cx.tcx), }; - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); + let map_closure_return_ty = cx.tcx.instantiate_bound_regions_with_erased(map_closure_sig.output()); is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index bcfd0de8efe..6da9a87f5ee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -18,22 +18,20 @@ pub(super) fn check( ) { let caller_ty = cx.typeck_results().expr_ty(caller); - if_chain! { - if is_trait_method(cx, expr, sym::Iterator) - || is_type_diagnostic_item(cx, caller_ty, sym::Result) - || is_type_diagnostic_item(cx, caller_ty, sym::Option); - if is_expr_untyped_identity_function(cx, map_arg); - if let Some(sugg_span) = expr.span.trim_start(caller.span); - then { - span_lint_and_sugg( - cx, - MAP_IDENTITY, - sugg_span, - "unnecessary map of the identity function", - &format!("remove the call to `{name}`"), - String::new(), - Applicability::MachineApplicable, - ) - } + if (is_trait_method(cx, expr, sym::Iterator) + || is_type_diagnostic_item(cx, caller_ty, sym::Result) + || is_type_diagnostic_item(cx, caller_ty, sym::Option)) + && is_expr_untyped_identity_function(cx, map_arg) + && let Some(sugg_span) = expr.span.trim_start(caller.span) + { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + sugg_span, + "unnecessary map of the identity function", + &format!("remove the call to `{name}`"), + String::new(), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index a71a136eba5..31d44bc1b3f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -123,7 +123,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; -use if_chain::if_chain; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -3698,8 +3697,10 @@ impl Methods { msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, - allowed_dotfiles: FxHashSet<String>, + mut allowed_dotfiles: FxHashSet<String>, ) -> Self { + allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string)); + Self { avoid_breaking_exported_api, msrv, @@ -3896,7 +3897,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); - let method_sig = cx.tcx.erase_late_bound_regions(method_sig); + let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); // if this impl block implements a trait, lint in trait definition instead if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { @@ -3977,44 +3978,41 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if_chain! { - if let TraitItemKind::Fn(ref sig, _) = item.kind; - if sig.decl.implicit_self.has_implicit_self(); - if let Some(first_arg_hir_ty) = sig.decl.inputs.first(); - if let Some(&first_arg_ty) = cx.tcx.fn_sig(item.owner_id) + if let TraitItemKind::Fn(ref sig, _) = item.kind + && sig.decl.implicit_self.has_implicit_self() + && let Some(first_arg_hir_ty) = sig.decl.inputs.first() + && let Some(&first_arg_ty) = cx + .tcx + .fn_sig(item.owner_id) .instantiate_identity() .inputs() .skip_binder() - .first(); - then { - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - wrong_self_convention::check( - cx, - item.ident.name.as_str(), - self_ty, - first_arg_ty, - first_arg_hir_ty.span, - false, - true, - ); - } - } - - if_chain! { - if item.ident.name == sym::new; - if let TraitItemKind::Fn(_, _) = item.kind; - let ret_ty = return_ty(cx, item.owner_id); + .first() + { let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - if !ret_ty.contains(self_ty); + wrong_self_convention::check( + cx, + item.ident.name.as_str(), + self_ty, + first_arg_ty, + first_arg_hir_ty.span, + false, + true, + ); + } - then { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); - } + if item.ident.name == sym::new + && let TraitItemKind::Fn(_, _) = item.kind + && let ret_ty = return_ty(cx, item.owner_id) + && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs index 2855e23bf5b..1a0fce2876d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -11,22 +10,20 @@ use rustc_span::{sym, Span}; use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { - if_chain! { - if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)); - if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind(); - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex); - then { - span_lint_and_sugg( - cx, - MUT_MUTEX_LOCK, - name_span, - "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", - "change this to", - "get_mut".to_owned(), - Applicability::MaybeIncorrect, - ); - } + if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) + && let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind() + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) + { + span_lint_and_sugg( + cx, + MUT_MUTEX_LOCK, + name_span, + "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", + "change this to", + "get_mut".to_owned(), + Applicability::MaybeIncorrect, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 2ef71be3217..79ed5515ea2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -225,7 +225,7 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - && let sig = cx.tcx.fn_sig(id).instantiate_identity() && sig.skip_binder().output().is_bool() && let [_, search_ty] = *sig.skip_binder().inputs() - && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() + && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.instantiate_bound_regions_with_erased(sig.rebind(search_ty)).kind() && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_name_and_kind( cx.tcx, diff --git a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs index 01655e860c4..81df32bdee2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_lang_item; use clippy_utils::SpanlessEq; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; @@ -19,17 +18,13 @@ pub(super) fn check<'tcx>( return; } - if_chain! { - if let ExprKind::Lit(spanned) = &arg1.kind; - if let Some(param1) = lit_string_value(&spanned.node); - - if let ExprKind::Lit(spanned) = &arg2.kind; - if let LitKind::Str(param2, _) = &spanned.node; - if param1 == param2.as_str(); - - then { - span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); - } + if let ExprKind::Lit(spanned) = &arg1.kind + && let Some(param1) = lit_string_value(&spanned.node) + && let ExprKind::Lit(spanned) = &arg2.kind + && let LitKind::Str(param2, _) = &spanned.node + && param1 == param2.as_str() + { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); } if SpanlessEq::new(cx).eq_expr(arg1, arg2) { diff --git a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs index f2ef42933df..e10bc0216e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -10,23 +9,20 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) // lint if the caller of `ok()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - let result_type = cx.typeck_results().expr_ty(recv); - if let Some(error_type) = get_error_type(cx, result_type); - if has_debug_impl(cx, error_type); - - then { - span_lint_and_help( - cx, - OK_EXPECT, - expr.span, - "called `ok().expect()` on a `Result` value", - None, - "you can call `expect()` directly on the `Result`", - ); - } + && let result_type = cx.typeck_results().expr_ty(recv) + && let Some(error_type) = get_error_type(cx, result_type) + && has_debug_impl(cx, error_type) + { + span_lint_and_help( + cx, + OK_EXPECT, + expr.span, + "called `ok().expect()` on a `Result` value", + None, + "you can call `expect()` directly on the `Result`", + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 7b81d4571b2..15111006133 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -58,34 +57,30 @@ pub(super) fn check( match &closure_expr.kind { hir::ExprKind::MethodCall(_, receiver, [], _) => { - if_chain! { - if path_to_local_id(receiver, closure_body.params[0].pat.hir_id); - let adj = cx + if path_to_local_id(receiver, closure_body.params[0].pat.hir_id) + && let adj = cx .typeck_results() .expr_adjustments(receiver) .iter() .map(|x| &x.kind) - .collect::<Box<[_]>>(); - if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; - then { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - cx.tcx.is_diagnostic_item(sym::deref_method, method_did) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) - } else { - false - } + .collect::<Box<[_]>>() + && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + { + let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + } else { + false } }, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => { - if_chain! { - if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind; - if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind; - then { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) - } else { - false - } + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind + && let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind + { + path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + } else { + false } }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs index cb6a2306857..418e6a7d6a0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -58,27 +58,25 @@ pub(super) fn check<'tcx>( if is_option { let self_snippet = snippet(cx, recv.span, ".."); - if_chain! { - if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind; - let arg_snippet = snippet(cx, fn_decl_span, ".."); - let body = cx.tcx.hir().body(body); - if let Some((func, [arg_char])) = reduce_unit_expression(body.value); - if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)); - if Some(id) == cx.tcx.lang_items().option_some_variant(); - then { - let func_snippet = snippet(cx, arg_char.span, ".."); - let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ - `map(..)` instead"; - return span_lint_and_sugg( - cx, - OPTION_MAP_OR_NONE, - expr.span, - msg, - "try using `map` instead", - format!("{self_snippet}.map({arg_snippet} {func_snippet})"), - Applicability::MachineApplicable, - ); - } + if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind + && let arg_snippet = snippet(cx, fn_decl_span, "..") + && let body = cx.tcx.hir().body(body) + && let Some((func, [arg_char])) = reduce_unit_expression(body.value) + && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)) + && Some(id) == cx.tcx.lang_items().option_some_variant() + { + let func_snippet = snippet(cx, arg_char.span, ".."); + let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ + `map(..)` instead"; + return span_lint_and_sugg( + cx, + OPTION_MAP_OR_NONE, + expr.span, + msg, + "try using `map` instead", + format!("{self_snippet}.map({arg_snippet} {func_snippet})"), + Applicability::MachineApplicable, + ); } let func_snippet = snippet(cx, map_arg.span, ".."); diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index b89c151461c..e38c66f6741 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -3,12 +3,11 @@ use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; use rustc_span::symbol::{self, sym, Symbol}; +use rustc_span::Span; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; @@ -131,54 +130,47 @@ pub(super) fn check<'tcx>( (sym::Result, true, &["or", "unwrap_or"], "else"), ]; - if_chain! { - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); - - if switch_to_lazy_eval(cx, arg); - if !contains_return(arg); - - let self_ty = cx.typeck_results().expr_ty(self_expr); - - if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)); - - if poss.contains(&name); - - then { - let ctxt = span.ctxt(); - let mut app = Applicability::HasPlaceholders; - let sugg = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - - let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; - let snip = if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snip}") - } else { - snip.into_owned() - }; - - if let Some(f) = second_arg { - let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; - format!("{snip}, {f}") - } else { - snip - } + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) + && switch_to_lazy_eval(cx, arg) + && !contains_return(arg) + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && let Some(&(_, fn_has_arguments, poss, suffix)) = + KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)) + && poss.contains(&name) + { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; + let sugg = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; + let snip = if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{l_arg}| {snip}") + } else { + snip.into_owned() }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - &format!("use of `{name}` followed by a function call"), - "try", - format!("{name}_{suffix}({sugg})"), - app, - ); - } + + if let Some(f) = second_arg { + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; + format!("{snip}, {f}") + } else { + snip + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + &format!("use of `{name}` followed by a function call"), + "try", + format!("{name}_{suffix}({sugg})"), + app, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs index 1c07d2a3a59..04a27cc98f3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -11,27 +10,25 @@ use std::path::{Component, Path}; use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf); - if let ExprKind::Lit(lit) = arg.kind; - if let LitKind::Str(ref path_lit, _) = lit.node; - if let pushed_path = Path::new(path_lit.as_str()); - if let Some(pushed_path_lit) = pushed_path.to_str(); - if pushed_path.has_root(); - if let Some(root) = pushed_path.components().next(); - if root == Component::RootDir; - then { - span_lint_and_sugg( - cx, - PATH_BUF_PUSH_OVERWRITE, - lit.span, - "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", - "try", - format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), - Applicability::MachineApplicable, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Str(ref path_lit, _) = lit.node + && let pushed_path = Path::new(path_lit.as_str()) + && let Some(pushed_path_lit) = pushed_path.to_str() + && pushed_path.has_root() + && let Some(root) = pushed_path.components().next() + && root == Component::RootDir + { + span_lint_and_sugg( + cx, + PATH_BUF_PUSH_OVERWRITE, + lit.span, + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "try", + format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index f253d8de91f..1148628b084 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq}; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,25 +8,26 @@ use rustc_span::sym; use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { - if_chain! { - if is_trait_method(cx, expr, sym::Iterator); + if is_trait_method(cx, expr, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg); - if is_integer_const(cx, start, 0); + && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) + && is_integer_const(cx, start, 0) // `.len()` call - if let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind; - if len_path.ident.name == sym::len; + && let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind + && len_path.ident.name == sym::len // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind; - if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - expr.span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, recv.span, "_")) - ); - } + && let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind + && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind + && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) + { + span_lint( + cx, + RANGE_ZIP_WITH_LEN, + expr.span, + &format!( + "it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, recv.span, "_") + ), + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 05a9a06c8c7..6339011c92f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -3,13 +3,12 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_trait_method, strip_pat_refs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::Span; use super::SEARCH_IS_SOME; @@ -35,29 +34,27 @@ pub(super) fn check<'tcx>( // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if_chain! { - if search_method == "find"; - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind; - let closure_body = cx.tcx.hir().body(body); - if let Some(closure_arg) = closure_body.params.first(); - then { - if let hir::PatKind::Ref(..) = closure_arg.pat.kind { - Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { - // `find()` provides a reference to the item, but `any` does not, - // so we should fix item usages for suggestion - if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { - applicability = closure_sugg.applicability; - Some(closure_sugg.suggestion) - } else { - Some(search_snippet.to_string()) - } + let any_search_snippet = if search_method == "find" + && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind + && let closure_body = cx.tcx.hir().body(body) + && let Some(closure_arg) = closure_body.params.first() + { + if let hir::PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { + // `find()` provides a reference to the item, but `any` does not, + // so we should fix item usages for suggestion + if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { + applicability = closure_sugg.applicability; + Some(closure_sugg.suggestion) } else { - None + Some(search_snippet.to_string()) } } else { None } + } else { + None }; // add note if not multi-line if is_some { @@ -110,41 +107,37 @@ pub(super) fn check<'tcx>( self_ty.is_str() } }; - if_chain! { - if is_string_or_str_slice(search_recv); - if is_string_or_str_slice(search_arg); - then { - let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); - match option_check_method { - "is_some" => { - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `contains()` instead", - format!("contains({find_arg})"), - applicability, - ); - }, - "is_none" => { - let string = snippet(cx, search_recv.span, ".."); - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - expr.span, - &msg, - "use `!_.contains()` instead", - format!("!{string}.contains({find_arg})"), - applicability, - ); - }, - _ => (), - } + if is_string_or_str_slice(search_recv) && is_string_or_str_slice(search_arg) { + let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); + match option_check_method { + "is_some" => { + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `contains()` instead", + format!("contains({find_arg})"), + applicability, + ); + }, + "is_none" => { + let string = snippet(cx, search_recv.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.contains()` instead", + format!("!{string}.contains({find_arg})"), + applicability, + ); + }, + _ => (), } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs index 4d704ec39eb..3983f0c0cab 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs @@ -1,6 +1,5 @@ use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -45,24 +44,23 @@ pub(super) fn check( args: &[hir::Expr<'_>], ) { for &(method, pos) in &PATTERN_METHODS { - if_chain! { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind(); - if ty.is_str(); - if method_name.as_str() == method && args.len() > pos; - let arg = &args[pos]; - let mut applicability = Applicability::MachineApplicable; - if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability); - then { - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); - } + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() + && method_name.as_str() == method + && args.len() > pos + && let arg = &args[pos] + && let mut applicability = Applicability::MachineApplicable + && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) + { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); } } } 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 9da61bca52c..0e7ad8fc996 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -6,7 +6,6 @@ use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, @@ -286,41 +285,35 @@ fn parse_iter_usage<'tcx>( match (name.ident.as_str(), args) { ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { - return if_chain! { - if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); - if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind(); - if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()); - if let ty::Tuple(subs) = subs.type_at(0).kind(); - if subs.len() == 2; - then { - Some(IterUsage { - kind: IterUsageKind::NextTuple, - span: e.span, - unwrap_kind: None - }) - } else { - None - } + return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE) + && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() + && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) + && let ty::Tuple(subs) = subs.type_at(0).kind() + && subs.len() == 2 + { + Some(IterUsage { + kind: IterUsageKind::NextTuple, + span: e.span, + unwrap_kind: None, + }) + } else { + None }; }, ("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) { let span = if name.ident.as_str() == "nth" { e.span + } else if let Some((_, Node::Expr(next_expr))) = iter.next() + && let ExprKind::MethodCall(next_name, _, [], _) = next_expr.kind + && next_name.ident.name == sym::next + && next_expr.span.ctxt() == ctxt + && let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id) + && cx.tcx.trait_of_item(next_id) == Some(iter_id) + { + next_expr.span } else { - if_chain! { - if let Some((_, Node::Expr(next_expr))) = iter.next(); - if let ExprKind::MethodCall(next_name, _, [], _) = next_expr.kind; - if next_name.ident.name == sym::next; - if next_expr.span.ctxt() == ctxt; - if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id); - if cx.tcx.trait_of_item(next_id) == Some(iter_id); - then { - next_expr.span - } else { - return None; - } - } + return None; }; (IterUsageKind::Nth(idx), span) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs index 0dc7fe2a2c5..ed49233acb7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::usage::mutated_variables; use clippy_utils::{expr_or_init, is_trait_method}; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,26 +8,24 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { - if_chain! { - if is_trait_method(cx, count_recv, sym::Iterator); - if let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind; - let closure_body = cx.tcx.hir().body(closure.body); - if !cx.typeck_results().expr_ty(closure_body.value).is_unit(); - then { - if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) { - // A variable is used mutably inside of the closure. Suppress the lint. - if !map_mutated_vars.is_empty() { - return; - } + if is_trait_method(cx, count_recv, sym::Iterator) + && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind + && let closure_body = cx.tcx.hir().body(closure.body) + && !cx.typeck_results().expr_ty(closure_body.value).is_unit() + { + if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) { + // A variable is used mutably inside of the closure. Suppress the lint. + if !map_mutated_vars.is_empty() { + return; } - span_lint_and_help( - cx, - SUSPICIOUS_MAP, - expr.span, - "this call to `map()` won't have an effect on the call to `count()`", - None, - "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`", - ); } + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + expr.span, + "this call to `map()` won't have an effect on the call to `count()`", + None, + "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`", + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index 3cb2719e4a0..c45212581ee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_note; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -8,41 +7,36 @@ use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { - if_chain! { - if count <= 1; - if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(call_id); - if cx.tcx.impl_trait_ref(impl_id).is_none(); - let self_ty = cx.tcx.type_of(impl_id).instantiate_identity(); - if self_ty.is_slice() || self_ty.is_str(); - then { - // Ignore empty slice and string literals when used with a literal count. - if matches!(self_arg.kind, ExprKind::Array([])) - || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) - { - return; - } + if count <= 1 + && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(call_id) + && cx.tcx.impl_trait_ref(impl_id).is_none() + && let self_ty = cx.tcx.type_of(impl_id).instantiate_identity() + && (self_ty.is_slice() || self_ty.is_str()) + { + // Ignore empty slice and string literals when used with a literal count. + if matches!(self_arg.kind, ExprKind::Array([])) + || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) + { + return; + } - let (msg, note_msg) = if count == 0 { - (format!("`{method_name}` called with `0` splits"), - "the resulting iterator will always return `None`") - } else { - (format!("`{method_name}` called with `1` split"), + let (msg, note_msg) = if count == 0 { + ( + format!("`{method_name}` called with `0` splits"), + "the resulting iterator will always return `None`", + ) + } else { + ( + format!("`{method_name}` called with `1` split"), if self_ty.is_slice() { "the resulting iterator will always return the entire slice followed by `None`" } else { "the resulting iterator will always return the entire string followed by `None`" - }) - }; + }, + ) + }; - span_lint_and_note( - cx, - SUSPICIOUS_SPLITN, - expr.span, - &msg, - None, - note_msg, - ); - } + span_lint_and_note(cx, SUSPICIOUS_SPLITN, expr.span, &msg, None, note_msg); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs index 9eb8d6e6e78..60864902a48 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,40 +11,37 @@ use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { - if_chain! { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, method_def_id, sym::ToOwned); - let input_type = cx.typeck_results().expr_ty(expr); - if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind(); - if cx.tcx.is_diagnostic_item(sym::Cow, adt.did()); - - then { - let mut app = Applicability::MaybeIncorrect; - let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_then( - cx, - SUSPICIOUS_TO_OWNED, - expr.span, - &with_forced_trimmed_paths!(format!( - "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" - )), - |diag| { - diag.span_suggestion( - expr.span, - "depending on intent, either make the Cow an Owned variant", - format!("{recv_snip}.into_owned()"), - app - ); - diag.span_suggestion( - expr.span, - "or clone the Cow itself", - format!("{recv_snip}.clone()"), - app - ); - } - ); - return true; - } + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && is_diag_trait_item(cx, method_def_id, sym::ToOwned) + && let input_type = cx.typeck_results().expr_ty(expr) + && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind() + && cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) + { + let mut app = Applicability::MaybeIncorrect; + let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; + span_lint_and_then( + cx, + SUSPICIOUS_TO_OWNED, + expr.span, + &with_forced_trimmed_paths!(format!( + "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" + )), + |diag| { + diag.span_suggestion( + expr.span, + "depending on intent, either make the Cow an Owned variant", + format!("{recv_snip}.into_owned()"), + app, + ); + diag.span_suggestion( + expr.span, + "or clone the Cow itself", + format!("{recv_snip}.clone()"), + app, + ); + }, + ); + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs index bc9c518dbcf..1ee655d61e1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_path_diagnostic_item; use clippy_utils::ty::is_uninit_value_valid_for_ty; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -10,18 +9,16 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Call(callee, args) = recv.kind; - if args.is_empty(); - if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit); - if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)); - then { - span_lint( - cx, - UNINIT_ASSUMED_INIT, - expr.span, - "this call for this type may be undefined behavior" - ); - } + if let hir::ExprKind::Call(callee, args) = recv.kind + && args.is_empty() + && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) + && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) + { + span_lint( + cx, + UNINIT_ASSUMED_INIT, + expr.span, + "this call for this type may be undefined behavior", + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs index bb32b1bb7fc..89cf20c14fc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs @@ -1,10 +1,11 @@ -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::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_span::{sym, Span}; use super::UNNECESSARY_FALLIBLE_CONVERSIONS; @@ -42,6 +43,7 @@ fn check<'tcx>( // (else there would be conflicting impls, even with #![feature(spec)]), so we don't even need to check // what `<T as TryFrom<U>>::Error` is: it's always `Infallible` && implements_trait(cx, self_ty, from_into_trait, &[other_ty]) + && let Some(other_ty) = other_ty.as_type() { let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| { if let ExprKind::MethodCall(path, .., span) = parent.kind @@ -52,8 +54,7 @@ fn check<'tcx>( None } }); - - let (sugg, span, applicability) = match kind { + let (source_ty, target_ty, sugg, span, applicability) = match kind { FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => { // Extend the span to include the unwrap/expect call: // `foo.try_into().expect("..")` @@ -63,24 +64,41 @@ fn check<'tcx>( // so that can be machine-applicable ( + self_ty, + other_ty, "into()", primary_span.with_hi(unwrap_span.hi()), Applicability::MachineApplicable, ) }, - FunctionKind::TryFromFunction => ("From::from", primary_span, Applicability::Unspecified), - FunctionKind::TryIntoFunction => ("Into::into", primary_span, Applicability::Unspecified), - FunctionKind::TryIntoMethod => ("into", primary_span, Applicability::Unspecified), + FunctionKind::TryFromFunction => ( + other_ty, + self_ty, + "From::from", + primary_span, + Applicability::Unspecified, + ), + FunctionKind::TryIntoFunction => ( + self_ty, + other_ty, + "Into::into", + primary_span, + Applicability::Unspecified, + ), + FunctionKind::TryIntoMethod => (self_ty, other_ty, "into", primary_span, Applicability::Unspecified), }; - span_lint_and_sugg( + span_lint_and_then( cx, UNNECESSARY_FALLIBLE_CONVERSIONS, span, "use of a fallible conversion when an infallible one could be used", - "use", - sugg.into(), - applicability, + |diag| { + with_forced_trimmed_paths!({ + diag.note(format!("converting `{source_ty}` to `{target_ty}` cannot fail")); + }); + diag.span_suggestion(span, "use", sugg, applicability); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 6d51c4ab054..ebbdde48b45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -60,57 +59,51 @@ fn check_fold_with_op( op: hir::BinOpKind, replacement: Replacement, ) { - if_chain! { + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind // Extract the body of the closure passed to fold - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind; - let closure_body = cx.tcx.hir().body(body); - let closure_expr = peel_blocks(closure_body.value); + && let closure_body = cx.tcx.hir().body(body) + && let closure_expr = peel_blocks(closure_body.value) // Check if the closure body is of the form `acc <op> some_expr(x)` - if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind; - if bin_op.node == op; + && let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind + && bin_op.node == op // Extract the names of the two arguments to the closure - if let [param_a, param_b] = closure_body.params; - if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind; - if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind; + && let [param_a, param_b] = closure_body.params + && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind + && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - if path_to_local_id(left_expr, first_arg_id); - if replacement.has_args || path_to_local_id(right_expr, second_arg_id); - - then { - let mut applicability = Applicability::MachineApplicable; - - let turbofish = if replacement.has_generic_return { - format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()) - } else { - String::new() - }; - - let sugg = if replacement.has_args { - format!( - "{method}{turbofish}(|{second_arg_ident}| {r})", - method = replacement.method_name, - r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), - ) - } else { - format!( - "{method}{turbofish}()", - method = replacement.method_name, - ) - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_FOLD, - fold_span.with_hi(expr.span.hi()), - // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) - "this `.fold` can be written more succinctly using another method", - "try", - sugg, - applicability, - ); - } + && path_to_local_id(left_expr, first_arg_id) + && (replacement.has_args || path_to_local_id(right_expr, second_arg_id)) + { + let mut applicability = Applicability::MachineApplicable; + + let turbofish = if replacement.has_generic_return { + format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()) + } else { + String::new() + }; + + let sugg = if replacement.has_args { + format!( + "{method}{turbofish}(|{second_arg_ident}| {r})", + method = replacement.method_name, + r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), + ) + } else { + format!("{method}{turbofish}()", method = replacement.method_name,) + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_FOLD, + fold_span.with_hi(expr.span.hi()), + // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) + "this `.fold` can be written more succinctly using another method", + "try", + sugg, + applicability, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 0c72c13a3ca..36497d59a5a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -13,15 +13,13 @@ use rustc_span::{sym, Symbol}; use super::UNNECESSARY_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let Some(callee_def_id) = fn_def_id(cx, parent); - if is_into_iter(cx, callee_def_id); - then { - check_for_loop_iter(cx, parent, method_name, receiver, false) - } else { - false - } + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(callee_def_id) = fn_def_id(cx, parent) + && is_into_iter(cx, callee_def_id) + { + check_for_loop_iter(cx, parent, method_name, receiver, false) + } else { + false } } @@ -36,65 +34,58 @@ pub fn check_for_loop_iter( receiver: &Expr<'_>, cloned_before_iter: bool, ) -> bool { - if_chain! { - if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); - if let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent); - let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body); - if !clone_or_copy_needed; - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - let snippet = if_chain! { - if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind; - if maybe_iter_method_name.ident.name == sym::iter; - - if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - let receiver_ty = cx.typeck_results().expr_ty(receiver); - if implements_trait(cx, receiver_ty, iterator_trait_id, &[]); - if let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty); - - if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator); - let collection_ty = cx.typeck_results().expr_ty(collection); - if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]); - if let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item"); - - if iter_item_ty == into_iter_item_ty; - if let Some(collection_snippet) = snippet_opt(cx, collection.span); - then { - collection_snippet + if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)) + && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) + && let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body) + && !clone_or_copy_needed + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind + && maybe_iter_method_name.ident.name == sym::iter + && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let receiver_ty = cx.typeck_results().expr_ty(receiver) + && implements_trait(cx, receiver_ty, iterator_trait_id, &[]) + && let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty) + && let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let collection_ty = cx.typeck_results().expr_ty(collection) + && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) + && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item") + && iter_item_ty == into_iter_item_ty + && let Some(collection_snippet) = snippet_opt(cx, collection.span) + { + collection_snippet + } else { + receiver_snippet + }; + span_lint_and_then( + cx, + UNNECESSARY_TO_OWNED, + expr.span, + &format!("unnecessary use of `{method_name}`"), + |diag| { + // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to + // a `to_owned`-like function was removed, then the next suggestion may be + // incorrect. This is because the iterator that results from the call's removal + // could hold a reference to a resource that is used mutably. See + // https://github.com/rust-lang/rust-clippy/issues/8148. + let applicability = if cloned_before_iter { + Applicability::MaybeIncorrect } else { - receiver_snippet - } - }; - span_lint_and_then( - cx, - UNNECESSARY_TO_OWNED, - expr.span, - &format!("unnecessary use of `{method_name}`"), - |diag| { - // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to - // a `to_owned`-like function was removed, then the next suggestion may be - // incorrect. This is because the iterator that results from the call's removal - // could hold a reference to a resource that is used mutably. See - // https://github.com/rust-lang/rust-clippy/issues/8148. - let applicability = if cloned_before_iter { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - diag.span_suggestion(expr.span, "use", snippet, applicability); - for addr_of_expr in addr_of_exprs { - match addr_of_expr.kind { - ExprKind::AddrOf(_, _, referent) => { - let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", "", applicability); - } - _ => unreachable!(), - } + Applicability::MachineApplicable + }; + diag.span_suggestion(expr.span, "use", snippet, applicability); + for addr_of_expr in addr_of_exprs { + match addr_of_expr.kind { + ExprKind::AddrOf(_, _, referent) => { + let span = addr_of_expr.span.with_hi(referent.span.lo()); + diag.span_suggestion(span, "remove this `&`", "", applicability); + }, + _ => unreachable!(), } } - ); - return true; - } + }, + ); + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs index d0c62fb56dc..e2b389e96da 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs @@ -18,25 +18,23 @@ pub(super) fn check<'tcx>( ) { let applicability = Applicability::MachineApplicable; let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); - if_chain! { + if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind() // the turbofish for collect is ::<Vec<String>> - if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); - if let Slice(slice) = ref_type.kind(); - if is_type_lang_item(cx, *slice, LangItem::String); + && let Slice(slice) = ref_type.kind() + && is_type_lang_item(cx, *slice, LangItem::String) // the argument for join is "" - if let ExprKind::Lit(spanned) = &join_arg.kind; - if let LitKind::Str(symbol, _) = spanned.node; - if symbol.is_empty(); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_JOIN, - span.with_hi(expr.span.hi()), - r#"called `.collect::<Vec<String>>().join("")` on an iterator"#, - "try using", - "collect::<String>()".to_owned(), - applicability, - ); - } + && let ExprKind::Lit(spanned) = &join_arg.kind + && let LitKind::Str(symbol, _) = spanned.node + && symbol.is_empty() + { + span_lint_and_sugg( + cx, + UNNECESSARY_JOIN, + span.with_hi(expr.span.hi()), + r#"called `.collect::<Vec<String>>().join("")` on an iterator"#, + "try using", + "collect::<String>()".to_owned(), + applicability, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index e62a65a2712..696e5e74d60 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -115,55 +114,72 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if cx.tcx.type_of(impl_id).instantiate_identity().is_slice(); - if let ExprKind::Closure(&Closure { body, .. }) = arg.kind; - if let closure_body = cx.tcx.hir().body(body); - if let &[ - Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, - Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } - ] = &closure_body.params; - if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind; - if method_path.ident.name == sym::cmp; - if is_trait_method(cx, closure_body.value, sym::Ord); - then { - let (closure_body, closure_arg, reverse) = if mirrored_exprs( - left_expr, - left_ident, - right_expr, - right_ident - ) { - (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false) - } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) { - (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true) - } else { - return None; - }; - let vec_name = Sugg::hir(cx, recv, "..").to_string(); + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && cx.tcx.type_of(impl_id).instantiate_identity().is_slice() + && let ExprKind::Closure(&Closure { body, .. }) = arg.kind + && let closure_body = cx.tcx.hir().body(body) + && let &[ + Param { + pat: + Pat { + kind: PatKind::Binding(_, _, left_ident, _), + .. + }, + .. + }, + Param { + pat: + Pat { + kind: PatKind::Binding(_, _, right_ident, _), + .. + }, + .. + }, + ] = &closure_body.params + && let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind + && method_path.ident.name == sym::cmp + && is_trait_method(cx, closure_body.value, sym::Ord) + { + let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) { + ( + Sugg::hir(cx, left_expr, "..").to_string(), + left_ident.name.to_string(), + false, + ) + } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) { + ( + Sugg::hir(cx, left_expr, "..").to_string(), + right_ident.name.to_string(), + true, + ) + } else { + return None; + }; + let vec_name = Sugg::hir(cx, recv, "..").to_string(); - if_chain! { - if let ExprKind::Path(QPath::Resolved(_, Path { - segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind; - if left_name == left_ident; - if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) - }); - then { - return Some(LintTrigger::Sort(SortDetection { vec_name })); - } - } + if let ExprKind::Path(QPath::Resolved( + _, + Path { + segments: [PathSegment { ident: left_name, .. }], + .. + }, + )) = &left_expr.kind + && left_name == left_ident + && cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) + }) + { + return Some(LintTrigger::Sort(SortDetection { vec_name })); + } - if !expr_borrows(cx, left_expr) { - return Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - closure_arg, - closure_body, - reverse, - })); - } + if !expr_borrows(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + closure_arg, + closure_body, + reverse, + })); } } 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 772686d93dd..395b22ebc5d 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 @@ -32,25 +32,23 @@ pub fn check<'tcx>( args: &'tcx [Expr<'_>], msrv: &Msrv, ) { - if_chain! { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if args.is_empty(); - then { - if is_cloned_or_copied(cx, method_name, method_def_id) { - unnecessary_iter_cloned::check(cx, expr, method_name, receiver); - } else if is_to_owned_like(cx, expr, method_name, method_def_id) { - // At this point, we know the call is of a `to_owned`-like function. The functions - // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary - // based on its context, that is, whether it is a referent in an `AddrOf` expression, an - // argument in a `into_iter` call, or an argument in the call of some other function. - if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { - return; - } - if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { - return; - } - check_other_call_arg(cx, expr, method_name, receiver); + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && args.is_empty() + { + if is_cloned_or_copied(cx, method_name, method_def_id) { + unnecessary_iter_cloned::check(cx, expr, method_name, receiver); + } else if is_to_owned_like(cx, expr, method_name, method_def_id) { + // At this point, we know the call is of a `to_owned`-like function. The functions + // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary + // based on its context, that is, whether it is a referent in an `AddrOf` expression, an + // argument in a `into_iter` call, or an argument in the call of some other function. + if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + return; } + if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { + return; + } + check_other_call_arg(cx, expr, method_name, receiver); } } } @@ -65,11 +63,10 @@ fn check_addr_of_expr( method_def_id: DefId, receiver: &Expr<'_>, ) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind; - let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>(); - if let + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind + && let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>() + && let // For matching uses of `Cow::from` [ Adjustment { @@ -110,10 +107,10 @@ fn check_addr_of_expr( kind: Adjust::Borrow(_), target: target_ty, }, - ] = 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); + ] = 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) // 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 @@ -123,77 +120,72 @@ fn check_addr_of_expr( // https://github.com/rust-lang/rust-clippy/issues/8759 // Arrays are a bit of a corner case. Non-copyable arrays are handled by // `redundant_clone`, but copyable arrays are not. - if *referent_ty != receiver_ty + && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) - || is_cow_into_owned(cx, method_name, method_def_id); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { + || is_cow_into_owned(cx, method_name, method_def_id)) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!( + "{:&>width$}{receiver_snippet}", + "", + width = n_target_refs - n_receiver_refs + ), + Applicability::MachineApplicable, + ); + return true; + } + if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) + && implements_trait(cx, receiver_ty, deref_trait_id, &[]) + && cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty) + // Make sure that it's actually calling the right `.to_string()`, (#10033) + // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) + // but that's ok for Cow::into_owned specifically) + && (cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty + || is_cow_into_owned(cx, method_name, method_def_id)) + { + if n_receiver_refs > 0 { span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, parent.span, &format!("unnecessary use of `{method_name}`"), "use", - format!( - "{:&>width$}{receiver_snippet}", - "", - width = n_target_refs - n_receiver_refs - ), + receiver_snippet, + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + expr.span.with_lo(receiver.span.hi()), + &format!("unnecessary use of `{method_name}`"), + "remove this", + String::new(), Applicability::MachineApplicable, ); - return true; - } - if_chain! { - if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); - if implements_trait(cx, receiver_ty, deref_trait_id, &[]); - if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty); - // Make sure that it's actually calling the right `.to_string()`, (#10033) - // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) - // but that's ok for Cow::into_owned specifically) - if cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty - || is_cow_into_owned(cx, method_name, method_def_id); - then { - if n_receiver_refs > 0 { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - parent.span, - &format!("unnecessary use of `{method_name}`"), - "use", - receiver_snippet, - Applicability::MachineApplicable, - ); - } else { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - expr.span.with_lo(receiver.span.hi()), - &format!("unnecessary use of `{method_name}`"), - "remove this", - String::new(), - Applicability::MachineApplicable, - ); - } - return true; - } - } - if_chain! { - if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); - if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - parent.span, - &format!("unnecessary use of `{method_name}`"), - "use", - format!("{receiver_snippet}.as_ref()"), - Applicability::MachineApplicable, - ); - return true; - } } + return true; + } + if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) + && implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]) + { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!("{receiver_snippet}.as_ref()"), + Applicability::MachineApplicable, + ); + return true; } } false @@ -208,38 +200,36 @@ fn check_into_iter_call_arg( receiver: &Expr<'_>, msrv: &Msrv, ) -> bool { - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let Some(callee_def_id) = fn_def_id(cx, parent); - if is_into_iter(cx, callee_def_id); - if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - let parent_ty = cx.typeck_results().expr_ty(parent); - if implements_trait(cx, parent_ty, iterator_trait_id, &[]); - if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { - return true; - } - let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { - "copied" - } else { - "cloned" - }; - // The next suggestion may be incorrect because the removal of the `to_owned`-like - // function could cause the iterator to hold a reference to a resource that is used - // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - parent.span, - &format!("unnecessary use of `{method_name}`"), - "use", - format!("{receiver_snippet}.iter().{cloned_or_copied}()"), - Applicability::MaybeIncorrect, - ); + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(callee_def_id) = fn_def_id(cx, parent) + && is_into_iter(cx, callee_def_id) + && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let parent_ty = cx.typeck_results().expr_ty(parent) + && implements_trait(cx, parent_ty, iterator_trait_id, &[]) + && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { + "copied" + } else { + "cloned" + }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like + // function could cause the iterator to hold a reference to a resource that is used + // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + parent.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!("{receiver_snippet}.iter().{cloned_or_copied}()"), + Applicability::MaybeIncorrect, + ); + return true; } false } @@ -252,26 +242,25 @@ fn check_other_call_arg<'tcx>( method_name: Symbol, receiver: &'tcx Expr<'tcx>, ) -> bool { - if_chain! { - if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr); - if let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call); - let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); - if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id); - if let Some(input) = fn_sig.inputs().get(i); - let (input, n_refs) = peel_mid_ty_refs(*input); - if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input); - if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait(); - if let [trait_predicate] = trait_predicates + if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr) + && let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call) + && 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 (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 .iter() .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id) - .collect::<Vec<_>>()[..]; - if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); - if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); - if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id; - let receiver_ty = cx.typeck_results().expr_ty(receiver); + .collect::<Vec<_>>()[..] + && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) + && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) + && (trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id) + && let receiver_ty = cx.typeck_results().expr_ty(receiver) // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. - if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { + && let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { Some((n_refs, receiver_ty)) } else if trait_predicate.def_id() != deref_trait_id { Some((1, Ty::new_ref(cx.tcx, @@ -283,21 +272,20 @@ fn check_other_call_arg<'tcx>( ))) } else { None - }; - if can_change_type(cx, maybe_arg, receiver_ty); - if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_TO_OWNED, - maybe_arg.span, - &format!("unnecessary use of `{method_name}`"), - "use", - format!("{:&>n_refs$}{receiver_snippet}", ""), - Applicability::MachineApplicable, - ); - return true; } + && can_change_type(cx, maybe_arg, receiver_ty) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + { + span_lint_and_sugg( + cx, + UNNECESSARY_TO_OWNED, + maybe_arg.span, + &format!("unnecessary use of `{method_name}`"), + "use", + format!("{:&>n_refs$}{receiver_snippet}", ""), + Applicability::MachineApplicable, + ); + return true; } false } @@ -329,22 +317,18 @@ fn get_callee_generic_args_and_args<'tcx>( Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>], )> { - if_chain! { - if let ExprKind::Call(callee, args) = expr.kind; - let callee_ty = cx.typeck_results().expr_ty(callee); - if let ty::FnDef(callee_def_id, _) = callee_ty.kind(); - then { - let generic_args = cx.typeck_results().node_args(callee.hir_id); - return Some((*callee_def_id, generic_args, None, args)); - } + if let ExprKind::Call(callee, args) = expr.kind + && let callee_ty = cx.typeck_results().expr_ty(callee) + && let ty::FnDef(callee_def_id, _) = callee_ty.kind() + { + let generic_args = cx.typeck_results().node_args(callee.hir_id); + return Some((*callee_def_id, generic_args, None, args)); } - if_chain! { - if let ExprKind::MethodCall(_, recv, args, _) = expr.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - then { - let generic_args = cx.typeck_results().node_args(expr.hir_id); - return Some((method_def_id, generic_args, Some(recv), args)); - } + if let ExprKind::MethodCall(_, recv, args, _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + { + let generic_args = cx.typeck_results().node_args(expr.hir_id); + return Some((method_def_id, generic_args, Some(recv), args)); } None } 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 b5f810eddf4..84ee64e88a6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; use clippy_utils::{get_parent_expr, is_trait_method}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -22,13 +21,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { // allow the `as_ref` or `as_mut` if it is followed by another method call - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::MethodCall(segment, ..) = parent.kind; - if segment.ident.span != expr.span; - then { - return; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let hir::ExprKind::MethodCall(segment, ..) = parent.kind + && segment.ident.span != expr.span + { + return; } let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 9f1f73e6021..9ad4250a141 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -1,7 +1,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; -use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -55,32 +54,33 @@ pub(super) fn get_hint_if_single_char_arg( arg: &hir::Expr<'_>, applicability: &mut Applicability, ) -> Option<String> { - if_chain! { - if let hir::ExprKind::Lit(lit) = &arg.kind; - if let ast::LitKind::Str(r, style) = lit.node; - let string = r.as_str(); - if string.chars().count() == 1; - then { - let snip = snippet_with_applicability(cx, arg.span, string, applicability); - let ch = if let ast::StrStyle::Raw(nhash) = style { - let nhash = nhash as usize; - // for raw string: r##"a"## - &snip[(nhash + 2)..(snip.len() - 1 - nhash)] - } else { - // for regular string: "a" - &snip[1..(snip.len() - 1)] - }; + if let hir::ExprKind::Lit(lit) = &arg.kind + && let ast::LitKind::Str(r, style) = lit.node + && let string = r.as_str() + && string.chars().count() == 1 + { + let snip = snippet_with_applicability(cx, arg.span, string, applicability); + let ch = if let ast::StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; - let hint = format!("'{}'", match ch { - "'" => "\\'" , + let hint = format!( + "'{}'", + match ch { + "'" => "\\'", r"\" => "\\\\", _ => ch, - }); + } + ); - Some(hint) - } else { - None - } + Some(hint) + } else { + None } } @@ -140,15 +140,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { return; }, ExprKind::MethodCall(.., args, _) => { - if_chain! { - if args.iter().all(|arg| !self.is_binding(arg)); - if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id); - let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity(); - let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder(); - if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)); - then { - return; - } + if args.iter().all(|arg| !self.is_binding(arg)) + && let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity() + && let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder() + && matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)) + { + return; } }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs index 73072718678..9e87fb45aa6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -17,29 +16,32 @@ pub(super) fn check<'tcx>( default_arg: &'tcx Expr<'_>, name_span: Span, ) { - if_chain! { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if let Some(impl_id) = cx.tcx.impl_of_method(method_id); - if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec); - if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind; - if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind; - then { - let method_call_span = expr.span.with_lo(name_span.lo()); - span_lint_and_then( - cx, - VEC_RESIZE_TO_ZERO, - expr.span, - "emptying a vector with `resize`", - |db| { - db.help("the arguments may be inverted..."); - db.span_suggestion( - method_call_span, - "...or you can empty the vector with", - "clear()".to_string(), - Applicability::MaybeIncorrect, - ); - }, - ); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) + && let ExprKind::Lit(Spanned { + node: LitKind::Int(0, _), + .. + }) = count_arg.kind + && let ExprKind::Lit(Spanned { + node: LitKind::Int(..), .. + }) = default_arg.kind + { + let method_call_span = expr.span.with_lo(name_span.lo()); + span_lint_and_then( + cx, + VEC_RESIZE_TO_ZERO, + expr.span, + "emptying a vector with `resize`", + |db| { + db.help("the arguments may be inverted..."); + db.span_suggestion( + method_call_span, + "...or you can empty the vector with", + "clear()".to_string(), + Applicability::MaybeIncorrect, + ); + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs index e9f268da691..0b829d99aef 100644 --- a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs +++ b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; @@ -7,12 +6,10 @@ use rustc_middle::ty; use super::ZST_OFFSET; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if_chain! { - if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); - if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty)); - if layout.is_zst(); - then { - span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); - } + if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind() + && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty)) + && layout.is_zst() + { + span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); } } diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index f4af5f37bf3..814fc3303b0 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -5,7 +5,6 @@ use clippy_utils::{ any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats, last_path_segment, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; @@ -143,73 +142,64 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, stmt.span); - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind; - if let Some(init) = local.init; + if !in_external_macro(cx.tcx.sess, stmt.span) + && let StmtKind::Local(local) = stmt.kind + && let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind + && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - if is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id); - then { - let ctxt = local.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ty) = local.ty { - let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; - format!(": &{mutopt}{ty_snip}") - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!( - "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, ".."), - ), - app, - ); - } - ); - } + && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + { + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); + let (mutopt, initref) = if mutabl == Mutability::Mut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ty) = local.ty { + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + app, + ); + }, + ); }; - if_chain! { - if let StmtKind::Semi(expr) = stmt.kind; - if let ExprKind::Binary(ref binop, a, b) = expr.kind; - if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; - if let Some(sugg) = Sugg::hir_opt(cx, a); - then { - span_lint_hir_and_then( - cx, - SHORT_CIRCUIT_STATEMENT, - expr.hir_id, - stmt.span, - "boolean short circuit operator in statement may be clearer using an explicit test", - |diag| { - let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; - diag.span_suggestion( - stmt.span, - "replace it with", - format!( - "if {sugg} {{ {}; }}", - &snippet(cx, b.span, ".."), - ), - Applicability::MachineApplicable, // snippet - ); - }); - } + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Binary(ref binop, a, b) = expr.kind + && (binop.node == BinOpKind::And || binop.node == BinOpKind::Or) + && let Some(sugg) = Sugg::hir_opt(cx, a) + { + span_lint_hir_and_then( + cx, + SHORT_CIRCUIT_STATEMENT, + expr.hir_id, + stmt.span, + "boolean short circuit operator in statement may be clearer using an explicit test", + |diag| { + let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; + diag.span_suggestion( + stmt.span, + "replace it with", + format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),), + Applicability::MachineApplicable, // snippet + ); + }, + ); }; } diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index 0d79ece087f..c74d0d623df 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -49,59 +49,59 @@ declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if_chain! { - if !item.span.from_expansion(); - if let ItemKind::Impl(imp) = &item.kind; - if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind; - if let Some(segment) = path.segments.iter().next(); - if let Some(generic_args) = segment.args; - if !generic_args.args.is_empty(); - then { - // get the name and span of the generic parameters in the Impl - let mut impl_params = Vec::new(); - for p in generic_args.args { - match p { - GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => - impl_params.push((path.segments[0].ident.to_string(), path.span)), - GenericArg::Type(_) => return, - _ => (), - }; - } - - // find the type that the Impl is for - // only lint on struct/enum/union for now - let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { - return + if !item.span.from_expansion() + && let ItemKind::Impl(imp) = &item.kind + && let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind + && let Some(segment) = path.segments.iter().next() + && let Some(generic_args) = segment.args + && !generic_args.args.is_empty() + { + // get the name and span of the generic parameters in the Impl + let mut impl_params = Vec::new(); + for p in generic_args.args { + match p { + GenericArg::Type(Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }) => impl_params.push((path.segments[0].ident.to_string(), path.span)), + GenericArg::Type(_) => return, + _ => (), }; + } + + // find the type that the Impl is for + // only lint on struct/enum/union for now + let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { + return; + }; - // get the names of the generic parameters in the type - let type_params = &cx.tcx.generics_of(defid).params; - let type_param_names: Vec<_> = type_params.iter() - .filter_map(|p| - match p.kind { - GenericParamDefKind::Type {..} => Some(p.name.to_string()), - _ => None, - } - ).collect(); - // hashmap of name -> index for mismatch_param_name - let type_param_names_hashmap: FxHashMap<&String, usize> = - type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); + // get the names of the generic parameters in the type + let type_params = &cx.tcx.generics_of(defid).params; + let type_param_names: Vec<_> = type_params + .iter() + .filter_map(|p| match p.kind { + GenericParamDefKind::Type { .. } => Some(p.name.to_string()), + _ => None, + }) + .collect(); + // hashmap of name -> index for mismatch_param_name + let type_param_names_hashmap: FxHashMap<&String, usize> = type_param_names + .iter() + .enumerate() + .map(|(i, param)| (param, i)) + .collect(); - let type_name = segment.ident; - for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { - if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { - let msg = format!("`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order"); - let help = format!("try `{}`, or a name that does not conflict with `{type_name}`'s generic params", - type_param_names[i]); - span_lint_and_help( - cx, - MISMATCHING_TYPE_PARAM_ORDER, - *impl_param_span, - &msg, - None, - &help - ); - } + let type_name = segment.ident; + for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { + if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { + let msg = format!( + "`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order" + ); + let help = format!( + "try `{}`, or a name that does not conflict with `{type_name}`'s generic params", + type_param_names[i] + ); + span_lint_and_help(cx, MISMATCHING_TYPE_PARAM_ORDER, *impl_param_span, &msg, None, &help); } } } diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index dccf72d3c84..ff2792faf57 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -57,7 +57,7 @@ declare_clippy_lint! { /// v[0] + v[1] + v[2] + v[3] /// } /// ``` - #[clippy::version = "1.70.0"] + #[clippy::version = "1.74.0"] pub MISSING_ASSERTS_FOR_INDEXING, restriction, "indexing into a slice multiple times without an `assert`" diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 973caa72b77..b5a884f7c8b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -8,7 +8,6 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; -use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -63,16 +62,14 @@ impl MissingDoc { } fn has_include(meta: Option<MetaItem>) -> bool { - if_chain! { - if let Some(meta) = meta; - if let MetaItemKind::List(list) = meta.kind; - if let Some(meta) = list.first(); - if let Some(name) = meta.ident(); - then { - name.name == sym::include - } else { - false - } + if let Some(meta) = meta + && let MetaItemKind::List(list) = meta.kind + && let Some(meta) = list.first() + && let Some(name) = meta.ident() + { + name.name == sym::include + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 16ff98a5922..f7e42815104 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -72,13 +72,12 @@ impl LateLintPass<'_> for ImportRename { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { for &res in &path.res { - if_chain! { - if let Res::Def(_, id) = res; - if let Some(name) = self.renames.get(&id); + if let Res::Def(_, id) = res + && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports - let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); - if let Some(snip) = snippet_opt(cx, span_without_semi); - if let Some(import) = match snip.split_once(" as ") { + && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') + && let Some(snip) = snippet_opt(cx, span_without_semi) + && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { if rename.trim() == name.as_str() { @@ -87,20 +86,17 @@ impl LateLintPass<'_> for ImportRename { Some(import.trim()) } }, - }; - then { - span_lint_and_sugg( - cx, - MISSING_ENFORCED_IMPORT_RENAMES, - span_without_semi, - "this import should be renamed", - "try", - format!( - "{import} as {name}", - ), - Applicability::MachineApplicable, - ); } + { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!("{import} as {name}",), + Applicability::MachineApplicable, + ); } } } 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 215161b04c7..e5ebdc14510 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,6 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; -use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -80,11 +79,13 @@ declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVER impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. - let var = if_chain! { - if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind; - if let Some(var) = path_to_local(lhs); - if expr.span.desugaring_kind().is_none(); - then { var } else { return; } + let var = if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind + && let Some(var) = path_to_local(lhs) + && expr.span.desugaring_kind().is_none() + { + var + } else { + return; }; let mut visitor = ReadVisitor { cx, @@ -164,7 +165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let sig = typ.fn_sig(self.cx.tcx); - if self.cx.tcx.erase_late_bound_regions(sig).output().kind() == &ty::Never { + if self.cx.tcx.instantiate_bound_regions_with_erased(sig).output().kind() == &ty::Never { self.report_diverging_sub_expr(e); } }, diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index efdc7560ee4..cd45467407e 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; @@ -124,11 +125,13 @@ impl EarlyLintPass for ModStyle { correct.pop(); correct.push(folder); correct.push("mod.rs"); - cx.struct_span_lint( + span_lint_and_help( + 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()), - |lint| lint.help(format!("move `{}` to `{}`", path.display(), correct.display(),)), + &format!("`mod.rs` files are required, found `{}`", path.display()), + None, + &format!("move `{}` to `{}`", path.display(), correct.display(),), ); } } @@ -153,17 +156,22 @@ fn process_paths_for_mod_files<'a>( } /// Checks every path for the presence of `mod.rs` files and emits the lint if found. +/// We should not emit a lint for test modules in the presence of `mod.rs`. +/// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test) +/// for code-sharing between tests. fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { - if path.ends_with("mod.rs") { + 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"); - cx.struct_span_lint( + span_lint_and_help( + 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()), - |lint| lint.help(format!("move `{}` to `{}`", path.display(), mod_file.display())), + &format!("`mod.rs` files are not allowed, found `{}`", path.display()), + None, + &format!("move `{}` to `{}`", path.display(), mod_file.display()), ); } } diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index ebfd53f1ee9..454fc6f5642 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -8,8 +8,8 @@ use rustc_middle::query::Key; use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::Span; use std::iter; declare_clippy_lint! { @@ -143,7 +143,7 @@ impl MutableKeyType { for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { self.check_ty_(cx, hir_ty.span, *ty); } - self.check_ty_(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); + self.check_ty_(cx, decl.output.span(), cx.tcx.instantiate_bound_regions_with_erased(fn_sig.output())); } // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 97e8f1c030a..1712262ff3e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use if_chain::if_chain; use rustc_ast::ast::{BindingAnnotation, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -66,48 +65,46 @@ enum Mode { } fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { - if_chain! { - if let [segment] = &path.segments[..]; - if segment.ident.name == kw::SelfUpper; - then { - // In case we have a named lifetime, we check if the name comes from expansion. - // If it does, at this point we know the rest of the parameter was written by the user, - // so let them decide what the name of the lifetime should be. - // See #6089 for more details. - let mut applicability = Applicability::MachineApplicable; - let self_param = match (binding_mode, mutbl) { - (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => { - if lifetime.ident.span.from_expansion() { - applicability = Applicability::HasPlaceholders; - "&'_ mut self".to_string() - } else { - format!("&{} mut self", &lifetime.ident.name) - } - }, - (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => { - if lifetime.ident.span.from_expansion() { - applicability = Applicability::HasPlaceholders; - "&'_ self".to_string() - } else { - format!("&{} self", &lifetime.ident.name) - } - }, - (Mode::Value, Mutability::Mut) => "mut self".to_string(), - (Mode::Value, Mutability::Not) => "self".to_string(), - }; + if let [segment] = &path.segments[..] + && segment.ident.name == kw::SelfUpper + { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; + let self_param = match (binding_mode, mutbl) { + (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if lifetime.ident.span.from_expansion() { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, + (Mode::Ref(None), Mutability::Not) => "&self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if lifetime.ident.span.from_expansion() { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, + (Mode::Value, Mutability::Mut) => "mut self".to_string(), + (Mode::Value, Mutability::Not) => "self".to_string(), + }; - span_lint_and_sugg( - cx, - NEEDLESS_ARBITRARY_SELF_TYPE, - span, - "the type of the `self` parameter does not need to be arbitrary", - "consider to change this parameter to", - self_param, - applicability, - ) - } + span_lint_and_sugg( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter does not need to be arbitrary", + "consider to change this parameter to", + self_param, + applicability, + ); } } @@ -125,12 +122,10 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { } }, TyKind::Ref(lifetime, mut_ty) => { - if_chain! { - if let TyKind::Path(None, path) = &mut_ty.ty.kind; - if let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind; - then { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); - } + if let TyKind::Path(None, path) = &mut_ty.ty.kind + && let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind + { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); } }, _ => {}, 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 dcfb109a4c4..f25475aaa8e 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 @@ -50,7 +50,7 @@ declare_clippy_lint! { /// let x = "foo"; /// f(x); /// ``` - #[clippy::version = "pre 1.29.0"] + #[clippy::version = "1.74.0"] pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS, style, "taking a reference that is going to be automatically dereferenced" diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index cb2738947d4..6803034f475 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -362,21 +362,19 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin } fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if_chain! { - if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind; - if let Some(last_stmt) = loop_block.stmts.last(); - if let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind; - if let ast::ExprKind::Continue(_) = inner_expr.kind; - then { - span_lint_and_help( - cx, - NEEDLESS_CONTINUE, - last_stmt.span, - MSG_REDUNDANT_CONTINUE_EXPRESSION, - None, - DROP_CONTINUE_EXPRESSION_MSG, - ); - } + if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind + && let Some(last_stmt) = loop_block.stmts.last() + && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + && let ast::ExprKind::Continue(_) = inner_expr.kind + { + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + last_stmt.span, + MSG_REDUNDANT_CONTINUE_EXPRESSION, + None, + DROP_CONTINUE_EXPRESSION_MSG, + ); } with_loop_block(expr, |loop_block, label| { for (i, stmt) in loop_block.stmts.iter().enumerate() { diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index c71996131db..70571d18e78 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -5,8 +5,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span, Symbol}; -use if_chain::if_chain; - use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; @@ -51,65 +49,63 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { return; }; - if_chain! { + if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind // Check the method name is `for_each`. - if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind; - if method_name.ident.name == Symbol::intern("for_each"); + && method_name.ident.name == Symbol::intern("for_each") // Check `for_each` is an associated function of `Iterator`. - if is_trait_method(cx, expr, sym::Iterator); + && is_trait_method(cx, expr, sym::Iterator) // Checks the receiver of `for_each` is also a method call. - if let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind; + && let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. - if matches!( + && matches!( iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) - ); + ) // Checks the type of the `iter` method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some(); + && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - if let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind; - let body = cx.tcx.hir().body(body); - if let ExprKind::Block(..) = body.value.kind; - then { - let mut ret_collector = RetCollector::default(); - ret_collector.visit_expr(body.value); - - // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. - if ret_collector.ret_in_loop { - return; - } - - let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { - (Applicability::MachineApplicable, None) - } else { - ( - Applicability::MaybeIncorrect, - Some( - ret_collector - .spans - .into_iter() - .map(|span| (span, "continue".to_string())) - .collect(), - ), - ) - }; + && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind + && let body = cx.tcx.hir().body(body) + && let ExprKind::Block(..) = body.value.kind + { + let mut ret_collector = RetCollector::default(); + ret_collector.visit_expr(body.value); + + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } - let sugg = format!( - "for {} in {} {}", - snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), - snippet_with_applicability(cx, body.value.span, "..", &mut applicability), - ); + let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { + (Applicability::MachineApplicable, None) + } else { + ( + Applicability::MaybeIncorrect, + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), + ), + ) + }; + + let sugg = format!( + "for {} in {} {}", + snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), + snippet_with_applicability(cx, body.value.span, "..", &mut applicability), + ); - span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { - diag.span_suggestion(stmt.span, "try", sugg, applicability); - if let Some(ret_suggs) = ret_suggs { - diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); - } - }) - } + span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); + } + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index c8888c744b6..0a95678d31a 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -128,21 +128,18 @@ impl LocalAssign { let assign = match expr.kind { ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span), ExprKind::Block(block, _) => { - if_chain! { - if let Some((last, other_stmts)) = block.stmts.split_last(); - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind; + if let Some((last, other_stmts)) = block.stmts.split_last() + && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind - let assign = Self::from_expr(expr, last.span)?; + && let assign = Self::from_expr(expr, last.span)? // avoid visiting if not needed - if assign.lhs_id == binding_id; - if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)); - - then { - Some(assign) - } else { - None - } + && assign.lhs_id == binding_id + && other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)) + { + Some(assign) + } else { + None } }, ExprKind::Assign(..) => Self::from_expr(expr, expr.span), @@ -368,22 +365,20 @@ fn check<'tcx>( impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if_chain! { - if let Local { - init: None, - pat: &Pat { + if let Local { + init: None, + pat: + &Pat { kind: PatKind::Binding(BindingAnnotation::NONE, binding_id, _, None), .. }, - source: LocalSource::Normal, - .. - } = local; - if let Some((_, Node::Stmt(local_stmt))) = parents.next(); - if let Some((_, Node::Block(block))) = parents.next(); - - then { - check(cx, local, local_stmt, block, binding_id); - } + source: LocalSource::Normal, + .. + } = local + && let Some((_, Node::Stmt(local_stmt))) = parents.next() + && let Some((_, Node::Block(block))) = parents.next() + { + check(cx, local, local_stmt, block, binding_id); } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs index 7bbf1fb4cd9..490c3f9c1ab 100644 --- a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs @@ -53,21 +53,23 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { // don't check floating point literals on the start expression of a range return; } - if_chain! { - if let ExprKind::Lit(literal) = e.kind; + if let ExprKind::Lit(literal) = e.kind // the indicator that parenthesis surround the literal is that the span of the expression and the literal differ - if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo); + && (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo) // inspect the source code of the expression for parenthesis - if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span, - "needless parenthesis on range literals can be removed", - |diag| { - let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); - diag.span_suggestion(e.span, "try", suggestion, applicability); - }); - } + && snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_then( + cx, + NEEDLESS_PARENS_ON_RANGE_LITERALS, + e.span, + "needless parenthesis on range literals can be removed", + |diag| { + let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); + diag.span_suggestion(e.span, "try", suggestion, applicability); + }, + ); } } 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 8fa461ac12c..7c48b84e430 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 @@ -5,7 +5,6 @@ use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::FnKind; @@ -177,118 +176,119 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { ) }; - if_chain! { - if !is_self(arg); - if !ty.is_mutable_ptr(); - if !is_copy(cx, ty); - if ty.is_sized(cx.tcx, cx.param_env); - if !allowed_traits.iter().any(|&t| implements_trait_with_env_from_iter( - cx.tcx, - cx.param_env, - ty, - t, - [Option::<ty::GenericArg<'tcx>>::None], - )); - if !implements_borrow_trait; - if !all_borrowable_trait; - - if let PatKind::Binding(BindingAnnotation(_, Mutability::Not), canonical_id, ..) = arg.pat.kind; - if !moved_vars.contains(&canonical_id); - then { - // Dereference suggestion - let sugg = |diag: &mut Diagnostic| { - if let ty::Adt(def, ..) = ty.kind() { - if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if type_allowed_to_implement_copy( - cx.tcx, - cx.param_env, - ty, - traits::ObligationCause::dummy_with_span(span), - ).is_ok() { - diag.span_help(span, "consider marking this type as `Copy`"); - } + if !is_self(arg) + && !ty.is_mutable_ptr() + && !is_copy(cx, ty) + && ty.is_sized(cx.tcx, cx.param_env) + && !allowed_traits.iter().any(|&t| { + implements_trait_with_env_from_iter( + cx.tcx, + cx.param_env, + ty, + t, + [Option::<ty::GenericArg<'tcx>>::None], + ) + }) + && !implements_borrow_trait + && !all_borrowable_trait + && let PatKind::Binding(BindingAnnotation(_, Mutability::Not), canonical_id, ..) = arg.pat.kind + && !moved_vars.contains(&canonical_id) + { + // Dereference suggestion + let sugg = |diag: &mut Diagnostic| { + if let ty::Adt(def, ..) = ty.kind() { + if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { + if type_allowed_to_implement_copy( + cx.tcx, + cx.param_env, + ty, + traits::ObligationCause::dummy_with_span(span), + ) + .is_ok() + { + diag.span_help(span, "consider marking this type as `Copy`"); } } + } - if_chain! { - if is_type_diagnostic_item(cx, ty, sym::Vec); - if let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); - if let TyKind::Path(QPath::Resolved(_, path)) = input.kind; - if let Some(elem_ty) = path.segments.iter() - .find(|seg| seg.ident.name == sym::Vec) - .and_then(|ps| ps.args.as_ref()) - .map(|params| params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).unwrap()); - then { - let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_")); - diag.span_suggestion( - input.span, - "consider changing the type to", - slice_ty, - Applicability::Unspecified, - ); - - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - snippet_opt(cx, span) - .map_or( - "change the call to".into(), - |x| format!("change `{x}` to"), - ), - suggestion, - Applicability::Unspecified, - ); - } - - // cannot be destructured, no need for `*` suggestion - return; - } + if is_type_diagnostic_item(cx, ty, sym::Vec) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]) + && let TyKind::Path(QPath::Resolved(_, path)) = input.kind + && let Some(elem_ty) = path + .segments + .iter() + .find(|seg| seg.ident.name == sym::Vec) + .and_then(|ps| ps.args.as_ref()) + .map(|params| { + params + .args + .iter() + .find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + .unwrap() + }) + { + let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_")); + diag.span_suggestion( + input.span, + "consider changing the type to", + slice_ty, + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { + diag.span_suggestion( + span, + snippet_opt(cx, span) + .map_or("change the call to".into(), |x| format!("change `{x}` to")), + suggestion, + Applicability::Unspecified, + ); } - if is_type_lang_item(cx, ty, LangItem::String) { - if let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { + // cannot be destructured, no need for `*` suggestion + return; + } + + if is_type_lang_item(cx, ty, LangItem::String) { + if let Some(clone_spans) = + get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) + { + diag.span_suggestion( + input.span, + "consider changing the type to", + "&str", + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { diag.span_suggestion( - input.span, - "consider changing the type to", - "&str", + span, + snippet_opt(cx, span) + .map_or("change the call to".into(), |x| format!("change `{x}` to")), + suggestion, Applicability::Unspecified, ); - - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - snippet_opt(cx, span) - .map_or( - "change the call to".into(), - |x| format!("change `{x}` to") - ), - suggestion, - Applicability::Unspecified, - ); - } - - return; } + + return; } + } - let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; + let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - multispan_sugg(diag, "consider taking a reference instead", spans); - }; + multispan_sugg(diag, "consider taking a reference instead", spans); + }; - span_lint_and_then( - cx, - NEEDLESS_PASS_BY_VALUE, - input.span, - "this argument is passed by value, but not consumed in the function body", - sugg, - ); - } + span_lint_and_then( + cx, + NEEDLESS_PASS_BY_VALUE, + input.span, + "this argument is passed by value, but not consumed in the function body", + sugg, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index 074c9fef1b2..7ec0879ba38 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::path_res; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, CoroutineKind, CoroutineSource, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -111,34 +110,32 @@ impl LateLintPass<'_> for NeedlessQuestionMark { } fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::Call(path, [arg]) = expr.kind; - if let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path); - if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); - let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + && let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some()" } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { "Ok()" } else { return; - }; - if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind; - if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind; - if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind; - if expr.span.eq_ctxt(inner_expr.span); - let expr_ty = cx.typeck_results().expr_ty(expr); - let inner_ty = cx.typeck_results().expr_ty(inner_expr); - if expr_ty == inner_ty; - then { - span_lint_and_sugg( - cx, - NEEDLESS_QUESTION_MARK, - expr.span, - "question mark operator is useless here", - &format!("try removing question mark and `{sugg_remove}`"), - format!("{}", snippet(cx, inner_expr.span, r#""...""#)), - Applicability::MachineApplicable, - ); } + && let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind + && let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind + && expr.span.eq_ctxt(inner_expr.span) + && let expr_ty = cx.typeck_results().expr_ty(expr) + && let inner_ty = cx.typeck_results().expr_ty(inner_expr) + && expr_ty == inner_ty + { + span_lint_and_sugg( + cx, + NEEDLESS_QUESTION_MARK, + expr.span, + "question mark operator is useless here", + &format!("try removing question mark and `{sugg_remove}`"), + format!("{}", snippet(cx, inner_expr.span, r#""...""#)), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 56c67406d6f..30aed8cc04a 100644 --- a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -46,42 +45,39 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; - if let ExprKind::Binary(ref op, left, _) = inner.kind; - if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Unary(UnOp::Not, inner) = expr.kind + && let ExprKind::Binary(ref op, left, _) = inner.kind + && let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node + { + let ty = cx.typeck_results().expr_ty(left); - then { - let ty = cx.typeck_results().expr_ty(left); - - let implements_ord = { - if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { - implements_trait(cx, ty, id, &[]) - } else { - return; - } - }; - - let implements_partial_ord = { - if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { - implements_trait(cx, ty, id, &[ty.into()]) - } else { - return; - } - }; + let implements_ord = { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { + implements_trait(cx, ty, id, &[]) + } else { + return; + } + }; - if implements_partial_ord && !implements_ord { - span_lint( - cx, - NEG_CMP_OP_ON_PARTIAL_ORD, - expr.span, - "the use of negated comparison operators on partially ordered \ - types produces code that is hard to read and refactor, please \ - consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable", - ); + let implements_partial_ord = { + if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { + implements_trait(cx, ty, id, &[ty.into()]) + } else { + return; } + }; + + if implements_partial_ord && !implements_ord { + span_lint( + cx, + NEG_CMP_OP_ON_PARTIAL_ORD, + expr.span, + "the use of negated comparison operators on partially ordered \ + types produces code that is hard to read and refactor, please \ + consider using the `partial_cmp` method instead, to make it \ + clear that the two values could be incomparable", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 8b69f94cbba..a6adb7c8a5f 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -2,7 +2,6 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::has_enclosing_paren; -use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -53,28 +52,25 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { } fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { - if_chain! { - if let ExprKind::Lit(l) = lit.kind; - if consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1); - if cx.typeck_results().expr_ty(exp).is_integral(); - - then { - let mut applicability = Applicability::MachineApplicable; - let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { - format!("-({snip})") - } else { - format!("-{snip}") - }; - span_lint_and_sugg( - cx, - NEG_MULTIPLY, - span, - "this multiplication by -1 can be written more succinctly", - "consider using", - suggestion, - applicability, - ); - } + if let ExprKind::Lit(l) = lit.kind + && consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1) + && cx.typeck_results().expr_ty(exp).is_integral() + { + let mut applicability = Applicability::MachineApplicable; + let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); + let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { + format!("-({snip})") + } else { + format!("-{snip}") + }; + span_lint_and_sugg( + cx, + NEG_MULTIPLY, + span, + "this multiplication by -1 can be written more succinctly", + "consider using", + suggestion, + applicability, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index f7f9dccfbce..abba622a285 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; use clippy_utils::source::snippet; use clippy_utils::sugg::DiagnosticExt; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirIdSet; @@ -93,73 +92,69 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // an impl of `Default` return; } - if_chain! { - if sig.decl.inputs.is_empty(); - if name == sym::new; - if cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id); - let self_def_id = cx.tcx.hir().get_parent_item(id.into()); - let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity(); - if self_ty == return_ty(cx, id); - if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default); - then { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { - let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() { - if let Some(local_def_id) = ty_def.did().as_local() { - impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); - } + if sig.decl.inputs.is_empty() + && name == sym::new + && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) + && let self_def_id = cx.tcx.hir().get_parent_item(id.into()) + && let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity() + && self_ty == return_ty(cx, id) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + { + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + cx.tcx.for_each_impl(default_trait_id, |d| { + let ty = cx.tcx.type_of(d).instantiate_identity(); + if let Some(ty_def) = ty.ty_adt_def() { + if let Some(local_def_id) = ty_def.did().as_local() { + impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); } - }); - self.impling_types = Some(impls); - } - - // Check if a Default implementation exists for the Self type, regardless of - // generics - if_chain! { - if let Some(ref impling_types) = self.impling_types; - let self_def = cx.tcx.type_of(self_def_id).instantiate_identity(); - if let Some(self_def) = self_def.ty_adt_def(); - if let Some(self_local_did) = self_def.did().as_local(); - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); - if impling_types.contains(&self_id); - then { - return; } - } + }); + self.impling_types = Some(impls); + } - let generics_sugg = snippet(cx, generics.span, ""); - let where_clause_sugg = if generics.has_where_clause_predicates { - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) - } else { - String::new() - }; - let self_ty_fmt = self_ty.to_string(); - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id.into(), - impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{self_type_snip}`" - ), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try adding this", - &create_new_without_default_suggest_msg( - &self_type_snip, - &generics_sugg, - &where_clause_sugg - ), - Applicability::MachineApplicable, - ); - }, - ); + // Check if a Default implementation exists for the Self type, regardless of + // generics + if let Some(ref impling_types) = self.impling_types + && let self_def = cx.tcx.type_of(self_def_id).instantiate_identity() + && let Some(self_def) = self_def.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did) + && impling_types.contains(&self_id) + { + return; } + + let generics_sugg = snippet(cx, generics.span, ""); + let where_clause_sugg = if generics.has_where_clause_predicates { + format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) + } else { + String::new() + }; + let self_ty_fmt = self_ty.to_string(); + let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id.into(), + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{self_type_snip}`" + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try adding this", + &create_new_without_default_suggest_msg( + &self_type_snip, + &generics_sugg, + &where_clause_sugg, + ), + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 3a28e511fdd..de8bb123e9b 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -132,24 +132,22 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { return true; } } else if let StmtKind::Local(local) = stmt.kind { - if_chain! { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id); - if let Some(init) = local.init; - if local.els.is_none(); - if !local.pat.span.from_expansion(); - if has_no_effect(cx, init); - if let PatKind::Binding(_, _, ident, _) = local.pat.kind; - if ident.name.to_ident_string().starts_with('_'); - then { - span_lint_hir( - cx, - NO_EFFECT_UNDERSCORE_BINDING, - init.hir_id, - stmt.span, - "binding to `_` prefixed variable with no side-effect" - ); - return true; - } + if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, _, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + { + span_lint_hir( + cx, + NO_EFFECT_UNDERSCORE_BINDING, + init.hir_id, + stmt.span, + "binding to `_` prefixed variable with no side-effect", + ); + return true; } } false @@ -199,63 +197,60 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if_chain! { - if let StmtKind::Semi(expr) = stmt.kind; - let ctxt = stmt.span.ctxt(); - if expr.span.ctxt() == ctxt; - if let Some(reduced) = reduce_expression(cx, expr); - if !in_external_macro(cx.sess(), stmt.span); - if reduced.iter().all(|e| e.span.ctxt() == ctxt); - then { - if let ExprKind::Index(..) = &expr.kind { - let snippet = if let (Some(arr), Some(func)) = - (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) - { + if let StmtKind::Semi(expr) = stmt.kind + && let ctxt = stmt.span.ctxt() + && expr.span.ctxt() == ctxt + && let Some(reduced) = reduce_expression(cx, expr) + && !in_external_macro(cx.sess(), stmt.span) + && reduced.iter().all(|e| e.span.ctxt() == ctxt) + { + if let ExprKind::Index(..) = &expr.kind { + 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) } else { return; }; - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be written as", - snippet, - Applicability::MaybeIncorrect, - ); - }, - ); - } else { - let mut snippet = String::new(); - for e in reduced { - if let Some(snip) = snippet_opt(cx, e.span) { - snippet.push_str(&snip); - snippet.push(';'); - } else { - return; - } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + snippet, + Applicability::MaybeIncorrect, + ); + }, + ); + } else { + let mut snippet = String::new(); + for e in reduced { + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be reduced to", - snippet, - Applicability::MachineApplicable, - ); - }, - ); } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be reduced to", + snippet, + Applicability::MachineApplicable, + ); + }, + ); } } } 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 54cec066ba1..3059eb25d29 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -7,7 +7,6 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; use clippy_utils::{def_path_def_ids, in_constant}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -386,15 +385,14 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { of_trait: Some(of_trait_ref), .. }) => { - if_chain! { + if let Some(of_trait_def_id) = of_trait_ref.trait_def_id() // Lint a trait impl item only when the definition is a generic type, // assuming an assoc const is not meant to be an interior mutable type. - if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); - if let Some(of_assoc_item) = cx + && let Some(of_assoc_item) = cx .tcx .associated_item(impl_item.owner_id) - .trait_item_def_id; - if cx + .trait_item_def_id + && cx .tcx .layout_of(cx.tcx.param_env(of_trait_def_id).and( // Normalize assoc types because ones originated from generic params @@ -405,23 +403,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.tcx.type_of(of_assoc_item).instantiate_identity(), ), )) - .is_err(); + .is_err() // If there were a function like `has_frozen_variant` described above, // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); - let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized); - if self.is_value_unfrozen_poly(cx, *body_id, normalized); - then { - lint( - cx, - Source::Assoc { - item: impl_item.span, - }, - ); - } + && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() + && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty) + && !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) + && self.is_value_unfrozen_poly(cx, *body_id, normalized) + { + lint(cx, Source::Assoc { item: impl_item.span }); } }, ItemKind::Impl(Impl { of_trait: None, .. }) => { diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 61622034d1a..649a23565a9 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -6,8 +6,8 @@ use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{sym, Span}; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index e94e4589966..6cfcc81025d 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -44,38 +43,36 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { - if_chain! { - if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def(); - if (path.ident.name == sym!(mode) - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder))) + if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() + && ((path.ident.name == sym!(mode) + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::FsOpenOptions | sym::DirBuilder) + )) || (path.ident.name == sym!(set_mode) - && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())); - if let ExprKind::Lit(_) = param.kind; - if param.span.eq_ctxt(expr.span); + && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) + && let ExprKind::Lit(_) = param.kind + && param.span.eq_ctxt(expr.span) + { + let Some(snip) = snippet_opt(cx, param.span) else { + return; + }; - then { - let Some(snip) = snippet_opt(cx, param.span) else { - return - }; - - if !snip.starts_with("0o") { - show_error(cx, param); - } + if !snip.starts_with("0o") { + show_error(cx, param); } } }, ExprKind::Call(func, [param]) => { - if_chain! { - if let ExprKind::Path(ref path) = func.kind; - if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); - if let ExprKind::Lit(_) = param.kind; - if param.span.eq_ctxt(expr.span); - if let Some(snip) = snippet_opt(cx, param.span); - if !snip.starts_with("0o"); - then { - show_error(cx, param); - } + if let ExprKind::Path(ref path) = func.kind + && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE) + && let ExprKind::Lit(_) = param.kind + && param.span.eq_ctxt(expr.span) + && let Some(snip) = snippet_opt(cx, param.span) + && !snip.starts_with("0o") + { + show_error(cx, param); } }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index 62ef48c8a90..df1476e6809 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -81,73 +81,74 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { // We start from `Send` impl instead of `check_field_def()` because // single `AdtDef` may have multiple `Send` impls due to generic // parameters, and the lint is much easier to implement in this way. - if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); - if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send); - if let ItemKind::Impl(hir_impl) = &item.kind; - if let Some(trait_ref) = &hir_impl.of_trait; - if let Some(trait_id) = trait_ref.trait_def_id(); - if send_trait == trait_id; - if hir_impl.polarity == ImplPolarity::Positive; - if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if let self_ty = ty_trait_ref.instantiate_identity().self_ty(); - if let ty::Adt(adt_def, impl_trait_args) = self_ty.kind(); - then { - let mut non_send_fields = Vec::new(); - - let hir_map = cx.tcx.hir(); - for variant in adt_def.variants() { - for field in &variant.fields { - if_chain! { - if let Some(field_hir_id) = field - .did - .as_local() - .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)); - if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id); - if let field_ty = field.ty(cx.tcx, impl_trait_args); - if !ty_allowed_in_send(cx, field_ty, send_trait); - if let Node::Field(field_def) = hir_map.get(field_hir_id); - then { - non_send_fields.push(NonSendField { - def: field_def, - ty: field_ty, - generic_params: collect_generic_params(field_ty), - }) - } - } + if !in_external_macro(cx.tcx.sess, item.span) + && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) + && let ItemKind::Impl(hir_impl) = &item.kind + && let Some(trait_ref) = &hir_impl.of_trait + && let Some(trait_id) = trait_ref.trait_def_id() + && send_trait == trait_id + && hir_impl.polarity == ImplPolarity::Positive + && let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && let self_ty = ty_trait_ref.instantiate_identity().self_ty() + && let ty::Adt(adt_def, impl_trait_args) = self_ty.kind() + { + let mut non_send_fields = Vec::new(); + + let hir_map = cx.tcx.hir(); + for variant in adt_def.variants() { + for field in &variant.fields { + if let Some(field_hir_id) = field + .did + .as_local() + .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)) + && !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id) + && let field_ty = field.ty(cx.tcx, impl_trait_args) + && !ty_allowed_in_send(cx, field_ty, send_trait) + && let Node::Field(field_def) = hir_map.get(field_hir_id) + { + non_send_fields.push(NonSendField { + def: field_def, + ty: field_ty, + generic_params: collect_generic_params(field_ty), + }); } } + } - if !non_send_fields.is_empty() { - span_lint_and_then( - cx, - NON_SEND_FIELDS_IN_SEND_TY, - item.span, - &format!( - "some fields in `{}` are not safe to be sent to another thread", - snippet(cx, hir_impl.self_ty.span, "Unknown") - ), - |diag| { - for field in non_send_fields { - diag.span_note( - field.def.span, - format!("it is not safe to send field `{}` to another thread", field.def.ident.name), - ); - - match field.generic_params.len() { - 0 => diag.help("use a thread-safe type that implements `Send`"), - 1 if is_ty_param(field.ty) => diag.help(format!("add `{}: Send` bound in `Send` impl", field.ty)), - _ => diag.help(format!( - "add bounds on type parameter{} `{}` that satisfy `{}: Send`", - if field.generic_params.len() > 1 { "s" } else { "" }, - field.generic_params_string(), - snippet(cx, field.def.ty.span, "Unknown"), - )), - }; - } - }, - ); - } + if !non_send_fields.is_empty() { + span_lint_and_then( + cx, + NON_SEND_FIELDS_IN_SEND_TY, + item.span, + &format!( + "some fields in `{}` are not safe to be sent to another thread", + snippet(cx, hir_impl.self_ty.span, "Unknown") + ), + |diag| { + for field in non_send_fields { + diag.span_note( + field.def.span, + format!( + "it is not safe to send field `{}` to another thread", + field.def.ident.name + ), + ); + + match field.generic_params.len() { + 0 => diag.help("use a thread-safe type that implements `Send`"), + 1 if is_ty_param(field.ty) => { + diag.help(format!("add `{}: Send` bound in `Send` impl", field.ty)) + }, + _ => diag.help(format!( + "add bounds on type parameter{} `{}` that satisfy `{}: Send`", + if field.generic_params.len() > 1 { "s" } else { "" }, + field.generic_params_string(), + snippet(cx, field.def.ty.span, "Unknown"), + )), + }; + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 11c3a5417b5..1c6a8e16ae2 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,6 @@ use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -34,17 +33,17 @@ declare_clippy_lint! { } /// The (callsite span, (open brace, close brace), source snippet) -type MacroInfo<'a> = (Span, &'a (String, String), String); +type MacroInfo = (Span, (char, char), String); -#[derive(Clone, Debug, Default)] +#[derive(Debug)] pub struct MacroBraces { - macro_braces: FxHashMap<String, (String, String)>, + macro_braces: FxHashMap<String, (char, char)>, done: FxHashSet<Span>, } impl MacroBraces { - pub fn new(conf: &FxHashSet<MacroMatcher>) -> Self { - let macro_braces = macro_braces(conf.clone()); + pub fn new(conf: &[MacroMatcher]) -> Self { + let macro_braces = macro_braces(conf); Self { macro_braces, done: FxHashSet::default(), @@ -84,7 +83,7 @@ impl EarlyLintPass for MacroBraces { } } -fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> { +fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBraces) -> Option<MacroInfo> { let unnested_or_local = || { !span.ctxt().outer_expn_data().call_site.from_expansion() || span @@ -93,28 +92,26 @@ fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a Mac .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) }; let span_call_site = span.ctxt().outer_expn_data().call_site; - if_chain! { - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind; - let name = mac_name.as_str(); - if let Some(braces) = mac_braces.macro_braces.get(name); - if let Some(snip) = snippet_opt(cx, span_call_site); + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind + && let name = mac_name.as_str() + && let Some(&braces) = mac_braces.macro_braces.get(name) + && let Some(snip) = snippet_opt(cx, span_call_site) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - if snip.starts_with(&format!("{name}!")); - if unnested_or_local(); + && snip.starts_with(&format!("{name}!")) + && unnested_or_local() // make formatting consistent - let c = snip.replace(' ', ""); - if !c.starts_with(&format!("{name}!{}", braces.0)); - if !mac_braces.done.contains(&span_call_site); - then { - Some((span_call_site, braces, snip)) - } else { - None - } + && let c = snip.replace(' ', "") + && !c.starts_with(&format!("{name}!{}", braces.0)) + && !mac_braces.done.contains(&span_call_site) + { + Some((span_call_site, braces, snip)) + } else { + None } } -fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: Span) { +fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span) { if let Some((macro_name, macro_args_str)) = snip.split_once('!') { let mut macro_args = macro_args_str.trim().to_string(); // now remove the wrong braces @@ -126,67 +123,31 @@ fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: span, &format!("use of irregular braces for `{macro_name}!` macro"), "consider writing", - format!("{macro_name}!{}{macro_args}{}", braces.0, braces.1), + format!("{macro_name}!{open}{macro_args}{close}"), Applicability::MachineApplicable, ); } } -fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> { - let mut braces = vec![ - macro_matcher!( - name: "print", - braces: ("(", ")"), - ), - macro_matcher!( - name: "println", - braces: ("(", ")"), - ), - macro_matcher!( - name: "eprint", - braces: ("(", ")"), - ), - macro_matcher!( - name: "eprintln", - braces: ("(", ")"), - ), - macro_matcher!( - name: "write", - braces: ("(", ")"), - ), - macro_matcher!( - name: "writeln", - braces: ("(", ")"), - ), - macro_matcher!( - name: "format", - braces: ("(", ")"), - ), - macro_matcher!( - name: "format_args", - braces: ("(", ")"), - ), - macro_matcher!( - name: "vec", - braces: ("[", "]"), - ), - macro_matcher!( - name: "matches", - braces: ("(", ")"), - ), - ] - .into_iter() - .collect::<FxHashMap<_, _>>(); +fn macro_braces(conf: &[MacroMatcher]) -> FxHashMap<String, (char, char)> { + let mut braces = FxHashMap::from_iter( + [ + ("print", ('(', ')')), + ("println", ('(', ')')), + ("eprint", ('(', ')')), + ("eprintln", ('(', ')')), + ("write", ('(', ')')), + ("writeln", ('(', ')')), + ("format", ('(', ')')), + ("format_args", ('(', ')')), + ("vec", ('[', ']')), + ("matches", ('(', ')')), + ] + .map(|(k, v)| (k.to_string(), v)), + ); // We want users items to override any existing items for it in conf { - braces.insert(it.name, it.braces); + braces.insert(it.name.clone(), it.braces); } braces } - -macro_rules! macro_matcher { - (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => { - ($name.to_owned(), ($open.to_owned(), $close.to_owned())) - }; -} -pub(crate) use macro_matcher; 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 f7d9650b2f8..1a2b20bf438 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 @@ -7,9 +7,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index c4572a09db6..2f85130fba1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -4,7 +4,6 @@ use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -25,43 +24,40 @@ pub(super) fn check<'tcx>( let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { let ty = cx.typeck_results().expr_ty(assignee); let rty = cx.typeck_results().expr_ty(rhs); - if_chain! { - if let Some((_, lang_item)) = binop_traits(op.node); - if let Some(trait_id) = cx.tcx.lang_items().get(lang_item); - let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id; - if trait_ref_of_method(cx, parent_fn) - .map_or(true, |t| t.path.res.def_id() != trait_id); - if implements_trait(cx, ty, trait_id, &[rty.into()]); - then { - // Primitive types execute assign-ops right-to-left. Every other type is left-to-right. - if !(ty.is_primitive() && rty.is_primitive()) { - // TODO: This will have false negatives as it doesn't check if the borrows are - // actually live at the end of their respective expressions. - let mut_borrows = mut_borrows_in_expr(cx, assignee); - let imm_borrows = imm_borrows_in_expr(cx, rhs); - if mut_borrows.iter().any(|id| imm_borrows.contains(id)) { - return; - } + if let Some((_, lang_item)) = binop_traits(op.node) + && let Some(trait_id) = cx.tcx.lang_items().get(lang_item) + && let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id + && trait_ref_of_method(cx, parent_fn).map_or(true, |t| t.path.res.def_id() != trait_id) + && implements_trait(cx, ty, trait_id, &[rty.into()]) + { + // Primitive types execute assign-ops right-to-left. Every other type is left-to-right. + if !(ty.is_primitive() && rty.is_primitive()) { + // TODO: This will have false negatives as it doesn't check if the borrows are + // actually live at the end of their respective expressions. + let mut_borrows = mut_borrows_in_expr(cx, assignee); + let imm_borrows = imm_borrows_in_expr(cx, rhs); + if mut_borrows.iter().any(|id| imm_borrows.contains(id)) { + return; } - span_lint_and_then( - cx, - ASSIGN_OP_PATTERN, - expr.span, - "manual implementation of an assign operation", - |diag| { - if let (Some(snip_a), Some(snip_r)) = - (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) - { - diag.span_suggestion( - expr.span, - "replace it with", - format!("{snip_a} {}= {snip_r}", op.node.as_str()), - Applicability::MachineApplicable, - ); - } - }, - ); } + span_lint_and_then( + cx, + ASSIGN_OP_PATTERN, + expr.span, + "manual implementation of an assign operation", + |diag| { + if let (Some(snip_a), Some(snip_r)) = + (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) + { + diag.span_suggestion( + expr.span, + "replace it with", + format!("{snip_a} {}= {snip_r}", op.node.as_str()), + Applicability::MachineApplicable, + ); + } + }, + ); } }; 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 ec2bb869973..e278cf9835a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -3,13 +3,12 @@ use std::cmp::Ordering; use clippy_utils::consts::{constant, Constant}; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{Ty, TypeckResults}; -use rustc_span::Span; use rustc_span::source_map::Spanned; +use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::source::snippet; @@ -24,19 +23,17 @@ fn comparison_to_const<'tcx>( typeck: &TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { - if_chain! { - if let ExprKind::Binary(operator, left, right) = expr.kind; - if let Ok(cmp_op) = CmpOp::try_from(operator.node); - then { - match (constant(cx, typeck, left), constant(cx, typeck, 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))), - _ => None, - } - } else { - None + 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)) { + (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))), + _ => None, } + } else { + None } } @@ -47,87 +44,89 @@ pub(super) fn check<'tcx>( right_cond: &'tcx Expr<'tcx>, span: Span, ) { - if_chain! { + if and_op.node == BinOpKind::And // Ensure that the binary operator is && - if and_op.node == BinOpKind::And; // Check that both operands to '&&' are themselves a binary operation // The `comparison_to_const` step also checks this, so this step is just an optimization - if let ExprKind::Binary(_, _, _) = left_cond.kind; - if let ExprKind::Binary(_, _, _) = right_cond.kind; + && let ExprKind::Binary(_, _, _) = left_cond.kind + && let ExprKind::Binary(_, _, _) = right_cond.kind - let typeck = cx.typeck_results(); + && let typeck = cx.typeck_results() // Check that both operands to '&&' compare a non-literal to a literal - if let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) = - comparison_to_const(cx, typeck, left_cond); - if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) = - comparison_to_const(cx, typeck, right_cond); + && let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) = + comparison_to_const(cx, typeck, left_cond) + && let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) = + comparison_to_const(cx, typeck, right_cond) - if left_type == right_type; + && left_type == right_type // Check that the same expression is compared in both comparisons - if SpanlessEq::new(cx).eq_expr(left_expr, right_expr); + && SpanlessEq::new(cx).eq_expr(left_expr, right_expr) - if !left_expr.can_have_side_effects(); + && !left_expr.can_have_side_effects() // Compare the two constant expressions - if let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const); + && let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const) // Rule out the `x >= 42 && x <= 42` corner case immediately // Mostly to simplify the implementation, but it is also covered by `clippy::double_comparisons` - if !matches!( + && !matches!( (&left_cmp_op, &right_cmp_op, ordering), (CmpOp::Le | CmpOp::Ge, CmpOp::Le | CmpOp::Ge, Ordering::Equal) - ); - - then { - if left_cmp_op.direction() == right_cmp_op.direction() { - let lhs_str = snippet(cx, left_cond.span, "<lhs>"); - let rhs_str = snippet(cx, right_cond.span, "<rhs>"); - // We already know that either side of `&&` has no effect, - // but emit a different error message depending on which side it is - if left_side_is_useless(left_cmp_op, ordering) { - span_lint_and_note( - cx, - REDUNDANT_COMPARISONS, - span, - "left-hand side of `&&` operator has no effect", - Some(left_cond.span.until(right_cond.span)), - &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), - ); - } else { - span_lint_and_note( - cx, - REDUNDANT_COMPARISONS, - span, - "right-hand side of `&&` operator has no effect", - Some(and_op.span.to(right_cond.span)), - &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), - ); - } - // We could autofix this error but choose not to, - // because code triggering this lint probably not behaving correctly in the first place - } - else if !comparison_is_possible(left_cmp_op.direction(), ordering) { - let expr_str = snippet(cx, left_expr.span, ".."); - let lhs_str = snippet(cx, left_const_expr.span, "<lhs>"); - let rhs_str = snippet(cx, right_const_expr.span, "<rhs>"); - let note = match ordering { - Ordering::Less => format!("since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"), - Ordering::Equal => format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`"), - Ordering::Greater => format!("since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"), - }; + ) + { + if left_cmp_op.direction() == right_cmp_op.direction() { + let lhs_str = snippet(cx, left_cond.span, "<lhs>"); + let rhs_str = snippet(cx, right_cond.span, "<rhs>"); + // We already know that either side of `&&` has no effect, + // but emit a different error message depending on which side it is + if left_side_is_useless(left_cmp_op, ordering) { span_lint_and_note( cx, - IMPOSSIBLE_COMPARISONS, + REDUNDANT_COMPARISONS, span, - "boolean expression will never evaluate to 'true'", - None, - ¬e, + "left-hand side of `&&` operator has no effect", + Some(left_cond.span.until(right_cond.span)), + &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), ); + } else { + span_lint_and_note( + cx, + REDUNDANT_COMPARISONS, + span, + "right-hand side of `&&` operator has no effect", + Some(and_op.span.to(right_cond.span)), + &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), + ); + } + // We could autofix this error but choose not to, + // because code triggering this lint probably not behaving correctly in the first place + } else if !comparison_is_possible(left_cmp_op.direction(), ordering) { + let expr_str = snippet(cx, left_expr.span, ".."); + let lhs_str = snippet(cx, left_const_expr.span, "<lhs>"); + let rhs_str = snippet(cx, right_const_expr.span, "<rhs>"); + let note = match ordering { + Ordering::Less => format!( + "since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`" + ), + Ordering::Equal => { + format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`") + }, + Ordering::Greater => format!( + "since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`" + ), }; - } + span_lint_and_note( + cx, + IMPOSSIBLE_COMPARISONS, + span, + "boolean expression will never evaluate to 'true'", + None, + ¬e, + ); + }; } } 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 bce6bdcaf61..0561739d160 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -2,7 +2,6 @@ use clippy_utils::consts::{constant_with_source, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -105,14 +104,12 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { return is_signum(cx, child_expr); } - if_chain! { - if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; - if sym!(signum) == method_name.ident.name; - // Check that the receiver of the signum() is a float (expressions[0] is the receiver of - // the method call) - then { - return is_float(cx, self_arg); - } + if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind + && sym!(signum) == method_name.ident.name + // Check that the receiver of the signum() is a float (expressions[0] is the receiver of + // the method call) + { + return is_float(cx, self_arg); } false } diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs index a0a8b6aabd9..cace85a2451 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths, sugg}; -use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -24,48 +23,43 @@ pub(crate) fn check<'tcx>( _ => return, }; - if_chain! { + if let ExprKind::Binary( // left hand side is a subtraction - if let ExprKind::Binary( Spanned { node: BinOpKind::Sub, .. }, val_l, val_r, - ) = lhs.kind; + ) = lhs.kind // right hand side matches either f32::EPSILON or f64::EPSILON - if let ExprKind::Path(ref epsilon_path) = rhs.kind; - if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id); - if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON); + && let ExprKind::Path(ref epsilon_path) = rhs.kind + && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) + && (match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON)) // values of the subtractions on the left hand side are of the type float - let t_val_l = cx.typeck_results().expr_ty(val_l); - let t_val_r = cx.typeck_results().expr_ty(val_r); - if let ty::Float(_) = t_val_l.kind(); - if let ty::Float(_) = t_val_r.kind(); - - then { - let sug_l = sugg::Sugg::hir(cx, val_l, ".."); - let sug_r = sugg::Sugg::hir(cx, val_r, ".."); - // format the suggestion - let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); - // spans the lint - span_lint_and_then( - cx, - FLOAT_EQUALITY_WITHOUT_ABS, - expr.span, - "float equality check without `.abs()`", - | diag | { - diag.span_suggestion( - lhs.span, - "add `.abs()`", - suggestion, - Applicability::MaybeIncorrect, - ); - } - ); - } + && let t_val_l = cx.typeck_results().expr_ty(val_l) + && let t_val_r = cx.typeck_results().expr_ty(val_r) + && let ty::Float(_) = t_val_l.kind() + && let ty::Float(_) = t_val_r.kind() + { + let sug_l = sugg::Sugg::hir(cx, val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, val_r, ".."); + // format the suggestion + let suggestion = format!( + "{}.abs()", + sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par() + ); + // spans the lint + span_lint_and_then( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + |diag| { + diag.span_suggestion(lhs.span, "add `.abs()`", suggestion, Applicability::MaybeIncorrect); + }, + ); } } 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 a2c3a4d8ba7..cb3916484c1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -19,15 +18,12 @@ pub(super) fn check<'tcx>( if op == BinOpKind::Rem { let lhs_operand = analyze_operand(lhs, cx, e); let rhs_operand = analyze_operand(rhs, cx, e); - if_chain! { - if let Some(lhs_operand) = lhs_operand; - if let Some(rhs_operand) = rhs_operand; - then { - check_const_operands(cx, e, &lhs_operand, &rhs_operand); - } - else { - check_non_const_operands(cx, e, lhs); - } + if let Some(lhs_operand) = lhs_operand + && let Some(rhs_operand) = rhs_operand + { + check_const_operands(cx, e, &lhs_operand, &rhs_operand); + } else { + check_non_const_operands(cx, e, lhs); } }; } 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 932dd470f9e..7d8aa3f56fe 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::get_enclosing_block; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; @@ -180,41 +179,33 @@ fn in_impl<'tcx>( e: &'tcx Expr<'_>, bin_op: DefId, ) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> { - if_chain! { - if let Some(block) = get_enclosing_block(cx, e.hir_id); - if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()); - let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()); - if let ItemKind::Impl(item) = &item.kind; - if let Some(of_trait) = &item.of_trait; - if let Some(seg) = of_trait.path.segments.last(); - if let Res::Def(_, trait_id) = seg.res; - if trait_id == bin_op; - if let Some(generic_args) = seg.args; - if let Some(GenericArg::Type(other_ty)) = generic_args.args.last(); - - then { - Some((item.self_ty, other_ty)) - } - else { - None - } + if let Some(block) = get_enclosing_block(cx, e.hir_id) + && let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()) + && let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()) + && let ItemKind::Impl(item) = &item.kind + && let Some(of_trait) = &item.of_trait + && let Some(seg) = of_trait.path.segments.last() + && let Res::Def(_, trait_id) = seg.res + && trait_id == bin_op + && let Some(generic_args) = seg.args + && let Some(GenericArg::Type(other_ty)) = generic_args.args.last() + { + Some((item.self_ty, other_ty)) + } else { + None } } fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { - if_chain! { - if let ty::Adt(adt_def, _) = middle_ty.kind(); - if let Some(local_did) = adt_def.did().as_local(); - let item = cx.tcx.hir().expect_item(local_did); - let middle_ty_id = item.owner_id.to_def_id(); - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; - if let Res::Def(_, hir_ty_id) = path.res; - - then { - hir_ty_id == middle_ty_id - } - else { - false - } + if let ty::Adt(adt_def, _) = middle_ty.kind() + && let Some(local_did) = adt_def.did().as_local() + && let item = cx.tcx.hir().expect_item(local_did) + && let middle_ty_id = item.owner_id.to_def_id() + && let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind + && let Res::Def(_, hir_ty_id) = path.res + { + hir_ty_id == middle_ty_id + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs index 1229c202f5a..9db2e24630a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -22,22 +21,20 @@ pub(super) fn check<'tcx>( _ => (left, right), }; - if_chain! { - if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); - if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); - if let Some(left_snip) = snippet_opt(cx, left_var.span); - if let Some(right_snip) = snippet_opt(cx, right_var.span); - then { - span_lint_and_sugg( - cx, - PTR_EQ, - expr.span, - LINT_MSG, - "try", - format!("std::ptr::eq({left_snip}, {right_snip})"), - Applicability::MachineApplicable, - ); - } + if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left) + && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right) + && let Some(left_snip) = snippet_opt(cx, left_var.span) + && let Some(right_snip) = snippet_opt(cx, right_var.span) + { + span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + LINT_MSG, + "try", + format!("std::ptr::eq({left_snip}, {right_snip})"), + 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 d7cbbe13a26..6e4e0c98d29 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 @@ -4,7 +4,6 @@ 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, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; @@ -122,73 +121,97 @@ fn try_get_option_occurrence<'tcx>( _ => expr, }; let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?; - if_chain! { - if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind; - if let Some(some_captures) = can_move_expr_to_closure(cx, if_then); - if let Some(none_captures) = can_move_expr_to_closure(cx, if_else); - if some_captures + if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind + && let Some(some_captures) = can_move_expr_to_closure(cx, if_then) + && let Some(none_captures) = can_move_expr_to_closure(cx, if_else) + && some_captures .iter() .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2))) - .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()); - then { - let capture_mut = if bind_annotation == BindingAnnotation::MUT { "mut " } else { "" }; - let some_body = peel_blocks(if_then); - let none_body = peel_blocks(if_else); - let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" }; - let capture_name = id.name.to_ident_string(); - let (as_ref, as_mut) = match &expr.kind { - ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), - ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), - _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => { - (mutb == Mutability::Not, mutb == Mutability::Mut) - } - _ => (bind_annotation == BindingAnnotation::REF, bind_annotation == BindingAnnotation::REF_MUT), - }; + .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()) + { + let capture_mut = if bind_annotation == BindingAnnotation::MUT { + "mut " + } else { + "" + }; + let some_body = peel_blocks(if_then); + let none_body = peel_blocks(if_else); + let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { + "map_or" + } else { + "map_or_else" + }; + let capture_name = id.name.to_ident_string(); + let (as_ref, as_mut) = match &expr.kind { + ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), + ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), + _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => { + (mutb == Mutability::Not, mutb == Mutability::Mut) + }, + _ => ( + bind_annotation == BindingAnnotation::REF, + bind_annotation == BindingAnnotation::REF_MUT, + ), + }; - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `<local>[.<field>]*` is checked for. - if as_ref || as_mut { - let e = peel_hir_expr_while(cond_expr, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind { - match some_captures.get(local_id) - .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id))) - { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, - Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `<local>[.<field>]*` is checked for. + if as_ref || as_mut { + let e = peel_hir_expr_while(cond_expr, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved( + None, + Path { + res: Res::Local(local_id), + .. + }, + )) = e.kind + { + match some_captures.get(local_id).or_else(|| { + (method_sugg == "map_or_else") + .then_some(()) + .and_then(|()| none_captures.get(local_id)) + }) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } + } - let mut app = Applicability::Unspecified; + let mut app = Applicability::Unspecified; - let (none_body, is_argless_call) = match none_body.kind { - ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true), - _ => (none_body, false), - }; + let (none_body, is_argless_call) = match none_body.kind { + ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true), + _ => (none_body, false), + }; - return Some(OptionOccurrence { - option: format_option_in_sugg( - Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app), - as_ref, - as_mut, - ), - method_sugg: method_sugg.to_string(), - some_expr: format!( - "|{capture_mut}{capture_name}| {}", - Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app), - ), - none_expr: format!( - "{}{}", - if method_sugg == "map_or" || is_argless_call { "" } else if is_result { "|_| " } else { "|| "}, - Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), - ), - }); - } + return Some(OptionOccurrence { + option: format_option_in_sugg( + Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app), + as_ref, + as_mut, + ), + method_sugg: method_sugg.to_string(), + some_expr: format!( + "|{capture_mut}{capture_name}| {}", + Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app), + ), + none_expr: format!( + "{}{}", + if method_sugg == "map_or" || is_argless_call { + "" + } else if is_result { + "|_| " + } else { + "|| " + }, + Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app), + ), + }); } None diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs index 38cd5043adc..e661bfbb96c 100644 --- a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs +++ b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::SpanlessEq; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,41 +33,37 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { // a + b < a, a > a + b, a < a - b, a - b > a fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); - if_chain! { - if let ExprKind::Binary(ref op, first, second) = expr.kind; - if let ExprKind::Binary(ref op2, ident1, ident2) = first.kind; - if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind; - if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); - if cx.typeck_results().expr_ty(ident1).is_integral(); - if cx.typeck_results().expr_ty(ident2).is_integral(); - then { - if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); - } - if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); - } + if let ExprKind::Binary(ref op, first, second) = expr.kind + && let ExprKind::Binary(ref op2, ident1, ident2) = first.kind + && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind + && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind + && let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind + && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0])) + && cx.typeck_results().expr_ty(ident1).is_integral() + && cx.typeck_results().expr_ty(ident2).is_integral() + { + if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); + } + if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); } } - if_chain! { - if let ExprKind::Binary(ref op, first, second) = expr.kind; - if let ExprKind::Binary(ref op2, ident1, ident2) = second.kind; - if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind; - if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); - if cx.typeck_results().expr_ty(ident1).is_integral(); - if cx.typeck_results().expr_ty(ident2).is_integral(); - then { - if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); - } - if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); - } + if let ExprKind::Binary(ref op, first, second) = expr.kind + && let ExprKind::Binary(ref op2, ident1, ident2) = second.kind + && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind + && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind + && let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind + && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0])) + && cx.typeck_results().expr_ty(ident1).is_integral() + && cx.typeck_results().expr_ty(ident2).is_integral() + { + if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); + } + if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); } } } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 68d3d00ac16..1b06762415d 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_hir; -use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -34,22 +33,24 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]); impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if_chain! { - if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); - if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); - if trait_ref.path.res.def_id() == eq_trait; - then { - for impl_item in *impl_items { - if impl_item.ident.name == sym::ne { - span_lint_hir( - cx, - PARTIALEQ_NE_IMPL, - impl_item.id.hir_id(), - impl_item.span, - "re-implementing `PartialEq::ne` is unnecessary", - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: impl_items, + .. + }) = item.kind + && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && let Some(eq_trait) = cx.tcx.lang_items().eq_trait() + && trait_ref.path.res.def_id() == eq_trait + { + for impl_item in *impl_items { + if impl_item.ident.name == sym::ne { + span_lint_hir( + cx, + PARTIALEQ_NE_IMPL, + impl_item.id.hir_id(), + impl_item.span, + "re-implementing `PartialEq::ne` is unnecessary", + ); } } }; diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index d6fa742b796..bbca8a123e6 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -5,7 +5,6 @@ use clippy_utils::source::snippet; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{is_self, is_self_ty}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -20,7 +19,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; -use rustc_target::spec::Target; declare_clippy_lint! { /// ### What it does @@ -117,10 +115,10 @@ impl<'tcx> PassByRefOrValue { ref_min_size: Option<u64>, value_max_size: u64, avoid_breaking_exported_api: bool, - target: &Target, + pointer_width: u32, ) -> Self { let ref_min_size = ref_min_size.unwrap_or_else(|| { - let bit_width = u64::from(target.pointer_width); + let bit_width = u64::from(pointer_width); // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); @@ -175,11 +173,11 @@ impl<'tcx> PassByRefOrValue { }, // Early bound regions on functions are either from the containing item, are bounded by another // lifetime, or are used as a bound for a type or lifetime. - RegionKind::ReEarlyBound(..) => continue, + RegionKind::ReEarlyParam(..) => continue, _ => (), } - let ty = cx.tcx.erase_late_bound_regions(fn_sig.rebind(ty)); + let ty = cx.tcx.instantiate_bound_regions_with_erased(fn_sig.rebind(ty)); if is_copy(cx, ty) && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) && size <= self.ref_min_size @@ -227,24 +225,25 @@ impl<'tcx> PassByRefOrValue { _ => continue, } } - let ty = cx.tcx.erase_late_bound_regions(ty); + let ty = cx.tcx.instantiate_bound_regions_with_erased(ty); - if_chain! { - if is_copy(cx, ty); - if !is_self_ty(input); - if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); - if size > self.value_max_size; - then { - span_lint_and_sugg( - cx, - LARGE_TYPES_PASSED_BY_VALUE, - input.span, - &format!("this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", self.value_max_size), - "consider passing by reference instead", - format!("&{}", snippet(cx, input.span, "_")), - Applicability::MaybeIncorrect, - ); - } + if is_copy(cx, ty) + && !is_self_ty(input) + && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) + && size > self.value_max_size + { + span_lint_and_sugg( + cx, + LARGE_TYPES_PASSED_BY_VALUE, + input.span, + &format!( + "this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", + self.value_max_size + ), + "consider passing by reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); } }, diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 057b7e30642..b638e83997a 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, MethodCall, UnOp}; use rustc_ast::token; use rustc_errors::Applicability; @@ -118,25 +117,23 @@ impl EarlyLintPass for Precedence { arg = receiver; } - if_chain! { - if !all_odd; - if let ExprKind::Lit(lit) = &arg.kind; - if let token::LitKind::Integer | token::LitKind::Float = &lit.kind; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, operand.span, "..", &mut applicability) - ), - applicability, - ); - } + if !all_odd + && let ExprKind::Lit(lit) = &arg.kind + && let token::LitKind::Integer | token::LitKind::Float = &lit.kind + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index c6ac96a4539..621a32d79bf 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -21,8 +21,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, Span}; use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -465,9 +465,9 @@ fn check_fn_args<'cx, 'tcx: 'cx>( .walk() .filter_map(|arg| { arg.as_region().and_then(|lifetime| match lifetime.kind() { - ty::ReEarlyBound(r) => Some(r.def_id), + ty::ReEarlyParam(r) => Some(r.def_id), ty::ReBound(_, r) => r.kind.get_id(), - ty::ReFree(r) => r.bound_region.get_id(), + ty::ReLateParam(r) => r.bound_region.get_id(), ty::ReStatic | ty::ReVar(_) | ty::RePlaceholder(_) @@ -712,7 +712,7 @@ fn matches_preds<'tcx>( preds: &'tcx [ty::PolyExistentialPredicate<'tcx>], ) -> bool { let infcx = cx.tcx.infer_ctxt().build(); - preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { + preds.iter().all(|&p| match cx.tcx.instantiate_bound_regions_with_erased(p) { ExistentialPredicate::Trait(p) => infcx .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.args.iter()), cx.param_env) .must_apply_modulo_regions(), diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index b133635e883..5c395143b9a 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -10,7 +10,6 @@ use clippy_utils::{ is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -179,17 +178,15 @@ fn expr_return_none_or_err( _ => false, }, ExprKind::Call(call_expr, args_expr) => { - if_chain! { - if smbl == sym::Result; - if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind; - if let Some(segment) = path.segments.first(); - if let Some(err_sym) = err_sym; - if let Some(arg) = args_expr.first(); - if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind; - if let Some(PathSegment { ident, .. }) = arg_path.segments.first(); - then { - return segment.ident.name == sym::Err && err_sym == ident.name; - } + if smbl == sym::Result + && let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind + && let Some(segment) = path.segments.first() + && let Some(err_sym) = err_sym + && let Some(arg) = args_expr.first() + && let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind + && let Some(PathSegment { ident, .. }) = arg_path.segments.first() + { + return segment.ident.name == sym::Err && err_sym == ident.name; } false }, @@ -218,81 +215,85 @@ impl QuestionMark { /// /// If it matches, it will suggest to use the question mark operator instead fn check_is_none_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if !self.inside_try_block(); - if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); - if !is_else_clause(cx.tcx, expr); - if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind; - let caller_ty = cx.typeck_results().expr_ty(caller); - let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else); - if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block); - then { - let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); - let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) && - !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); - let sugg = if let Some(else_inner) = r#else { - if eq_expr_value(cx, caller, peel_blocks(else_inner)) { - format!("Some({receiver_str}?)") - } else { - return; - } + if !self.inside_try_block() + && let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) + && !is_else_clause(cx.tcx, expr) + && let ExprKind::MethodCall(segment, caller, ..) = &cond.kind + && let caller_ty = cx.typeck_results().expr_ty(caller) + && let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else) + && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block)) + { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); + let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); + let sugg = if let Some(else_inner) = r#else { + if eq_expr_value(cx, caller, peel_blocks(else_inner)) { + format!("Some({receiver_str}?)") } else { - format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) - }; + return; + } + } else { + format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) + }; - span_lint_and_sugg( - cx, - QUESTION_MARK, - expr.span, - "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, - ); - } + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, + ); } } fn check_if_let_some_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if !self.inside_try_block(); - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr); - if !is_else_clause(cx.tcx, expr); - if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind; - if ddpos.as_opt_usize().is_none(); - if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind; - let caller_ty = cx.typeck_results().expr_ty(let_expr); - let if_block = IfBlockType::IfLet( + if !self.inside_try_block() + && let Some(higher::IfLet { + let_pat, + let_expr, + if_then, + if_else, + }) = higher::IfLet::hir(cx, expr) + && !is_else_clause(cx.tcx, expr) + && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind + && ddpos.as_opt_usize().is_none() + && let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind + && let caller_ty = cx.typeck_results().expr_ty(let_expr) + && let if_block = IfBlockType::IfLet( cx.qpath_res(path1, let_pat.hir_id), caller_ty, ident.name, let_expr, if_then, - if_else + if_else, + ) + && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) + || is_early_return(sym::Result, cx, &if_block)) + && if_else + .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) + .filter(|e| *e) + .is_none() + { + let mut applicability = Applicability::MachineApplicable; + let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); + let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); + let sugg = format!( + "{receiver_str}{}?{}", + if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, + if requires_semi { ";" } else { "" } + ); + span_lint_and_sugg( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + "replace it with", + sugg, + applicability, ); - if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) - || is_early_return(sym::Result, cx, &if_block); - if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none(); - then { - let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); - let sugg = format!( - "{receiver_str}{}?{}", - if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, - if requires_semi { ";" } else { "" } - ); - span_lint_and_sugg( - cx, - QUESTION_MARK, - expr.span, - "this block may be rewritten with the `?` operator", - "replace it with", - sugg, - applicability, - ); - } } } } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 1b3081abc13..fd9de76bacf 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -4,15 +4,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local}; -use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::source_map::Spanned; +use rustc_span::Span; use std::cmp::Ordering; declare_clippy_lint! { @@ -283,16 +282,14 @@ fn check_possible_range_contains( // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have // the same operator precedence - if_chain! { - if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind; - if op == lhs_op.node; - let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); - if let Some(snip) = &snippet_opt(cx, new_span); + if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind + && op == lhs_op.node + && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) + && let Some(snip) = &snippet_opt(cx, new_span) // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong - if snip.matches('(').count() == snip.matches(')').count(); - then { - check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); - } + && snip.matches('(').count() == snip.matches(')').count() + { + check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); } } @@ -349,71 +346,66 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> // exclusive range plus one: `x..(y+1)` fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if expr.span.can_be_used_for_suggestions(); - if let Some(higher::Range { + if expr.span.can_be_used_for_suggestions() + && let Some(higher::Range { start, end: Some(end), - limits: RangeLimits::HalfOpen - }) = higher::Range::hir(expr); - if let Some(y) = y_plus_one(cx, end); - then { - let span = expr.span; - span_lint_and_then( - cx, - RANGE_PLUS_ONE, - span, - "an inclusive range would be more readable", - |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); - if let Some(is_wrapped) = &snippet_opt(cx, span) { - if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { - diag.span_suggestion( - span, - "use", - format!("({start}..={end})"), - Applicability::MaybeIncorrect, - ); - } else { - diag.span_suggestion( - span, - "use", - format!("{start}..={end}"), - Applicability::MachineApplicable, // snippet - ); - } + limits: RangeLimits::HalfOpen, + }) = higher::Range::hir(expr) + && let Some(y) = y_plus_one(cx, end) + { + let span = expr.span; + span_lint_and_then( + cx, + RANGE_PLUS_ONE, + span, + "an inclusive range would be more readable", + |diag| { + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_par(); + if let Some(is_wrapped) = &snippet_opt(cx, span) { + if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { + diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect); + } else { + diag.span_suggestion( + span, + "use", + format!("{start}..={end}"), + Applicability::MachineApplicable, // snippet + ); } - }, - ); - } + } + }, + ); } } // inclusive range minus one: `x..=(y-1)` fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if expr.span.can_be_used_for_suggestions(); - if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr); - if let Some(y) = y_minus_one(cx, end); - then { - span_lint_and_then( - cx, - RANGE_MINUS_ONE, - expr.span, - "an exclusive range would be more readable", - |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); - diag.span_suggestion( - expr.span, - "use", - format!("{start}..{end}"), - Applicability::MachineApplicable, // snippet - ); - }, - ); - } + if expr.span.can_be_used_for_suggestions() + && let Some(higher::Range { + start, + end: Some(end), + limits: RangeLimits::Closed, + }) = higher::Range::hir(expr) + && let Some(y) = y_minus_one(cx, end) + { + span_lint_and_then( + cx, + RANGE_MINUS_ONE, + expr.span, + "an exclusive range would be more readable", + |diag| { + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_par(); + diag.span_suggestion( + expr.span, + "use", + format!("{start}..{end}"), + Applicability::MachineApplicable, // snippet + ); + }, + ); } } @@ -447,52 +439,54 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } } - if_chain! { - if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr); - let ty = cx.typeck_results().expr_ty(start); - if let ty::Int(_) | ty::Uint(_) = ty.kind(); - if let Some(start_idx) = constant(cx, cx.typeck_results(), start); - if let Some(end_idx) = constant(cx, cx.typeck_results(), end); - if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); - if is_empty_range(limits, ordering); - then { - if inside_indexing_expr(cx, expr) { - // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... - if ordering != Ordering::Equal { - span_lint( - cx, - REVERSED_EMPTY_RANGES, - expr.span, - "this range is reversed and using it to index a slice will panic at run-time", - ); - } - // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` - } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { - span_lint_and_then( + if let Some(higher::Range { + start: Some(start), + end: Some(end), + limits, + }) = 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 Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx) + && is_empty_range(limits, ordering) + { + if inside_indexing_expr(cx, expr) { + // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... + if ordering != Ordering::Equal { + span_lint( cx, REVERSED_EMPTY_RANGES, expr.span, - "this range is empty so it will yield no values", - |diag| { - if ordering != Ordering::Equal { - let start_snippet = snippet(cx, start.span, "_"); - let end_snippet = snippet(cx, end.span, "_"); - let dots = match limits { - RangeLimits::HalfOpen => "..", - RangeLimits::Closed => "..=" - }; - - diag.span_suggestion( - expr.span, - "consider using the following if you are attempting to iterate over this \ - range in reverse", - format!("({end_snippet}{dots}{start_snippet}).rev()"), - Applicability::MaybeIncorrect, - ); - } - }, + "this range is reversed and using it to index a slice will panic at run-time", ); } + // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` + } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { + span_lint_and_then( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is empty so it will yield no values", + |diag| { + if ordering != Ordering::Equal { + let start_snippet = snippet(cx, start.span, "_"); + let end_snippet = snippet(cx, end.span, "_"); + let dots = match limits { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=", + }; + + diag.span_suggestion( + expr.span, + "consider using the following if you are attempting to iterate over this \ + range in reverse", + format!("({end_snippet}{dots}{start_snippet}).rev()"), + Applicability::MaybeIncorrect, + ); + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index 391c77dbf90..98f5a07da0d 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRINGS, NEEDLESS_RAW_STRING_HASHES]); pub struct RawStrings { - pub needless_raw_string_hashes_allow_one: bool, + pub allow_one_hash_in_raw_strings: bool, } impl EarlyLintPass for RawStrings { diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs index 59ce289e7d2..b99af44655d 100644 --- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs @@ -118,26 +118,24 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< /// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak` fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { - if_chain! { - if let ExprKind::Call(func, _args) = expr.kind; - if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; - if let TyKind::Path(ref ty_path) = ty.kind; - if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); - - then { - if last_path_segment(func_path).ident.name == sym::new - && let Some(symbol) = cx - .tcx - .get_diagnostic_name(def_id) - .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) { - return Some((symbol, func.span)); - } + if let ExprKind::Call(func, _args) = expr.kind + && let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind + && let TyKind::Path(ref ty_path) = ty.kind + && let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id() + { + if last_path_segment(func_path).ident.name == sym::new + && let Some(symbol) = cx + .tcx + .get_diagnostic_name(def_id) + .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) + { + return Some((symbol, func.span)); + } - if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak)) - { - return Some((Symbol::intern("Weak"), func.span)); - } + if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind() + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak)) + { + return Some((Symbol::intern("Weak"), func.span)); } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 8daf085a42f..698af9fb192 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -3,7 +3,6 @@ use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, LangItem}; @@ -145,18 +144,16 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let pred_terminator = mir[ps[0]].terminator(); // receiver of the `deref()` call - let (pred_arg, deref_clone_ret) = if_chain! { - if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = - is_call_with_ref_arg(cx, mir, &pred_terminator.kind); - if res == cloned; - if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id); - if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) - || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString); - then { - (pred_arg, res) - } else { - continue; - } + let (pred_arg, deref_clone_ret) = if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = + is_call_with_ref_arg(cx, mir, &pred_terminator.kind) + && res == cloned + && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id) + && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) + || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString)) + { + (pred_arg, res) + } else { + continue; }; let (local, cannot_move_out) = @@ -211,45 +208,37 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .assert_crate_local() .lint_root; - if_chain! { - if let Some(snip) = snippet_opt(cx, span); - if let Some(dot) = snip.rfind('.'); - then { - let sugg_span = span.with_lo( - span.lo() + BytePos(u32::try_from(dot).unwrap()) - ); - let mut app = Applicability::MaybeIncorrect; - - let call_snip = &snip[dot + 1..]; - // Machine applicable when `call_snip` looks like `foobar()` - if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { - if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { - app = Applicability::MachineApplicable; - } + if let Some(snip) = snippet_opt(cx, span) + && let Some(dot) = snip.rfind('.') + { + let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); + let mut app = Applicability::MaybeIncorrect; + + let call_snip = &snip[dot + 1..]; + // Machine applicable when `call_snip` looks like `foobar()` + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { + if call_snip + .as_bytes() + .iter() + .all(|b| b.is_ascii_alphabetic() || *b == b'_') + { + app = Applicability::MachineApplicable; } + } - span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { - diag.span_suggestion( - sugg_span, - "remove this", - "", - app, + span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { + diag.span_suggestion(sugg_span, "remove this", "", app); + if clone_usage.cloned_used { + diag.span_note(span, "cloned value is neither consumed nor mutated"); + } else { + diag.span_note( + span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), + "this value is dropped without further use", ); - if clone_usage.cloned_used { - diag.span_note( - span, - "cloned value is neither consumed nor mutated", - ); - } else { - diag.span_note( - span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), - "this value is dropped without further use", - ); - } - }); - } else { - span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); - } + } + }); + } else { + span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); } } } @@ -261,18 +250,21 @@ fn is_call_with_ref_arg<'tcx>( mir: &'tcx mir::Body<'tcx>, kind: &'tcx mir::TerminatorKind<'tcx>, ) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> { - if_chain! { - if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; - if args.len() == 1; - if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind(); - if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)); - if !is_copy(cx, inner_ty); - then { - Some((def_id, *local, inner_ty, destination.as_local()?)) - } else { - None - } + if let mir::TerminatorKind::Call { + func, + args, + destination, + .. + } = kind + && args.len() == 1 + && let mir::Operand::Move(mir::Place { local, .. }) = &args[0] + && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() + && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)) + && !is_copy(cx, inner_ty) + { + Some((def_id, *local, inner_ty, destination.as_local()?)) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index e679fab5323..f2a006ebdfc 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -2,7 +2,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::get_parent_expr; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit as hir_visit; @@ -141,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } if let hir::ExprKind::Call(recv, _) = expr.kind - // don't lint if the receiver is a call, too. + // don't lint if the receiver is a call, too. // we do this in order to prevent linting multiple times; consider: // `(|| || 1)()()` // ^^ we only want to lint for this call (but we walk up the calls to consider both calls). @@ -201,14 +200,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if_chain! { - if let hir::ExprKind::Call(closure, _) = expr.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; - if self.path.segments[0].ident == path.segments[0].ident; - if self.path.res == path.res; - then { - self.count += 1; - } + if let hir::ExprKind::Call(closure, _) = expr.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind + && self.path.segments[0].ident == path.segments[0].ident + && self.path.res == path.res + { + self.count += 1; } hir_visit::walk_expr(self, expr); } @@ -223,25 +220,23 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } for w in block.stmts.windows(2) { - if_chain! { - if let hir::StmtKind::Local(local) = w[0].kind; - if let Option::Some(t) = local.init; - if let hir::ExprKind::Closure { .. } = t.kind; - if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; - if let hir::StmtKind::Semi(second) = w[1].kind; - if let hir::ExprKind::Assign(_, call, _) = second.kind; - if let hir::ExprKind::Call(closure, _) = call.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; - if ident == path.segments[0].ident; - if count_closure_usage(cx, block, path) == 1; - then { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "closure called just once immediately after it was declared", - ); - } + if let hir::StmtKind::Local(local) = w[0].kind + && let Option::Some(t) = local.init + && let hir::ExprKind::Closure { .. } = t.kind + && let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind + && let hir::StmtKind::Semi(second) = w[1].kind + && let hir::ExprKind::Assign(_, call, _) = second.kind + && let hir::ExprKind::Call(closure, _) = call.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind + && ident == path.segments[0].ident + && count_closure_usage(cx, block, path) == 1 + { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "closure called just once immediately after it was declared", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 6bc0d06183f..15b784039b6 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -46,40 +46,38 @@ declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); impl<'tcx> LateLintPass<'tcx> for RedundantLocals { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { - if_chain! { - if !local.span.is_desugaring(DesugaringKind::Async); + if !local.span.is_desugaring(DesugaringKind::Async) // the pattern is a single by-value binding - if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind; + && let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind // the binding is not type-ascribed - if local.ty.is_none(); + && local.ty.is_none() // the expression is a resolved path - if let Some(expr) = local.init; - if let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind; + && let Some(expr) = local.init + && let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind // the path is a single segment equal to the local's name - if let [last_segment] = path.segments; - if last_segment.ident == ident; + && let [last_segment] = path.segments + && last_segment.ident == ident // resolve the path to its defining binding pattern - if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id); - if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id); + && let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id) + && let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id) // the previous binding has the same mutability - if find_binding(binding_pat, ident).is_some_and(|bind| bind.1 == mutability); + && find_binding(binding_pat, ident).is_some_and(|bind| bind.1 == mutability) // the local does not change the effect of assignments to the binding. see #11290 - if !affects_assignments(cx, mutability, binding_id, local.hir_id); + && !affects_assignments(cx, mutability, binding_id, local.hir_id) // the local does not affect the code's drop behavior - if !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr)); + && !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr)) // the local is user-controlled - if !in_external_macro(cx.sess(), local.span); - if !is_from_proc_macro(cx, expr); - then { - span_lint_and_help( - cx, - REDUNDANT_LOCALS, - local.span, - &format!("redundant redefinition of a binding `{ident}`"), - Some(binding_pat.span), - &format!("`{ident}` is initially defined here"), - ); - } + && !in_external_macro(cx.sess(), local.span) + && !is_from_proc_macro(cx, expr) + { + span_lint_and_help( + cx, + REDUNDANT_LOCALS, + local.span, + &format!("redundant redefinition of a binding `{ident}`"), + Some(binding_pat.span), + &format!("`{ident}` is initially defined here"), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 03673eb27f8..32e0c3749ab 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -45,28 +45,27 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if_chain! { - if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); - if !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false); - if is_not_macro_export(item); - then { - let span = item.span.with_hi(item.ident.span.hi()); - let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); - span_lint_and_then( - cx, - REDUNDANT_PUB_CRATE, - span, - &format!("pub(crate) {descr} inside private module"), - |diag| { - diag.span_suggestion( - item.vis_span, - "consider using", - "pub".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - } + if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) + && !cx.effective_visibilities.is_exported(item.owner_id.def_id) + && self.is_exported.last() == Some(&false) + && is_not_macro_export(item) + { + let span = item.span.with_hi(item.ident.span.hi()); + let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); + span_lint_and_then( + cx, + REDUNDANT_PUB_CRATE, + span, + &format!("pub(crate) {descr} inside private module"), + |diag| { + diag.span_suggestion( + item.vis_span, + "consider using", + "pub".to_string(), + Applicability::MachineApplicable, + ); + }, + ); } if let ItemKind::Mod { .. } = item.kind { diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 7adbd67912c..c9fc65653c2 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs}; -use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -78,92 +77,83 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { } let ctxt = expr.span.ctxt(); - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; - if addressee.span.ctxt() == ctxt; - if let ExprKind::Index(indexed, range, _) = addressee.kind; - if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); - then { - 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 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; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind + && addressee.span.ctxt() == ctxt + && 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 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_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 - // `self` contains a reference to the `Self` type. If this occurs then the - // lint no longer applies as it's essentially a field access, which is not - // redundant. - return; - } - let deref_count = indexed_ref_count - expr_ref_count; + let ((lint, msg), help, sugg) = 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 + // `self` contains a reference to the `Self` type. If this occurs then the + // lint no longer applies as it's essentially a field access, which is not + // redundant. + return; + } + let deref_count = indexed_ref_count - expr_ref_count; - let (lint, reborrow_str, help_str) = 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!( - parent_expr, - Some(Expr { - kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), - .. - }) - ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| { - matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. }))) - }) { - // The slice was used to make a temporary reference. - (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead") - } else if deref_count != 0 { - (DEREF_BY_SLICING_LINT, "", "dereference the original value instead") - } else { - (REDUNDANT_SLICING_LINT, "", "use the original value instead") - }; + let (lint, reborrow_str, help_str) = 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!( + parent_expr, + Some(Expr { + kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), + .. + }) + ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| { + matches!( + a.kind, + Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })) + ) + }) { + // The slice was used to make a temporary reference. + (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead") + } else if deref_count != 0 { + (DEREF_BY_SLICING_LINT, "", "dereference the original value instead") + } else { + (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)) - }; + 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) - } 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(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) + (lint, help_str, sugg) + } 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(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 { - return; - } + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) } else { return; } } else { return; - }; + } + } else { + return; + }; - span_lint_and_sugg( - cx, - lint, - expr.span, - msg, - help, - sugg, - app, - ); - } + span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg, app); } } } diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index c984a8286eb..0ba898e75a1 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -38,34 +37,30 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { - if_chain! { - if let TyKind::Ref(_, ref mut_ty) = ty.kind; - if mut_ty.mutbl == Mutability::Not; - if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; - let last = last_path_segment(qpath); - if let Some(def_id) = last.res.opt_def_id(); - - if cx.tcx.is_diagnostic_item(sym::Option, def_id); - if let Some(params) = last_path_segment(qpath).args ; - if params.parenthesized == GenericArgsParentheses::No; - if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { + if let TyKind::Ref(_, ref mut_ty) = ty.kind + && mut_ty.mutbl == Mutability::Not + && let TyKind::Path(ref qpath) = &mut_ty.ty.kind + && let last = last_path_segment(qpath) + && let Some(def_id) = last.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::Option, def_id) + && let Some(params) = last_path_segment(qpath).args + && params.parenthesized == GenericArgsParentheses::No + && let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, - }); - if let TyKind::Ref(_, ref inner_mut_ty) = inner_ty.kind; - if inner_mut_ty.mutbl == Mutability::Not; - - then { - span_lint_and_sugg( - cx, - REF_OPTION_REF, - ty.span, - "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", - "try", - format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), - Applicability::MaybeIncorrect, - ); - } + }) + && let TyKind::Ref(_, ref inner_mut_ty) = inner_ty.kind + && inner_mut_ty.mutbl == Mutability::Not + { + span_lint_and_sugg( + cx, + REF_OPTION_REF, + ty.span, + "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`", + "try", + format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), + Applicability::MaybeIncorrect, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index 12da29f1108..69818db7c82 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -47,58 +46,62 @@ fn without_parens(mut e: &Expr) -> &Expr { impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if_chain! { - if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; - if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; - if deref_target.span.eq_ctxt(e.span); - if !addrof_target.span.from_expansion(); - then { - let mut applicability = Applicability::MachineApplicable; - let sugg = if e.span.from_expansion() { - if let Some(macro_source) = snippet_opt(cx, e.span) { - // Remove leading whitespace from the given span - // e.g: ` $visitor` turns into `$visitor` - let trim_leading_whitespaces = |span| { - snippet_opt(cx, span).and_then(|snip| { + if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind + && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind + && deref_target.span.eq_ctxt(e.span) + && !addrof_target.span.from_expansion() + { + let mut applicability = Applicability::MachineApplicable; + let sugg = if e.span.from_expansion() { + if let Some(macro_source) = snippet_opt(cx, e.span) { + // Remove leading whitespace from the given span + // e.g: ` $visitor` turns into `$visitor` + let trim_leading_whitespaces = |span| { + snippet_opt(cx, span) + .and_then(|snip| { #[expect(clippy::cast_possible_truncation)] - snip.find(|c: char| !c.is_whitespace()).map(|pos| { - span.lo() + BytePos(pos as u32) - }) - }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) - }; - - let mut generate_snippet = |pattern: &str| { - #[expect(clippy::cast_possible_truncation)] - macro_source.rfind(pattern).map(|pattern_pos| { - let rpos = pattern_pos + pattern.len(); - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| span.lo() + BytePos(pos as u32)) }) - }; + .map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) + }; + + let mut generate_snippet = |pattern: &str| { + #[expect(clippy::cast_possible_truncation)] + macro_source.rfind(pattern).map(|pattern_pos| { + let rpos = pattern_pos + pattern.len(); + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability) + }) + }; - if *mutability == Mutability::Mut { - generate_snippet("mut") - } else { - generate_snippet("&") - } + if *mutability == Mutability::Mut { + generate_snippet("mut") } else { - Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) + generate_snippet("&") } } else { - Some(snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)) - }; - if let Some(sugg) = sugg { - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try", - sugg.to_string(), - applicability, - ); + Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) } + } else { + Some(snippet_with_applicability( + cx, + addrof_target.span, + "_", + &mut applicability, + )) + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try", + sugg.to_string(), + applicability, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index cb78eec9e8d..ae8be006781 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -191,13 +191,11 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { } fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; - if let ExprKind::Array(exprs) = expr.kind; - then { - for expr in exprs { - check_regex(cx, expr, utf8); - } + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind + && let ExprKind::Array(exprs) = expr.kind + { + for expr in exprs { + check_regex(cx, expr, utf8); } } } diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs index 1975946c6e6..b4842e7d48a 100644 --- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// ```no_run /// let mut v: Vec<usize> = Vec::with_capacity(10); /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.74.0"] pub RESERVE_AFTER_INITIALIZATION, complexity, "`reserve` called immediately after `Vec` creation" diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 245029a066d..ad22b751bef 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -69,35 +69,32 @@ declare_clippy_lint! { declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, owner_id: OwnerId) { - if_chain! { + if !in_external_macro(cx.sess(), span) // If it comes from an external macro, better ignore it. - if !in_external_macro(cx.sess(), span); - if decl.implicit_self.has_implicit_self(); + && decl.implicit_self.has_implicit_self() // We only show this warning for public exported methods. - if cx.effective_visibilities.is_exported(fn_def); + && cx.effective_visibilities.is_exported(fn_def) // We don't want to emit this lint if the `#[must_use]` attribute is already there. - if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)); - if cx.tcx.visibility(fn_def.to_def_id()).is_public(); - let ret_ty = return_ty(cx, owner_id); - let self_arg = nth_arg(cx, owner_id, 0); + && !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)) + && cx.tcx.visibility(fn_def.to_def_id()).is_public() + && let ret_ty = return_ty(cx, owner_id) + && let self_arg = nth_arg(cx, owner_id, 0) // If `Self` has the same type as the returned type, then we want to warn. // // For this check, we don't want to remove the reference on the returned type because if // there is one, we shouldn't emit a warning! - if self_arg.peel_refs() == ret_ty; + && self_arg.peel_refs() == ret_ty // If `Self` is already marked as `#[must_use]`, no need for the attribute here. - if !is_must_use_ty(cx, ret_ty); - - then { - span_lint_and_help( - cx, - RETURN_SELF_NOT_MUST_USE, - span, - "missing `#[must_use]` attribute on a method returning `Self`", - None, - "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type" - ); - } + && !is_must_use_ty(cx, ret_ty) + { + span_lint_and_help( + cx, + RETURN_SELF_NOT_MUST_USE, + span, + "missing `#[must_use]` attribute on a method returning `Self`", + None, + "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type", + ); } } @@ -111,18 +108,15 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse { span: Span, fn_def: LocalDefId, ) { - if_chain! { + if matches!(kind, FnKind::Method(_, _)) // We are only interested in methods, not in functions or associated functions. - if matches!(kind, FnKind::Method(_, _)); - if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id()); + && let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id()) // We don't want this method to be te implementation of a trait because the // `#[must_use]` should be put on the trait definition directly. - if cx.tcx.trait_id_of_impl(impl_def).is_none(); - - then { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def); - check_method(cx, decl, fn_def, span, hir_id.expect_owner()); - } + && cx.tcx.trait_id_of_impl(impl_def).is_none() + { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def); + check_method(cx, decl, fn_def, span, hir_id.expect_owner()); } } diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index f2a3dc5099d..8595205691b 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -2,9 +2,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lin use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi}; +use clippy_utils::{fn_def_id, is_from_proc_macro, is_inside_let_else, path_to_local_id, span_find_starting_semi}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -170,6 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { && let ItemKind::Fn(_, _, body) = item.kind && let block = cx.tcx.hir().body(body).value && let ExprKind::Block(block, _) = block.kind + && !is_inside_let_else(cx.tcx, expr) && let [.., final_stmt] = block.stmts && final_stmt.hir_id != stmt.hir_id && !is_from_proc_macro(cx, expr) @@ -188,50 +188,45 @@ impl<'tcx> LateLintPass<'tcx> for Return { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if cx.tcx.hir().attrs(local.hir_id).is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(_, local_id, _, _) = local.pat.kind; - if path_to_local_id(retexpr, local_id); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !local.span.from_expansion(); - then { - span_lint_hir_and_then( - cx, - LET_AND_RETURN, - retexpr.hir_id, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); + if let Some(retexpr) = block.expr + && let Some(stmt) = block.stmts.iter().last() + && let StmtKind::Local(local) = &stmt.kind + && local.ty.is_none() + && cx.tcx.hir().attrs(local.hir_id).is_empty() + && let Some(initexpr) = &local.init + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && path_to_local_id(retexpr, local_id) + && !last_statement_borrows(cx, initexpr) + && !in_external_macro(cx.sess(), initexpr.span) + && !in_external_macro(cx.sess(), retexpr.span) + && !local.span.from_expansion() + { + span_lint_hir_and_then( + cx, + LET_AND_RETURN, + retexpr.hir_id, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); - if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if !has_enclosing_paren(&snippet) { - snippet = format!("({snippet})"); - } - snippet.push_str(" as _"); + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if !has_enclosing_paren(&snippet) { + snippet = format!("({snippet})"); } - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); + snippet.push_str(" as _"); } - }, - ); - } + err.multipart_suggestion( + "return the expression directly", + vec![(local.span, String::new()), (retexpr.span, snippet)], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index fd1f3d3903a..4f53cceeecf 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -75,26 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { match of_trait { Some(trait_ref) => { - let mut methods_in_trait: BTreeSet<Symbol> = if_chain! { - if let Some(Node::TraitRef(TraitRef { path, .. })) = - cx.tcx.hir().find(trait_ref.hir_ref_id); - if let Res::Def(DefKind::Trait, did) = path.res; - then{ - // FIXME: if - // `rustc_middle::ty::assoc::AssocItems::items` is public, - // we can iterate its keys instead of `in_definition_order`, - // which's more efficient - cx.tcx - .associated_items(did) - .in_definition_order() - .filter(|assoc_item| { - matches!(assoc_item.kind, AssocKind::Fn) - }) - .map(|assoc_item| assoc_item.name) - .collect() - }else{ - BTreeSet::new() - } + let mut methods_in_trait: BTreeSet<Symbol> = if let Some(Node::TraitRef(TraitRef { + path, .. + })) = cx.tcx.hir().find(trait_ref.hir_ref_id) + && let Res::Def(DefKind::Trait, did) = path.res + { + // FIXME: if + // `rustc_middle::ty::assoc::AssocItems::items` is public, + // we can iterate its keys instead of `in_definition_order`, + // which's more efficient + cx.tcx + .associated_items(did) + .in_definition_order() + .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) + .map(|assoc_item| assoc_item.name) + .collect() + } else { + BTreeSet::new() }; let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs index b92014f68b3..c1edcf509ef 100644 --- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs +++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs @@ -70,22 +70,20 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { return; } - if_chain! { - if let Some(self_def) = self_ty.ty_adt_def(); - if let Some(self_local_did) = self_def.did().as_local(); - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); - if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id); - let type_name = x.ident.name.as_str().to_lowercase(); - if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name; - - then { - span_lint( - cx, - SELF_NAMED_CONSTRUCTORS, - impl_item.span, - &format!("constructor `{}` has the same name as the type", impl_item.ident.name), - ); - } + if let Some(self_def) = self_ty.ty_adt_def() + && let Some(self_local_did) = self_def.did().as_local() + && let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did) + && let Some(Node::Item(x)) = cx.tcx.hir().find(self_id) + && let type_name = x.ident.name.as_str().to_lowercase() + && (impl_item.ident.name.as_str() == type_name + || impl_item.ident.name.as_str().replace('_', "") == type_name) + { + span_lint( + cx, + SELF_NAMED_CONSTRUCTORS, + impl_item.span, + &format!("constructor `{}` has the same name as the type", impl_item.ident.name), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs index ccf8b997705..3aabcadaa1f 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -38,30 +37,29 @@ declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED] impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if_chain! { - if !block.span.from_expansion(); - if let Some(expr) = block.expr; - let t_expr = cx.typeck_results().expr_ty(expr); - if t_expr.is_unit(); - let mut app = Applicability::MachineApplicable; - if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0; - if !snippet.ends_with('}') && !snippet.ends_with(';'); - if cx.sess().source_map().is_multiline(block.span); - then { - // filter out the desugared `for` loop - if let ExprKind::DropTemps(..) = &expr.kind { - return; - } - span_lint_and_sugg( - cx, - SEMICOLON_IF_NOTHING_RETURNED, - expr.span.source_callsite(), - "consider adding a `;` to the last statement for consistent formatting", - "add a `;` here", - format!("{snippet};"), - app, - ); + if !block.span.from_expansion() + && let Some(expr) = block.expr + && let t_expr = cx.typeck_results().expr_ty(expr) + && t_expr.is_unit() + && let mut app = Applicability::MachineApplicable + && let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0 + && !snippet.ends_with('}') + && !snippet.ends_with(';') + && cx.sess().source_map().is_multiline(block.span) + { + // filter out the desugared `for` loop + if let ExprKind::DropTemps(..) = &expr.kind { + return; } + span_lint_and_sugg( + cx, + SEMICOLON_IF_NOTHING_RETURNED, + expr.span.source_callsite(), + "consider adding a `;` to the last statement for consistent formatting", + "add a `;` here", + format!("{snippet};"), + app, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index b940cac6047..0385f1a98e5 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -2,7 +2,6 @@ //! expecting a count of T use clippy_utils::diagnostics::span_lint_and_help; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty, TypeAndMut}; @@ -39,16 +38,17 @@ declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { match expr.kind { ExprKind::Call(count_func, _func_args) => { - if_chain! { - if !inverted; - if let ExprKind::Path(ref count_func_qpath) = count_func.kind; - if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); - if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val)); - then { - cx.typeck_results().node_args(count_func.hir_id).types().next() - } else { - None - } + if !inverted + && let ExprKind::Path(ref count_func_qpath) = count_func.kind + && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::mem_size_of | sym::mem_size_of_val) + ) + { + cx.typeck_results().node_args(count_func.hir_id).types().next() + } else { + None } }, ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => { @@ -80,13 +80,12 @@ fn get_pointee_ty_and_count_expr<'tcx>( "wrapping_offset", ]; - if_chain! { + if let ExprKind::Call(func, [.., count]) = expr.kind // Find calls to ptr::{copy, copy_nonoverlapping} // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, [.., count]) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if matches!(cx.tcx.get_diagnostic_name(def_id), Some( + && let ExprKind::Path(ref func_qpath) = func.kind + && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() + && matches!(cx.tcx.get_diagnostic_name(def_id), Some( sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_slice_from_raw_parts @@ -95,26 +94,23 @@ fn get_pointee_ty_and_count_expr<'tcx>( | sym::ptr_write_bytes | sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut - )); + )) // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } + && let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next() + { + return Some((pointee_ty, count)); }; - if_chain! { + if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind; - let method_ident = method_path.ident.as_str(); - if METHODS.iter().any(|m| *m == method_ident); + && let method_ident = method_path.ident.as_str() + && METHODS.iter().any(|m| *m == method_ident) // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = - cx.typeck_results().expr_ty(ptr_self).kind(); - then { - return Some((*pointee_ty, count)); - } + && let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = + cx.typeck_results().expr_ty(ptr_self).kind() + { + return Some((*pointee_ty, count)); }; None } @@ -127,25 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { const LINT_MSG: &str = "found a count of bytes \ instead of a count of elements of `T`"; - if_chain! { + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr) // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression - if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); // Find a size_of call in the count parameter expression and // check that it's the same type - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false); - if pointee_ty == ty_used_for_size_of; - then { - span_lint_and_help( - cx, - SIZE_OF_IN_ELEMENT_COUNT, - count_expr.span, - LINT_MSG, - None, - HELP_MSG - ); - } + && let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false) + && pointee_ty == ty_used_for_size_of + { + span_lint_and_help(cx, SIZE_OF_IN_ELEMENT_COUNT, count_expr.span, LINT_MSG, None, HELP_MSG); }; } } diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 2244eab96ca..733da790441 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -5,7 +5,6 @@ use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, paths, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; @@ -103,41 +102,35 @@ enum InitializationType<'tcx> { impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` - if_chain! { - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(local_id) = path_to_local(left); - if let Some(size_expr) = Self::as_vec_initializer(cx, right); - - then { - let vi = VecAllocation { - local_id, - allocation_expr: right, - size_expr, - }; - - Self::search_initialization(cx, vi, expr.hir_id); - } + if let ExprKind::Assign(left, right, _) = expr.kind + && let Some(local_id) = path_to_local(left) + && let Some(size_expr) = Self::as_vec_initializer(cx, right) + { + let vi = VecAllocation { + local_id, + allocation_expr: right, + size_expr, + }; + + Self::search_initialization(cx, vi, expr.hir_id); } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` // or `Vec::new()` - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind; - if let Some(init) = local.init; - if let Some(size_expr) = Self::as_vec_initializer(cx, init); - - then { - let vi = VecAllocation { - local_id, - allocation_expr: init, - size_expr, - }; - - Self::search_initialization(cx, vi, stmt.hir_id); - } + if let StmtKind::Local(local) = stmt.kind + && let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind + && let Some(init) = local.init + && let Some(size_expr) = Self::as_vec_initializer(cx, init) + { + let vi = VecAllocation { + local_id, + allocation_expr: init, + size_expr, + }; + + Self::search_initialization(cx, vi, stmt.hir_id); } } } @@ -242,16 +235,13 @@ struct VectorInitializationVisitor<'a, 'tcx> { impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Checks if the given expression is extending a vector with `repeat(0).take(..)` fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if self.initialization_found; - if let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind; - if path_to_local_id(self_arg, self.vec_alloc.local_id); - if path.ident.name == sym!(extend); - if self.is_repeat_take(extend_arg); - - then { - self.slow_expression = Some(InitializationType::Extend(expr)); - } + if self.initialization_found + && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind + && path_to_local_id(self_arg, self.vec_alloc.local_id) + && path.ident.name == sym!(extend) + && self.is_repeat_take(extend_arg) + { + self.slow_expression = Some(InitializationType::Extend(expr)); } } @@ -281,21 +271,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { - if_chain! { - if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind; - if take_path.ident.name == sym!(take); + if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind + && take_path.ident.name == sym!(take) // Check that take is applied to `repeat(0)` - if self.is_repeat_zero(recv); - then { - if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { - // Check that len expression is equals to `with_capacity` expression - return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") - } - - self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); - return true; + && self.is_repeat_zero(recv) + { + if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { + // Check that len expression is equals to `with_capacity` expression + return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity"); } + + self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); + return true; } false @@ -303,15 +291,13 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; - if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat); - if is_integer_literal(repeat_arg, 0); - then { - true - } else { - false - } + if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind + && is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat) + && is_integer_literal(repeat_arg, 0) + { + true + } else { + false } } } diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index a44adc93855..baa9750cc01 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -5,7 +5,6 @@ use clippy_utils::{ get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls, peel_blocks, SpanlessEq, }; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; @@ -251,128 +250,115 @@ const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { - #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use rustc_ast::LitKind; - if_chain! { + if let ExprKind::Call(fun, args) = e.kind // Find std::str::converts::from_utf8 - if let ExprKind::Call(fun, args) = e.kind; - if is_path_diagnostic_item(cx, fun, sym::str_from_utf8); + && is_path_diagnostic_item(cx, fun, sym::str_from_utf8) // Find string::as_bytes - if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; - if let ExprKind::Index(left, right, _) = args.kind; - let (method_names, expressions, _) = method_calls(left, 1); - if method_names.len() == 1; - if expressions.len() == 1; - if expressions[0].1.is_empty(); - if method_names[0] == sym!(as_bytes); + && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind + && let ExprKind::Index(left, right, _) = args.kind + && let (method_names, expressions, _) = method_calls(left, 1) + && method_names.len() == 1 + && expressions.len() == 1 + && expressions[0].1.is_empty() + && method_names[0] == sym!(as_bytes) // Check for slicer - if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind; + && let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind + { + let mut applicability = Applicability::MachineApplicable; + let string_expression = &expressions[0].0; - then { - let mut applicability = Applicability::MachineApplicable; - let string_expression = &expressions[0].0; + let snippet_app = snippet_with_applicability(cx, string_expression.span, "..", &mut applicability); - let snippet_app = snippet_with_applicability( - cx, - string_expression.span, "..", - &mut applicability, - ); + span_lint_and_sugg( + cx, + STRING_FROM_UTF8_AS_BYTES, + e.span, + "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", + "try", + format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), + applicability, + ); + } + if !in_external_macro(cx.sess(), e.span) + && let ExprKind::MethodCall(path, receiver, ..) = &e.kind + && path.ident.name == sym!(as_bytes) + && let ExprKind::Lit(lit) = &receiver.kind + && let LitKind::Str(lit_content, _) = &lit.node + { + let callsite = snippet(cx, receiver.span.source_callsite(), r#""foo""#); + let mut applicability = Applicability::MachineApplicable; + if callsite.starts_with("include_str!") { span_lint_and_sugg( cx, - STRING_FROM_UTF8_AS_BYTES, + STRING_LIT_AS_BYTES, e.span, - "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", - "try", - format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), - applicability - ) - } - } - - if_chain! { - if !in_external_macro(cx.sess(), e.span); - if let ExprKind::MethodCall(path, receiver, ..) = &e.kind; - if path.ident.name == sym!(as_bytes); - if let ExprKind::Lit(lit) = &receiver.kind; - if let LitKind::Str(lit_content, _) = &lit.node; - then { - let callsite = snippet(cx, receiver.span.source_callsite(), r#""foo""#); - let mut applicability = Applicability::MachineApplicable; - if callsite.starts_with("include_str!") { + "calling `as_bytes()` on `include_str!(..)`", + "consider using `include_bytes!(..)` instead", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability).replacen( + "include_str", + "include_bytes", + 1, + ), + applicability, + ); + } else if lit_content.as_str().is_ascii() + && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT + && !receiver.span.from_expansion() + { + if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) + && let Node::Expr(parent) = parent + && let ExprKind::Match(scrutinee, ..) = parent.kind + && scrutinee.hir_id == id + { + // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces + // `&[u8]`. This change would prevent matching with different sized slices. + } else if !callsite.starts_with("env!") { span_lint_and_sugg( cx, STRING_LIT_AS_BYTES, e.span, - "calling `as_bytes()` on `include_str!(..)`", - "consider using `include_bytes!(..)` instead", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability).replacen( - "include_str", - "include_bytes", - 1, + "calling `as_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) ), applicability, ); - } else if lit_content.as_str().is_ascii() - && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT - && !receiver.span.from_expansion() - { - if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) - && let Node::Expr(parent) = parent - && let ExprKind::Match(scrutinee, ..) = parent.kind - && scrutinee.hir_id == id - { - // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces - // `&[u8]`. This change would prevent matching with different sized slices. - } else if !callsite.starts_with("env!") { - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `as_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) - ), - applicability, - ); - } } } } - if_chain! { - if let ExprKind::MethodCall(path, recv, [], _) = &e.kind; - if path.ident.name == sym!(into_bytes); - if let ExprKind::MethodCall(path, recv, [], _) = &recv.kind; - if matches!(path.ident.name.as_str(), "to_owned" | "to_string"); - if let ExprKind::Lit(lit) = &recv.kind; - if let LitKind::Str(lit_content, _) = &lit.node; - - if lit_content.as_str().is_ascii(); - if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT; - if !recv.span.from_expansion(); - then { - let mut applicability = Applicability::MachineApplicable; + if let ExprKind::MethodCall(path, recv, [], _) = &e.kind + && path.ident.name == sym!(into_bytes) + && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind + && matches!(path.ident.name.as_str(), "to_owned" | "to_string") + && let ExprKind::Lit(lit) = &recv.kind + && let LitKind::Str(lit_content, _) = &lit.node + && lit_content.as_str().is_ascii() + && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT + && !recv.span.from_expansion() + { + let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `into_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}.to_vec()", - snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) - ), - applicability, - ); - } + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `into_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}.to_vec()", + snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) + ), + applicability, + ); } } } @@ -406,22 +392,20 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; - if path.ident.name == sym::to_string; - let ty = cx.typeck_results().expr_ty(self_arg); - if let ty::Ref(_, ty, ..) = ty.kind(); - if ty.is_str(); - then { - span_lint_and_help( - cx, - STR_TO_STRING, - expr.span, - "`to_string()` called on a `&str`", - None, - "consider using `.to_owned()`", - ); - } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + && path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && let ty::Ref(_, ty, ..) = ty.kind() + && ty.is_str() + { + span_lint_and_help( + cx, + STR_TO_STRING, + expr.span, + "`to_string()` called on a `&str`", + None, + "consider using `.to_owned()`", + ); } } } @@ -456,21 +440,19 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; - if path.ident.name == sym::to_string; - let ty = cx.typeck_results().expr_ty(self_arg); - if is_type_lang_item(cx, ty, LangItem::String); - then { - span_lint_and_help( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - None, - "consider using `.clone()`", - ); - } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + && path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && is_type_lang_item(cx, ty, LangItem::String) + { + span_lint_and_help( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + None, + "consider using `.clone()`", + ); } } } @@ -500,26 +482,24 @@ declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); - if_chain! { - if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind; - if path.ident.name == sym!(split_whitespace); - if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); - if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); - if let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind; - if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); - if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); - if is_one_of_trim_diagnostic_items(cx, trim_def_id); - then { - span_lint_and_sugg( - cx, - TRIM_SPLIT_WHITESPACE, - trim_span.with_hi(split_ws_span.lo()), - &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), - &format!("remove `{trim_fn_name}()`"), - String::new(), - Applicability::MachineApplicable, - ); - } + if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind + && path.ident.name == sym!(split_whitespace) + && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) + && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) + && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind + && let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str() + && let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id) + && is_one_of_trim_diagnostic_items(cx, trim_def_id) + { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), + &format!("remove `{trim_fn_name}()`"), + String::new(), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index b3db5e9a422..644664b104d 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -3,7 +3,6 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{get_parent_node, match_libc_symbol}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; @@ -41,48 +40,45 @@ declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Call(func, [recv]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if match_libc_symbol(cx, did, "strlen"); - if let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind; - if !recv.span.from_expansion(); - if path.ident.name == sym::as_ptr; - then { - let ctxt = expr.span.ctxt(); - let span = match get_parent_node(cx.tcx, expr.hir_id) { - Some(Node::Block(&Block { - rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, .. - })) - if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => { - span - } - _ => expr.span, - }; + if !expr.span.from_expansion() + && let ExprKind::Call(func, [recv]) = expr.kind + && let ExprKind::Path(path) = &func.kind + && let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_libc_symbol(cx, did, "strlen") + && let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind + && !recv.span.from_expansion() + && path.ident.name == sym::as_ptr + { + let ctxt = expr.span.ctxt(); + let span = match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Block(&Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + span, + .. + })) if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => span, + _ => expr.span, + }; - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - let mut app = Applicability::MachineApplicable; - let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; - let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { - "as_bytes" - } else if is_type_lang_item(cx, ty, LangItem::CStr) { - "to_bytes" - } else { - return; - }; + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + let mut app = Applicability::MachineApplicable; + let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { + "as_bytes" + } else if is_type_lang_item(cx, ty, LangItem::CStr) { + "to_bytes" + } else { + return; + }; - span_lint_and_sugg( - cx, - STRLEN_ON_C_STRINGS, - span, - "using `libc::strlen` on a `CString` or `CStr` value", - "try", - format!("{val_name}.{method_name}().len()"), - app, - ); - } + span_lint_and_sugg( + cx, + STRLEN_ON_C_STRINGS, + span, + "using `libc::strlen` on a `CString` or `CStr` value", + "try", + format!("{val_name}.{method_name}().len()"), + app, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs deleted file mode 100644 index 0abc199da16..00000000000 --- a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs +++ /dev/null @@ -1,95 +0,0 @@ -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; -use if_chain::if_chain; -use rustc_ast::token::CommentKind; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Item}; -use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// ### What it does - /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!` - /// - /// ### Why is this bad? - /// Triple-slash comments (known as "outer doc comments") apply to items that follow it. - /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning. - /// - /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which - /// applies to the parent item (i.e. the item that the comment is contained in, - /// usually a module or crate). - /// - /// ### Known problems - /// Inner doc comments can only appear before items, so there are certain cases where the suggestion - /// made by this lint is not valid code. For example: - /// ```rs - /// fn foo() {} - /// ///! - /// fn bar() {} - /// ``` - /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment - /// is not valid at that position. - /// - /// ### Example - /// In this example, the doc comment is attached to the *function*, rather than the *module*. - /// ```no_run - /// pub mod util { - /// ///! This module contains utility functions. - /// - /// pub fn dummy() {} - /// } - /// ``` - /// - /// Use instead: - /// ```no_run - /// pub mod util { - /// //! This module contains utility functions. - /// - /// pub fn dummy() {} - /// } - /// ``` - #[clippy::version = "1.70.0"] - pub SUSPICIOUS_DOC_COMMENTS, - suspicious, - "suspicious usage of (outer) doc comments" -} -declare_lint_pass!(SuspiciousDocComments => [SUSPICIOUS_DOC_COMMENTS]); - -const WARNING: &str = "this is an outer doc comment and does not apply to the parent module or crate"; -const HELP: &str = "use an inner doc comment to document the parent module or crate"; - -impl EarlyLintPass for SuspiciousDocComments { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let replacements = collect_doc_comment_replacements(&item.attrs); - - if let Some(((lo_span, _), (hi_span, _))) = replacements.first().zip(replacements.last()) { - let span = lo_span.to(*hi_span); - - span_lint_and_then(cx, SUSPICIOUS_DOC_COMMENTS, span, WARNING, |diag| { - multispan_sugg_with_applicability(diag, HELP, Applicability::MaybeIncorrect, replacements); - }); - } - } -} - -fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> { - attrs - .iter() - .filter_map(|attr| { - if_chain! { - if let AttrKind::DocComment(com_kind, sym) = attr.kind; - if let AttrStyle::Outer = attr.style; - if let Some(com) = sym.as_str().strip_prefix('!'); - then { - let sugg = match com_kind { - CommentKind::Line => format!("//!{com}"), - CommentKind::Block => format!("/*!{com}*/") - }; - Some((attr.span, sugg)) - } else { - None - } - } - }) - .collect() -} diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index bb8cde5b94d..92de7917871 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -2,7 +2,6 @@ use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; -use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -155,34 +154,22 @@ fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) { match (no_difference_info, double_difference_info) { (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc), (None, Some((double_difference_index, ident_loc1, ident_loc2))) => { - if_chain! { - if one_ident_difference_count == binop_count - 1; - if let Some(binop) = binops.get(double_difference_index); - then { - let changed_loc = if ident_loc1 == expected_loc { - ident_loc2 - } else if ident_loc2 == expected_loc { - ident_loc1 - } else { - // This expression doesn't match the form we're - // looking for. - return; - }; - - if let Some(sugg) = ident_swap_sugg( - cx, - &paired_identifiers, - binop, - changed_loc, - &mut applicability, - ) { - emit_suggestion( - cx, - binop.span, - sugg, - applicability, - ); - } + if one_ident_difference_count == binop_count - 1 + && let Some(binop) = binops.get(double_difference_index) + { + let changed_loc = if ident_loc1 == expected_loc { + ident_loc2 + } else if ident_loc2 == expected_loc { + ident_loc1 + } else { + // This expression doesn't match the form we're + // looking for. + return; + }; + + if let Some(sugg) = ident_swap_sugg(cx, &paired_identifiers, binop, changed_loc, &mut applicability) + { + emit_suggestion(cx, binop.span, sugg, applicability); } } }, @@ -212,48 +199,32 @@ fn attempt_to_emit_no_difference_lint( let old_right_ident = get_ident(binop.right, expected_loc); for b in skip_index(binops.iter(), i) { - if_chain! { - if let (Some(old_ident), Some(new_ident)) = - (old_left_ident, get_ident(b.left, expected_loc)); - if old_ident != new_ident; - if let Some(sugg) = suggestion_with_swapped_ident( + if let (Some(old_ident), Some(new_ident)) = (old_left_ident, get_ident(b.left, expected_loc)) + && old_ident != new_ident + && let Some(sugg) = + suggestion_with_swapped_ident(cx, binop.left, expected_loc, new_ident, &mut applicability) + { + emit_suggestion( cx, - binop.left, - expected_loc, - new_ident, - &mut applicability, + binop.span, + replace_left_sugg(cx, binop, &sugg, &mut applicability), + applicability, ); - then { - emit_suggestion( - cx, - binop.span, - replace_left_sugg(cx, binop, &sugg, &mut applicability), - applicability, - ); - return; - } + return; } - if_chain! { - if let (Some(old_ident), Some(new_ident)) = - (old_right_ident, get_ident(b.right, expected_loc)); - if old_ident != new_ident; - if let Some(sugg) = suggestion_with_swapped_ident( + if let (Some(old_ident), Some(new_ident)) = (old_right_ident, get_ident(b.right, expected_loc)) + && old_ident != new_ident + && let Some(sugg) = + suggestion_with_swapped_ident(cx, binop.right, expected_loc, new_ident, &mut applicability) + { + emit_suggestion( cx, - binop.right, - expected_loc, - new_ident, - &mut applicability, + binop.span, + replace_right_sugg(cx, binop, &sugg, &mut applicability), + applicability, ); - then { - emit_suggestion( - cx, - binop.span, - replace_right_sugg(cx, binop, &sugg, &mut applicability), - applicability, - ); - return; - } + return; } } } diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 6271ea02731..3244933a124 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -57,37 +56,39 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind; - if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node); - if let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang); - if let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang); + if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind + && let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node) + && let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang) + && let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang) // Check for more than one binary operation in the implemented function // Linting when multiple operations are involved can result in false positives - let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id; - if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get_by_def_id(parent_fn); - if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; - let body = cx.tcx.hir().body(body_id); - let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id; - if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn); - let trait_id = trait_ref.path.res.def_id(); - if ![binop_trait_id, op_assign_trait_id].contains(&trait_id); - if let Some(&(_, lint)) = [ + && let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id + && let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get_by_def_id(parent_fn) + && let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind + && let body = cx.tcx.hir().body(body_id) + && let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id).def_id + && let Some(trait_ref) = trait_ref_of_method(cx, parent_fn) + && let trait_id = trait_ref.path.res.def_id() + && ![binop_trait_id, op_assign_trait_id].contains(&trait_id) + && let Some(&(_, lint)) = [ (&BINOP_TRAITS, SUSPICIOUS_ARITHMETIC_IMPL), (&OP_ASSIGN_TRAITS, SUSPICIOUS_OP_ASSIGN_IMPL), ] .iter() - .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))); - if count_binops(body.value) == 1; - then { - span_lint( - cx, - lint, - binop.span, - &format!("suspicious use of `{}` in `{}` impl", binop.node.as_str(), cx.tcx.item_name(trait_id)), - ); - } + .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))) + && count_binops(body.value) == 1 + { + span_lint( + cx, + lint, + binop.span, + &format!( + "suspicious use of `{}` in `{}` impl", + binop.node.as_str(), + cx.tcx.item_name(trait_id) + ), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 660e6835e46..285f2f4f6f9 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -3,7 +3,6 @@ use clippy_utils::source::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 if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -149,36 +148,33 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { } for [s1, s2, s3] in block.stmts.array_windows::<3>() { - if_chain! { + if let StmtKind::Local(tmp) = s1.kind // let t = foo(); - if let StmtKind::Local(tmp) = s1.kind; - if let Some(tmp_init) = tmp.init; - if let PatKind::Binding(.., ident, None) = tmp.pat.kind; + && let Some(tmp_init) = tmp.init + && let PatKind::Binding(.., ident, None) = tmp.pat.kind // foo() = bar(); - if let StmtKind::Semi(first) = s2.kind; - if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; + && let StmtKind::Semi(first) = s2.kind + && let ExprKind::Assign(lhs1, rhs1, _) = first.kind // bar() = t; - if let StmtKind::Semi(second) = s3.kind; - if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; - if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; - if rhs2.segments.len() == 1; + && let StmtKind::Semi(second) = s3.kind + && let ExprKind::Assign(lhs2, rhs2, _) = second.kind + && let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind + && rhs2.segments.len() == 1 - if ident.name == rhs2.segments[0].ident.name; - if eq_expr_value(cx, tmp_init, lhs1); - if eq_expr_value(cx, rhs1, lhs2); + && ident.name == rhs2.segments[0].ident.name + && eq_expr_value(cx, tmp_init, lhs1) + && eq_expr_value(cx, rhs1, lhs2) - let ctxt = s1.span.ctxt(); - if s2.span.ctxt() == ctxt; - if s3.span.ctxt() == ctxt; - if first.span.ctxt() == ctxt; - if second.span.ctxt() == ctxt; - - then { - let span = s1.span.to(s3.span); - generate_swap_warning(cx, lhs1, lhs2, span, false); - } + && let ctxt = s1.span.ctxt() + && s2.span.ctxt() == ctxt + && s3.span.ctxt() == ctxt + && first.span.ctxt() == ctxt + && second.span.ctxt() == ctxt + { + let span = s1.span.to(s3.span); + generate_swap_warning(cx, lhs1, lhs2, span, false); } } } @@ -261,20 +257,18 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { for [s1, s2, s3] in block.stmts.array_windows::<3>() { let ctxt = s1.span.ctxt(); - if_chain! { - if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt); - if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt); - if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt); - if eq_expr_value(cx, lhs0, rhs1); - if eq_expr_value(cx, lhs2, rhs1); - if eq_expr_value(cx, lhs1, rhs0); - if eq_expr_value(cx, lhs1, rhs2); - if s2.span.ctxt() == ctxt; - if s3.span.ctxt() == ctxt; - then { - let span = s1.span.to(s3.span); - generate_swap_warning(cx, lhs0, rhs0, span, true); - } + if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt) + && let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt) + && let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt) + && eq_expr_value(cx, lhs0, rhs1) + && eq_expr_value(cx, lhs2, rhs1) + && eq_expr_value(cx, lhs1, rhs0) + && eq_expr_value(cx, lhs1, rhs2) + && s2.span.ctxt() == ctxt + && s3.span.ctxt() == ctxt + { + let span = s1.span.to(s3.span); + generate_swap_warning(cx, lhs0, rhs0, span, true); }; } } 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 0cfb1c1253c..9481c78a505 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 @@ -55,20 +55,18 @@ impl LateLintPass<'_> for TestsOutsideTestModule { sp: Span, _: LocalDefId, ) { - if_chain! { - if !matches!(kind, FnKind::Closure); - if is_in_test_function(cx.tcx, body.id().hir_id); - if !is_in_cfg_test(cx.tcx, body.id().hir_id); - then { - span_lint_and_note( - 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)]", - ); - } + if !matches!(kind, FnKind::Closure) + && is_in_test_function(cx.tcx, body.id().hir_id) + && !is_in_cfg_test(cx.tcx, body.id().hir_id) + { + span_lint_and_note( + 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)]", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index a171d225f1e..1dca523a966 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -38,59 +37,57 @@ declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind; - if is_some_path.ident.name.as_str() == "is_some"; - then { - let match_result = match &to_digit_expr.kind { - hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { - if_chain! { - if to_digits_path.ident.name.as_str() == "to_digit"; - let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg); - if *char_arg_ty.kind() == ty::Char; - then { - Some((true, *char_arg, radix_arg)) - } else { - None - } - } + if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind + && is_some_path.ident.name.as_str() == "is_some" + { + let match_result = match &to_digit_expr.kind { + hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { + if to_digits_path.ident.name.as_str() == "to_digit" + && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) + && *char_arg_ty.kind() == ty::Char + { + Some((true, *char_arg, radix_arg)) + } else { + None } - hir::ExprKind::Call(to_digits_call, to_digit_args) => { - if_chain! { - if let [char_arg, radix_arg] = *to_digit_args; - if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind; - if let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id); - if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id(); - if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "<impl char>", "to_digit"]); - then { - Some((false, char_arg, radix_arg)) - } else { - None - } - } + }, + hir::ExprKind::Call(to_digits_call, to_digit_args) => { + if let [char_arg, radix_arg] = *to_digit_args + && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind + && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) + && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() + && match_def_path( + cx, + to_digits_def_id, + &["core", "char", "methods", "<impl char>", "to_digit"], + ) + { + Some((false, char_arg, radix_arg)) + } else { + None } - _ => None - }; + }, + _ => None, + }; - if let Some((is_method_call, char_arg, radix_arg)) = match_result { - let mut applicability = Applicability::MachineApplicable; - let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); - let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); + if let Some((is_method_call, char_arg, radix_arg)) = match_result { + let mut applicability = Applicability::MachineApplicable; + let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); + let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - TO_DIGIT_IS_SOME, - expr.span, - "use of `.to_digit(..).is_some()`", - "try", - if is_method_call { - format!("{char_arg_snip}.is_digit({radix_snip})") - } else { - format!("char::is_digit({char_arg_snip}, {radix_snip})") - }, - applicability, - ); - } + span_lint_and_sugg( + cx, + TO_DIGIT_IS_SOME, + expr.span, + "use of `.to_digit(..).is_some()`", + "try", + if is_method_call { + format!("{char_arg_snip}.is_digit({radix_snip})") + } else { + format!("char::is_digit({char_arg_snip}, {radix_snip})") + }, + applicability, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index 87181adc24b..7eef02b3c65 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -54,20 +54,18 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if_chain! { + if let ItemKind::Struct(data, _) = &item.kind // First check if last field is an array - if let ItemKind::Struct(data, _) = &item.kind; - if let Some(last_field) = data.fields().last(); - if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind; + && let Some(last_field) = data.fields().last() + && let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind // Then check if that array is zero-sized - let length = Const::from_anon_const(cx.tcx, length.def_id); - let length = length.try_eval_target_usize(cx.tcx, cx.param_env); - if let Some(length) = length; - then { - length == 0 - } else { - false - } + && let length = Const::from_anon_const(cx.tcx, length.def_id) + && let length = length.try_eval_target_usize(cx.tcx, cx.param_env) + && let Some(length) = length + { + length == 0 + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index f065d215e48..e624bbe5ff1 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; use core::hash::{Hash, Hasher}; -use if_chain::if_chain; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; @@ -124,103 +123,100 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut self_bounds_map = FxHashMap::default(); for predicate in item.generics.predicates { - if_chain! { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; - if bound_predicate.origin != PredicateOrigin::ImplTrait; - if !bound_predicate.span.from_expansion(); - if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; - if let Some(PathSegment { - res: Res::SelfTyParam { trait_: def_id }, .. - }) = segments.first(); - if let Some( - Node::Item( - Item { - kind: ItemKind::Trait(_, _, _, self_bounds, _), - .. } - ) - ) = cx.tcx.hir().get_if_local(*def_id); - then { - if self_bounds_map.is_empty() { - for bound in *self_bounds { - let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue }; - self_bounds_map.insert(self_res, self_segments); - } + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate + && bound_predicate.origin != PredicateOrigin::ImplTrait + && !bound_predicate.span.from_expansion() + && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind + && let Some(PathSegment { + res: Res::SelfTyParam { trait_: def_id }, + .. + }) = segments.first() + && let Some(Node::Item(Item { + kind: ItemKind::Trait(_, _, _, self_bounds, _), + .. + })) = cx.tcx.hir().get_if_local(*def_id) + { + if self_bounds_map.is_empty() { + for bound in *self_bounds { + let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { + continue; + }; + self_bounds_map.insert(self_res, self_segments); } + } - bound_predicate - .bounds - .iter() - .filter_map(get_trait_info_from_bound) - .for_each(|(trait_item_res, trait_item_segments, span)| { - if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { - if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in trait declaration", - None, - "consider removing this trait bound", - ); - } + bound_predicate + .bounds + .iter() + .filter_map(get_trait_info_from_bound) + .for_each(|(trait_item_res, trait_item_segments, span)| { + if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { + if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); } - }); - } + } + }); } } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { - if_chain! { - if let TyKind::Ref(.., mut_ty) = &ty.kind; - if let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind; - if bounds.len() > 2; - then { - - // Build up a hash of every trait we've seen - // When we see a trait for the first time, add it to unique_traits - // so we can later use it to build a string of all traits exactly once, without duplicates + if let TyKind::Ref(.., mut_ty) = &ty.kind + && let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind + && bounds.len() > 2 + { + // Build up a hash of every trait we've seen + // When we see a trait for the first time, add it to unique_traits + // so we can later use it to build a string of all traits exactly once, without duplicates - let mut seen_def_ids = FxHashSet::default(); - let mut unique_traits = Vec::new(); + let mut seen_def_ids = FxHashSet::default(); + let mut unique_traits = Vec::new(); - // Iterate the bounds and add them to our seen hash - // If we haven't yet seen it, add it to the fixed traits - for bound in bounds { - let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; + // Iterate the bounds and add them to our seen hash + // If we haven't yet seen it, add it to the fixed traits + for bound in bounds { + let Some(def_id) = bound.trait_ref.trait_def_id() else { + continue; + }; - let new_trait = seen_def_ids.insert(def_id); + let new_trait = seen_def_ids.insert(def_id); - if new_trait { - unique_traits.push(bound); - } + if new_trait { + unique_traits.push(bound); } + } - // If the number of unique traits isn't the same as the number of traits in the bounds, - // there must be 1 or more duplicates - if bounds.len() != unique_traits.len() { - let mut bounds_span = bounds[0].span; - - for bound in bounds.iter().skip(1) { - bounds_span = bounds_span.to(bound.span); - } - - let fixed_trait_snippet = unique_traits - .iter() - .filter_map(|b| snippet_opt(cx, b.span)) - .collect::<Vec<_>>() - .join(" + "); + // If the number of unique traits isn't the same as the number of traits in the bounds, + // there must be 1 or more duplicates + if bounds.len() != unique_traits.len() { + let mut bounds_span = bounds[0].span; - span_lint_and_sugg( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - bounds_span, - "this trait bound is already specified in trait declaration", - "try", - fixed_trait_snippet, - Applicability::MaybeIncorrect, - ); + for bound in bounds.iter().skip(1) { + bounds_span = bounds_span.to(bound.span); } + + let fixed_trait_snippet = unique_traits + .iter() + .filter_map(|b| snippet_opt(cx, b.span)) + .collect::<Vec<_>>() + .join(" + "); + + span_lint_and_sugg( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + bounds_span, + "this trait bound is already specified in trait declaration", + "try", + fixed_trait_snippet, + Applicability::MaybeIncorrect, + ); } } } @@ -267,36 +263,38 @@ impl TraitBounds { let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.predicates { - if_chain! { - if let WherePredicate::BoundPredicate(ref p) = bound; - if p.origin != PredicateOrigin::ImplTrait; - if p.bounds.len() as u64 <= self.max_trait_bounds; - if !p.span.from_expansion(); - let bounds = p.bounds.iter().filter(|b| !self.cannot_combine_maybe_bound(cx, b)).collect::<Vec<_>>(); - if !bounds.is_empty(); - if let Some(ref v) = map.insert(SpanlessTy { ty: p.bounded_ty, cx }, bounds); - if !is_from_proc_macro(cx, p.bounded_ty); - then { - let trait_bounds = v - .iter() - .copied() - .chain(p.bounds.iter()) - .filter_map(get_trait_info_from_bound) - .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) - .join(" + "); - let hint_string = format!( - "consider combining the bounds: `{}: {trait_bounds}`", - snippet(cx, p.bounded_ty.span, "_"), - ); - span_lint_and_help( - cx, - TYPE_REPETITION_IN_BOUNDS, - p.span, - "this type has already been used as a bound predicate", - None, - &hint_string, - ); - } + if let WherePredicate::BoundPredicate(ref p) = bound + && p.origin != PredicateOrigin::ImplTrait + && p.bounds.len() as u64 <= self.max_trait_bounds + && !p.span.from_expansion() + && let bounds = p + .bounds + .iter() + .filter(|b| !self.cannot_combine_maybe_bound(cx, b)) + .collect::<Vec<_>>() + && !bounds.is_empty() + && let Some(ref v) = map.insert(SpanlessTy { ty: p.bounded_ty, cx }, bounds) + && !is_from_proc_macro(cx, p.bounded_ty) + { + let trait_bounds = v + .iter() + .copied() + .chain(p.bounds.iter()) + .filter_map(get_trait_info_from_bound) + .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) + .join(" + "); + let hint_string = format!( + "consider combining the bounds: `{}: {trait_bounds}`", + snippet(cx, p.bounded_ty.span, "_"), + ); + span_lint_and_help( + cx, + TYPE_REPETITION_IN_BOUNDS, + p.span, + "this type has already been used as a bound predicate", + None, + &hint_string, + ); } } } @@ -318,15 +316,19 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { .predicates .iter() .filter_map(|pred| { - if_chain! { - if pred.in_where_clause(); - if let WherePredicate::BoundPredicate(bound_predicate) = pred; - if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind; - then { - return Some( - rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements") - .into_iter().map(|(trait_ref, _)| (path.res, trait_ref))) - } + if pred.in_where_clause() + && let WherePredicate::BoundPredicate(bound_predicate) = pred + && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind + { + return Some( + rollup_traits( + cx, + bound_predicate.bounds, + "these where clauses contain repeated elements", + ) + .into_iter() + .map(|(trait_ref, _)| (path.res, trait_ref)), + ); } None }) @@ -340,25 +342,23 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { // compare trait bounds keyed by generic name and comparable trait to collected where // predicates eg. (T, Clone) for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) { - if_chain! { - if let WherePredicate::BoundPredicate(bound_predicate) = predicate; - if bound_predicate.origin != PredicateOrigin::ImplTrait; - if !bound_predicate.span.from_expansion(); - if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind; - then { - let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); - for (trait_ref, span) in traits { - let key = (path.res, trait_ref); - if where_predicates.contains(&key) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in the where clause", - None, - "consider removing this trait bound", - ); - } + if let WherePredicate::BoundPredicate(bound_predicate) = predicate + && bound_predicate.origin != PredicateOrigin::ImplTrait + && !bound_predicate.span.from_expansion() + && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind + { + let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); + for (trait_ref, span) in traits { + let key = (path.res, trait_ref); + if where_predicates.contains(&key) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); } } } @@ -401,10 +401,10 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { .filter_map(|segment| { // get trait bound type arguments Some(segment.args?.args.iter().filter_map(|arg| { - if_chain! { - if let GenericArg::Type(ty) = arg; - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - then { return Some(path.res) } + if let GenericArg::Type(ty) = arg + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + { + return Some(path.res); } None })) @@ -444,27 +444,24 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) - comparable_bounds[i] = (k, v); } - if_chain! { - if repeated_res; - if let [first_trait, .., last_trait] = bounds; - then { - let all_trait_span = first_trait.span().to(last_trait.span()); - - let traits = comparable_bounds.iter() - .filter_map(|&(_, span)| snippet_opt(cx, span)) - .collect::<Vec<_>>(); - let traits = traits.join(" + "); - - span_lint_and_sugg( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - all_trait_span, - msg, - "try", - traits, - Applicability::MachineApplicable - ); - } + if repeated_res && let [first_trait, .., last_trait] = bounds { + let all_trait_span = first_trait.span().to(last_trait.span()); + + let traits = comparable_bounds + .iter() + .filter_map(|&(_, span)| snippet_opt(cx, span)) + .collect::<Vec<_>>(); + let traits = traits.join(" + "); + + span_lint_and_sugg( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + all_trait_span, + msg, + "try", + traits, + Applicability::MachineApplicable, + ); } comparable_bounds diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 6eec40cb529..a3a50acb609 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -19,7 +19,6 @@ mod wrong_transmute; use clippy_config::msrvs::Msrv; use clippy_utils::in_constant; -use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -494,51 +493,47 @@ impl Transmute { } impl<'tcx> LateLintPass<'tcx> for Transmute { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path_expr, [arg]) = e.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind; - if let Some(def_id) = path.res.opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::transmute, def_id); - then { - // Avoid suggesting non-const operations in const contexts: - // - 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); + if let ExprKind::Call(path_expr, [arg]) = e.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind + && let Some(def_id) = path.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::transmute, def_id) + { + // Avoid suggesting non-const operations in const contexts: + // - 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 (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { - [] => (cx.typeck_results().expr_ty(arg), false), - [.., a] => (a.target, true), - }; - // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. - let to_ty = cx.typeck_results().expr_ty(e); + let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { + [] => (cx.typeck_results().expr_ty(arg), false), + [.., a] => (a.target, true), + }; + // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. + let to_ty = cx.typeck_results().expr_ty(e); - // If useless_transmute is triggered, the other lints can be skipped. - if useless_transmute::check(cx, e, from_ty, to_ty, arg) { - return; - } + // If useless_transmute is triggered, the other lints can be skipped. + if useless_transmute::check(cx, e, from_ty, to_ty, arg) { + return; + } - let linted = wrong_transmute::check(cx, e, from_ty, to_ty) - | crosspointer_transmute::check(cx, e, from_ty, to_ty) - | transmuting_null::check(cx, e, arg, to_ty) - | transmute_null_to_fn::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context) - | ( - unsound_collection_transmute::check(cx, e, from_ty, to_ty) - || transmute_undefined_repr::check(cx, e, from_ty, to_ty) - ); + let linted = wrong_transmute::check(cx, e, from_ty, to_ty) + | crosspointer_transmute::check(cx, e, from_ty, to_ty) + | transmuting_null::check(cx, e, arg, to_ty) + | transmute_null_to_fn::check(cx, e, arg, to_ty) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) + | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) + | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context) + | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) + || transmute_undefined_repr::check(cx, e, from_ty, to_ty)); - if !linted { - transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); - } + if !linted { + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); } } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs index 5ecba512b0f..aef520923e4 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -1,7 +1,6 @@ use super::TRANSMUTE_FLOAT_TO_INT; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg; -use if_chain::if_chain; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -32,17 +31,15 @@ pub(super) fn check<'tcx>( arg = inner_expr; } - if_chain! { + if let ExprKind::Lit(lit) = &arg.kind // if the expression is a float literal and it is unsuffixed then // add a suffix so the suggestion is valid and unambiguous - if let ExprKind::Lit(lit) = &arg.kind; - if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; - then { - let op = format!("{sugg}{}", float_ty.name_str()).into(); - match sugg { - sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), - _ => sugg = sugg::Sugg::NonParen(op) - } + && let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node + { + let op = format!("{sugg}{}", float_ty.name_str()).into(); + match sugg { + sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), + _ => sugg = sugg::Sugg::NonParen(op), } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs index ea9ad99618a..98e9ea2d775 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -2,7 +2,6 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -21,68 +20,59 @@ pub(super) fn check<'tcx>( let mut triggered = false; if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (&from_ty.kind(), &to_ty.kind()) { - if_chain! { - if let ty::Slice(slice_ty) = *ty_from.kind(); - if ty_to.is_str(); - if let ty::Uint(ty::UintTy::U8) = slice_ty.kind(); - if from_mutbl == to_mutbl; - then { - let postfix = if *from_mutbl == Mutability::Mut { - "_mut" - } else { - "" - }; + if let ty::Slice(slice_ty) = *ty_from.kind() + && ty_to.is_str() + && let ty::Uint(ty::UintTy::U8) = slice_ty.kind() + && from_mutbl == to_mutbl + { + let postfix = if *from_mutbl == Mutability::Mut { "_mut" } else { "" }; - let snippet = snippet(cx, arg.span, ".."); + let snippet = snippet(cx, arg.span, ".."); - span_lint_and_sugg( - cx, - TRANSMUTE_BYTES_TO_STR, - e.span, - &format!("transmute from a `{from_ty}` to a `{to_ty}`"), - "consider using", - if const_context { - format!("std::str::from_utf8_unchecked{postfix}({snippet})") - } else { - format!("std::str::from_utf8{postfix}({snippet}).unwrap()") - }, - Applicability::MaybeIncorrect, - ); - triggered = true; - } else { - if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) - && !const_context { - span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_PTR, - e.span, - "transmute from a reference to a reference", - |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let ty_from_and_mut = ty::TypeAndMut { - ty: *ty_from, - mutbl: *from_mutbl - }; - let ty_to_and_mut = ty::TypeAndMut { ty: *ty_to, mutbl: *to_mutbl }; - let sugg_paren = arg - .as_ty(Ty::new_ptr(cx.tcx,ty_from_and_mut)) - .as_ty(Ty::new_ptr(cx.tcx,ty_to_and_mut)); - let sugg = if *to_mutbl == Mutability::Mut { - sugg_paren.mut_addr_deref() - } else { - sugg_paren.addr_deref() - }; - diag.span_suggestion( - e.span, - "try", - sugg, - Applicability::Unspecified, - ); - }, - ); + span_lint_and_sugg( + cx, + TRANSMUTE_BYTES_TO_STR, + e.span, + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), + "consider using", + if const_context { + format!("std::str::from_utf8_unchecked{postfix}({snippet})") + } else { + format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + }, + Applicability::MaybeIncorrect, + ); + triggered = true; + } else if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) && !const_context { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a reference to a reference", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { + let ty_from_and_mut = ty::TypeAndMut { + ty: *ty_from, + mutbl: *from_mutbl, + }; + let ty_to_and_mut = ty::TypeAndMut { + ty: *ty_to, + mutbl: *to_mutbl, + }; + let sugg_paren = arg + .as_ty(Ty::new_ptr(cx.tcx, ty_from_and_mut)) + .as_ty(Ty::new_ptr(cx.tcx, ty_to_and_mut)); + let sugg = if *to_mutbl == Mutability::Mut { + sugg_paren.mut_addr_deref() + } else { + sugg_paren.addr_deref() + }; + diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); + } + }, + ); - triggered = true; - } - } + triggered = true; } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 7c2223ca3ab..a65bc0ce458 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -299,14 +299,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - if_chain! { - if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty); - if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); - then { - layout.layout.size().bytes() == 0 - } else { - false - } + if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) + && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) + { + layout.layout.size().bytes() == 0 + } else { + false } } 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 306ca5724da..801e8862619 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind, @@ -15,66 +14,64 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m TyKind::Path(ref qpath) => { let hir_id = mut_ty.ty.hir_id; let def = cx.qpath_res(qpath, hir_id); - if_chain! { - if let Some(def_id) = def.opt_def_id(); - if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let QPath::Resolved(None, path) = *qpath; - if let [ref bx] = *path.segments; - if let Some(params) = bx.args; - if params.parenthesized == hir::GenericArgsParentheses::No; - if let Some(inner) = params.args.iter().find_map(|arg| match arg { + if let Some(def_id) = def.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() + && let QPath::Resolved(None, path) = *qpath + && let [ref bx] = *path.segments + && let Some(params) = bx.args + && params.parenthesized == hir::GenericArgsParentheses::No + && let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, - }); - then { - if is_any_trait(cx, inner) { - // Ignore `Box<Any>` types; see issue #1884 for details. - return false; - } - - let ltopt = if lt.is_anonymous() { - String::new() - } else { - format!("{} ", lt.ident.as_str()) - }; + }) + { + if is_any_trait(cx, inner) { + // Ignore `Box<Any>` types; see issue #1884 for details. + return false; + } - if mut_ty.mutbl == Mutability::Mut { - // Ignore `&mut Box<T>` types; see issue #2907 for - // details. - return false; - } + let ltopt = if lt.is_anonymous() { + String::new() + } else { + format!("{} ", lt.ident.as_str()) + }; - // When trait objects or opaque types have lifetime or auto-trait bounds, - // we need to add parentheses to avoid a syntax error due to its ambiguity. - // Originally reported as the issue #3128. - 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) - }, - 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), - }; - span_lint_and_sugg( - cx, - BORROWED_BOX, - hir_ty.span, - "you seem to be trying to use `&Box<T>`. Consider using just `&T`", - "try", - suggestion, - // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item - // because the trait impls of it will break otherwise; - // and there may be other cases that result in invalid code. - // For example, type coercion doesn't work nicely. - Applicability::Unspecified, - ); - return true; + if mut_ty.mutbl == Mutability::Mut { + // Ignore `&mut Box<T>` types; see issue #2907 for + // details. + return false; } + + // When trait objects or opaque types have lifetime or auto-trait bounds, + // we need to add parentheses to avoid a syntax error due to its ambiguity. + // Originally reported as the issue #3128. + 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) + }, + 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), + }; + span_lint_and_sugg( + cx, + BORROWED_BOX, + hir_ty.span, + "you seem to be trying to use `&Box<T>`. Consider using just `&T`", + "try", + suggestion, + // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item + // because the trait impls of it will break otherwise; + // and there may be other cases that result in invalid code. + // For example, type coercion doesn't work nicely. + Applicability::Unspecified, + ); + return true; }; false }, @@ -84,33 +81,29 @@ 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_chain! { - if let TyKind::TraitObject(traits, ..) = t.kind; - if !traits.is_empty(); - if let Some(trait_did) = traits[0].trait_ref.trait_def_id(); + if let TyKind::TraitObject(traits, ..) = t.kind + && !traits.is_empty() + && let Some(trait_did) = traits[0].trait_ref.trait_def_id() // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if cx.tcx.is_diagnostic_item(sym::Any, trait_did); - then { - return true; - } + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; } false } fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> { - if_chain! { - if let Some(did) = cx.qpath_res(qpath, id).opt_def_id(); - if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); - if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; - if synthetic; - if let Some(generics) = cx.tcx.hir().get_generics(id.owner.def_id); - if let Some(pred) = generics.bounds_for_param(did.expect_local()).next(); - then { - Some(pred.bounds) - } else { - None - } + if let Some(did) = cx.qpath_res(qpath, id).opt_def_id() + && let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did) + && let GenericParamKind::Type { synthetic, .. } = generic_param.kind + && synthetic + && let Some(generics) = cx.tcx.hir().get_generics(id.owner.def_id) + && let Some(pred) = generics.bounds_for_param(did.expect_local()).next() + { + Some(pred.bounds) + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs index 4a5a94f2630..fc3420af020 100644 --- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs +++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs @@ -8,30 +8,26 @@ use rustc_span::{sym, Symbol}; use super::BOX_COLLECTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if_chain! { - if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let Some(item_type) = get_std_collection(cx, qpath); - then { - let generic = match item_type { - sym::String => "", - _ => "<..>", - }; + if Some(def_id) == cx.tcx.lang_items().owned_box() + && let Some(item_type) = get_std_collection(cx, qpath) + { + let generic = match item_type { + sym::String => "", + _ => "<..>", + }; - let box_content = format!("{item_type}{generic}"); - span_lint_and_help( - cx, - BOX_COLLECTION, - hir_ty.span, - &format!( - "you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"), - None, - &format!( - "`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation") - ); - true - } else { - false - } + let box_content = format!("{item_type}{generic}"); + span_lint_and_help( + cx, + BOX_COLLECTION, + hir_ty.span, + &format!("you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"), + None, + &format!("`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation"), + ); + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 6a6160c4983..f333b2cdcb4 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -490,7 +490,7 @@ impl Types { // All lints that are being checked in this block are guarded by // the `avoid_breaking_exported_api` configuration. When adding a // new lint, please also add the name to the configuration documentation - // in `clippy_lints::utils::conf.rs` + // in `clippy_config::conf` let mut triggered = false; triggered |= box_collection::check(cx, hir_ty, qpath, def_id); diff --git a/src/tools/clippy/clippy_lints/src/types/option_option.rs b/src/tools/clippy/clippy_lints/src/types/option_option.rs index 60622903af1..d12d14f2b14 100644 --- a/src/tools/clippy/clippy_lints/src/types/option_option.rs +++ b/src/tools/clippy/clippy_lints/src/types/option_option.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::{path_def_id, qpath_generic_tys}; -use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -9,21 +8,19 @@ use rustc_span::symbol::sym; use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if_chain! { - if cx.tcx.is_diagnostic_item(sym::Option, def_id); - if let Some(arg) = qpath_generic_tys(qpath).next(); - if path_def_id(cx, arg) == Some(def_id); - then { - span_lint( - cx, - OPTION_OPTION, - hir_ty.span, - "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \ - enum if you need to distinguish all 3 cases", - ); - true - } else { - false - } + if cx.tcx.is_diagnostic_item(sym::Option, def_id) + && let Some(arg) = qpath_generic_tys(qpath).next() + && path_def_id(cx, arg) == Some(def_id) + { + span_lint( + cx, + OPTION_OPTION, + hir_ty.span, + "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \ + enum if you need to distinguish all 3 cases", + ); + true + } else { + false } } 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 a616c3e4ea8..afc31921704 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{path_def_id, qpath_generic_tys}; -use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -9,22 +8,20 @@ use rustc_span::symbol::sym; use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if_chain! { - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; - if let Some(arg) = qpath_generic_tys(qpath).next(); - if let Some(id) = path_def_id(cx, arg); - if cx.tcx.is_diagnostic_item(sym::Mutex, id); - then { - span_lint_and_help( - cx, - RC_MUTEX, - hir_ty.span, - "usage of `Rc<Mutex<_>>`", - None, - "consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead", - ); - return true; - } + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) + && let Some(arg) = qpath_generic_tys(qpath).next() + && 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", + ); + return true; } false diff --git a/src/tools/clippy/clippy_lints/src/types/utils.rs b/src/tools/clippy/clippy_lints/src/types/utils.rs index 39469841bd4..0bca56b8da7 100644 --- a/src/tools/clippy/clippy_lints/src/types/utils.rs +++ b/src/tools/clippy/clippy_lints/src/types/utils.rs @@ -1,22 +1,19 @@ use clippy_utils::last_path_segment; -use if_chain::if_chain; use rustc_hir::{GenericArg, GenericArgsParentheses, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::Span; pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> { let last = last_path_segment(qpath); - if_chain! { - if let Some(params) = last.args; - if params.parenthesized == GenericArgsParentheses::No; - if let Some(ty) = params.args.iter().find_map(|arg| match arg { + if let Some(params) = last.args + && params.parenthesized == GenericArgsParentheses::No + && let Some(ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, - }); - if let TyKind::Ref(..) = ty.kind; - then { - return Some(ty.span); - } + }) + && let TyKind::Ref(..) = ty.kind + { + return Some(ty.span); } None } diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs index decc183ad96..9d5066199bd 100644 --- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::last_path_segment; +use clippy_utils::paths::ALLOCATOR_GLOBAL; use clippy_utils::source::snippet; -use if_chain::if_chain; +use clippy_utils::{last_path_segment, match_def_path}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg, QPath, TyKind}; @@ -21,43 +21,57 @@ pub(super) fn check( box_size_threshold: u64, ) -> bool { if cx.tcx.is_diagnostic_item(sym::Vec, def_id) { - if_chain! { + if let Some(last) = last_path_segment(qpath).args // Get the _ part of Vec<_> - if let Some(last) = last_path_segment(qpath).args; - if let Some(ty) = last.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); + && let Some(GenericArg::Type(ty)) = last.args.first() + // extract allocator from the Vec for later + && let vec_alloc_ty = last.args.get(1) // ty is now _ at this point - if let TyKind::Path(ref ty_qpath) = ty.kind; - let res = cx.qpath_res(ty_qpath, ty.hir_id); - if let Some(def_id) = res.opt_def_id(); - if Some(def_id) == cx.tcx.lang_items().owned_box(); + && let TyKind::Path(ref ty_qpath) = ty.kind + && let res = cx.qpath_res(ty_qpath, ty.hir_id) + && let Some(def_id) = res.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() // At this point, we know ty is Box<T>, now get T - if let Some(last) = last_path_segment(ty_qpath).args; - if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); - if !ty_ty.has_escaping_bound_vars(); - if ty_ty.is_sized(cx.tcx, cx.param_env); - if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size < box_size_threshold; - then { - span_lint_and_sugg( - cx, - VEC_BOX, - hir_ty.span, - "`Vec<T>` is already on the heap, the boxing is unnecessary", - "try", - format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), - Applicability::MachineApplicable, - ); - true - } else { - false + && let Some(last) = last_path_segment(ty_qpath).args + && let Some(GenericArg::Type(boxed_ty)) = last.args.first() + // extract allocator from the Box for later + && let boxed_alloc_ty = last.args.get(1) + && let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty) + && !ty_ty.has_escaping_bound_vars() + && ty_ty.is_sized(cx.tcx, cx.param_env) + && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) + && ty_ty_size < box_size_threshold + // https://github.com/rust-lang/rust-clippy/issues/7114 + && match (vec_alloc_ty, boxed_alloc_ty) { + (None, None) => true, + // this is in the event that we have something like + // Vec<_, Global>, in which case is equivalent to + // Vec<_> + (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { + if let TyKind::Path(path) = inner.kind + && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { + match_def_path(cx, did, &ALLOCATOR_GLOBAL) + } else { + false + } + }, + (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => + hir_ty_to_ty(cx.tcx, l) == hir_ty_to_ty(cx.tcx, r), + _ => false } + { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec<T>` is already on the heap, the boxing is unnecessary", + "try", + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), + Applicability::Unspecified, + ); + true + } else { + false } } else { false diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 72569e10f05..a7119434517 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -83,41 +83,39 @@ fn handle_uninit_vec_pair<'tcx>( maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, ) { - if_chain! { - if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve); - if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); - if vec.location.eq_expr(cx, set_len_self); - if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); - if let ty::Adt(_, args) = vec_ty.kind(); + if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve) + && let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len) + && vec.location.eq_expr(cx, set_len_self) + && let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind() + && let ty::Adt(_, args) = vec_ty.kind() // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` - if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id); - then { - if vec.has_capacity() { - // with_capacity / reserve -> set_len + && !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id) + { + if vec.has_capacity() { + // with_capacity / reserve -> set_len - // Check T of Vec<T> - if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { - // FIXME: #7698, false positive of the internal lints - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then( - cx, - UNINIT_VEC, - vec![call_span, maybe_init_or_reserve.span], - "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, - ); - } - } else { - // new / default -> set_len - span_lint( + // Check T of Vec<T> + if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { + // FIXME: #7698, false positive of the internal lints + #[expect(clippy::collapsible_span_lint_calls)] + span_lint_and_then( cx, UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], - "calling `set_len()` on empty `Vec` creates out-of-bound values", + "calling `set_len()` immediately after reserving a buffer creates uninitialized values", + |diag| { + diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); + }, ); } + } else { + // new / default -> set_len + span_lint( + cx, + UNINIT_VEC, + vec![call_span, maybe_init_or_reserve.span], + "calling `set_len()` on empty `Vec` creates out-of-bound values", + ); } } } @@ -156,16 +154,14 @@ impl<'tcx> VecLocation<'tcx> { fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<TargetVec<'tcx>> { match stmt.kind { StmtKind::Local(local) => { - if_chain! { - if let Some(init_expr) = local.init; - if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; - if let Some(init_kind) = get_vec_init_kind(cx, init_expr); - then { - return Some(TargetVec { - location: VecLocation::Local(hir_id), - init_kind: Some(init_kind), - }) - } + if let Some(init_expr) = local.init + && let PatKind::Binding(_, hir_id, _, None) = local.pat.kind + && let Some(init_kind) = get_vec_init_kind(cx, init_expr) + { + return Some(TargetVec { + location: VecLocation::Local(hir_id), + init_kind: Some(init_kind), + }); } }, StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index e76cc65fd46..bfd30cec3dd 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -44,14 +43,12 @@ fn get_trait_predicates_for_trait_id<'tcx>( ) -> Vec<TraitPredicate<'tcx>> { let mut preds = Vec::new(); for (pred, _) in generics.predicates { - if_chain! { - if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder(); - let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)); - if let Some(trait_def_id) = trait_id; - if trait_def_id == trait_pred.trait_ref.def_id; - then { - preds.push(trait_pred); - } + if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder() + && let trait_pred = cx.tcx.instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred)) + && let Some(trait_def_id) = trait_id + && trait_def_id == trait_pred.trait_ref.def_id + { + preds.push(trait_pred); } } preds @@ -64,7 +61,7 @@ fn get_projection_pred<'tcx>( ) -> Option<ProjectionPredicate<'tcx>> { generics.predicates.iter().find_map(|(proj_pred, _)| { if let ClauseKind::Projection(pred) = proj_pred.kind().skip_binder() { - let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred)); + let projection_pred = cx.tcx.instantiate_bound_regions_with_erased(proj_pred.kind().rebind(pred)); if projection_pred.projection_ty.args == trait_pred.trait_ref.args { return Some(projection_pred); } @@ -82,10 +79,10 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); let partial_ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); - // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error + // Trying to call instantiate_bound_regions_with_erased on fn_sig.inputs() gives the following error // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for // `&[rustc_middle::ty::Ty<'_>]` - let inputs_output = cx.tcx.erase_late_bound_regions(fn_sig.inputs_and_output()); + let inputs_output = cx.tcx.instantiate_bound_regions_with_erased(fn_sig.inputs_and_output()); inputs_output .iter() .rev() @@ -94,21 +91,19 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve .enumerate() .for_each(|(i, inp)| { for trait_pred in &fn_mut_preds { - if_chain! { - if trait_pred.self_ty() == inp; - if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); - then { - if ord_preds - .iter() - .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) - { - args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds - .iter() - .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) - { - args_to_check.push((i, "PartialOrd".to_string())); - } + if trait_pred.self_ty() == inp + && let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred) + { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { + args_to_check.push((i, "Ord".to_string())); + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { + args_to_check.push((i, "PartialOrd".to_string())); } } } @@ -118,30 +113,26 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve } fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option<Span>)> { - if_chain! { - if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind; - if let ty::Closure(_def_id, args) = &cx.typeck_results().node_type(arg.hir_id).kind(); - let ret_ty = args.as_closure().sig().output(); - let ty = cx.tcx.erase_late_bound_regions(ret_ty); - if ty.is_unit(); - then { - let body = cx.tcx.hir().body(body); - if_chain! { - if let ExprKind::Block(block, _) = body.value.kind; - if block.expr.is_none(); - if let Some(stmt) = block.stmts.last(); - if let StmtKind::Semi(_) = stmt.kind; - then { - let data = stmt.span.data(); - // Make a span out of the semicolon for the help message - Some((fn_decl_span, Some(data.with_lo(data.hi-BytePos(1))))) - } else { - Some((fn_decl_span, None)) - } - } + if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind + && let ty::Closure(_def_id, args) = &cx.typeck_results().node_type(arg.hir_id).kind() + && let ret_ty = args.as_closure().sig().output() + && let ty = cx.tcx.instantiate_bound_regions_with_erased(ret_ty) + && ty.is_unit() + { + let body = cx.tcx.hir().body(body); + if let ExprKind::Block(block, _) = body.value.kind + && block.expr.is_none() + && let Some(stmt) = block.stmts.last() + && let StmtKind::Semi(_) = stmt.kind + { + let data = stmt.span.data(); + // Make a span out of the semicolon for the help message + Some((fn_decl_span, Some(data.with_lo(data.hi - BytePos(1))))) } else { - None + Some((fn_decl_span, None)) } + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 462b1aa8153..44cff78a793 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; @@ -22,12 +21,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { } let map = &cx.tcx.hir(); let opt_parent_node = map.find_parent(expr.hir_id); - if_chain! { - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; - if is_questionmark_desugar_marked_call(parent_expr); - then { - return; - } + if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node + && is_questionmark_desugar_marked_call(parent_expr) + { + return; } let args: Vec<_> = match expr.kind { @@ -80,21 +77,15 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp args_to_recover .iter() .filter_map(|arg| { - if_chain! { - if let ExprKind::Block(block, _) = arg.kind; - if block.expr.is_none(); - if let Some(last_stmt) = block.stmts.iter().last(); - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let Some(snip) = snippet_opt(cx, last_expr.span); - then { - Some(( - last_stmt.span, - snip, - )) - } - else { - None - } + if let ExprKind::Block(block, _) = arg.kind + && block.expr.is_none() + && let Some(last_stmt) = block.stmts.iter().last() + && let StmtKind::Semi(last_expr) = last_stmt.kind + && let Some(snip) = snippet_opt(cx, last_expr.span) + { + Some((last_stmt.span, snip)) + } else { + None } }) .for_each(|(span, sugg)| { diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs index e7355f92304..2223cbcb060 100644 --- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs +++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -76,55 +75,50 @@ impl LateLintPass<'_> for UnnamedAddress { matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..)) } - if_chain! { - if let ExprKind::Binary(binop, left, right) = expr.kind; - if is_comparison(binop.node); - if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); - then { - span_lint_and_help( - cx, - VTABLE_ADDRESS_COMPARISONS, - expr.span, - "comparing trait object pointers compares a non-unique vtable address", - None, - "consider extracting and comparing data pointers only", - ); - } + if let ExprKind::Binary(binop, left, right) = expr.kind + && is_comparison(binop.node) + && is_trait_ptr(cx, left) + && is_trait_ptr(cx, right) + { + span_lint_and_help( + cx, + VTABLE_ADDRESS_COMPARISONS, + expr.span, + "comparing trait object pointers compares a non-unique vtable address", + None, + "consider extracting and comparing data pointers only", + ); } - if_chain! { - if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind; - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if cx.tcx.is_diagnostic_item(sym::ptr_eq, def_id); - let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0); - if ty_param.is_trait(); - then { - span_lint_and_help( - cx, - VTABLE_ADDRESS_COMPARISONS, - expr.span, - "comparing trait object pointers compares a non-unique vtable address", - None, - "consider extracting and comparing data pointers only", - ); - } + if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind + && let ExprKind::Path(ref func_qpath) = func.kind + && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::ptr_eq, def_id) + && let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0) + && ty_param.is_trait() + { + span_lint_and_help( + cx, + VTABLE_ADDRESS_COMPARISONS, + expr.span, + "comparing trait object pointers compares a non-unique vtable address", + None, + "consider extracting and comparing data pointers only", + ); } - if_chain! { - if let ExprKind::Binary(binop, left, right) = expr.kind; - if is_comparison(binop.node); - if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr(); - if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); - if is_fn_def(cx, left) || is_fn_def(cx, right); - then { - span_lint( - cx, - FN_ADDRESS_COMPARISONS, - expr.span, - "comparing with a non-unique address of a function item", - ); - } + if let ExprKind::Binary(binop, left, right) = expr.kind + && is_comparison(binop.node) + && cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() + && cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr() + && (is_fn_def(cx, left) || is_fn_def(cx, right)) + { + span_lint( + cx, + FN_ADDRESS_COMPARISONS, + expr.span, + "comparing with a non-unique address of a function item", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index ca159eb4d5f..9bd7167db25 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -71,7 +71,7 @@ impl UnnecessaryBoxReturns { let return_ty = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(def_id).skip_binder()) + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder()) .output(); if !return_ty.is_box() { diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index 9107b2b99b8..06c017ea15a 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ```no_run /// Some(i32::swap_bytes(4)); /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.74.0"] pub UNNECESSARY_MAP_ON_CONSTRUCTOR, complexity, "using `map`/`map_err` on `Option` or `Result` constructors" diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs index 28ea02e4d9a..14694bb3a28 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -36,46 +35,40 @@ declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRI impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; - if let ExprKind::Call(fun, args) = inner_expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); - if inner_str.is_str(); - then { - if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { - span_lint_and_sugg( - cx, - UNNECESSARY_OWNED_EMPTY_STRINGS, - expr.span, - "usage of `&String::new()` for a function expecting a `&str` argument", - "try", - "\"\"".to_owned(), - Applicability::MachineApplicable, - ); - } else { - if_chain! { - if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id); - if let [.., last_arg] = args; - if let ExprKind::Lit(spanned) = &last_arg.kind; - if let LitKind::Str(symbol, _) = spanned.node; - if symbol.is_empty(); - let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); - if is_type_lang_item(cx, inner_expr_type, LangItem::String); - then { - span_lint_and_sugg( - cx, - UNNECESSARY_OWNED_EMPTY_STRINGS, - expr.span, - "usage of `&String::from(\"\")` for a function expecting a `&str` argument", - "try", - "\"\"".to_owned(), - Applicability::MachineApplicable, - ); - } - } - } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind + && let ExprKind::Call(fun, args) = inner_expr.kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind() + && inner_str.is_str() + { + if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::new()` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id) + && let [.., last_arg] = args + && let ExprKind::Lit(spanned) = &last_arg.kind + && let LitKind::Str(symbol, _) = spanned.node + && symbol.is_empty() + && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) + && is_type_lang_item(cx, inner_expr_type, LangItem::String) + { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::from(\"\")` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs index a1083a0a68e..1e2b20469ef 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use if_chain::if_chain; use rustc_ast::{Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -36,35 +35,36 @@ declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]); impl EarlyLintPass for UnnecessarySelfImports { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if let ItemKind::Use(use_tree) = &item.kind; - if let UseTreeKind::Nested(nodes) = &use_tree.kind; - if let [(self_tree, _)] = &**nodes; - if let [self_seg] = &*self_tree.prefix.segments; - if self_seg.ident.name == kw::SelfLower; - if let Some(last_segment) = use_tree.prefix.segments.last(); - - then { - span_lint_and_then( - cx, - UNNECESSARY_SELF_IMPORTS, - item.span, - "import ending with `::{self}`", - |diag| { - diag.span_suggestion( - last_segment.span().with_hi(item.span.hi()), - "consider omitting `::{self}`", - format!( - "{}{};", - last_segment.ident, - if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { format!(" as {alias}") } else { String::new() }, - ), - Applicability::MaybeIncorrect, - ); - diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); - }, - ); - } + if let ItemKind::Use(use_tree) = &item.kind + && let UseTreeKind::Nested(nodes) = &use_tree.kind + && let [(self_tree, _)] = &**nodes + && let [self_seg] = &*self_tree.prefix.segments + && self_seg.ident.name == kw::SelfLower + && let Some(last_segment) = use_tree.prefix.segments.last() + { + span_lint_and_then( + cx, + UNNECESSARY_SELF_IMPORTS, + item.span, + "import ending with `::{self}`", + |diag| { + diag.span_suggestion( + last_segment.span().with_hi(item.span.hi()), + "consider omitting `::{self}`", + format!( + "{}{};", + last_segment.ident, + if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { + format!(" as {alias}") + } else { + String::new() + }, + ), + Applicability::MaybeIncorrect, + ); + diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index ab8de17b091..5599a9dc4e8 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::{OptionSome, ResultOk}; @@ -119,28 +118,24 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Check if all return expression respect the following condition and collect them. let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, body.value, |ret_expr| { - if_chain! { - if !ret_expr.span.from_expansion(); + if !ret_expr.span.from_expansion() // Check if a function call. - if let ExprKind::Call(func, [arg]) = ret_expr.kind; - if is_res_lang_ctor(cx, path_res(cx, func), lang_item); + && let ExprKind::Call(func, [arg]) = ret_expr.kind + && is_res_lang_ctor(cx, path_res(cx, func), lang_item) // Make sure the function argument does not contain a return expression. - if !contains_return(arg); - then { - suggs.push( - ( - ret_expr.span, - if inner_type.is_unit() { - String::new() - } else { - snippet(cx, arg.span.source_callsite(), "..").to_string() - } - ) - ); - true - } else { - false - } + && !contains_return(arg) + { + suggs.push(( + ret_expr.span, + if inner_type.is_unit() { + String::new() + } else { + snippet(cx, arg.span.source_callsite(), "..").to_string() + }, + )); + true + } else { + false } }); diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs index c43d5dc94b3..a7b2d2148e9 100644 --- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index f864c520302..532207310bc 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; -use if_chain::if_chain; use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -73,25 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { }) .is_some() }; - if_chain! { - if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; - if assoc_item.fn_has_self_parameter; - if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; - if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api; - let body = cx.tcx.hir().body(*body_id); - if let [self_param, ..] = body.params; - if !is_local_used(cx, body, self_param.pat.hir_id); - if !contains_todo(cx, body); - then { - span_lint_and_help( - cx, - UNUSED_SELF, - self_param.span, - "unused `self` argument", - None, - "consider refactoring to an associated function", - ); - } + if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind + && assoc_item.fn_has_self_parameter + && let ImplItemKind::Fn(.., body_id) = &impl_item.kind + && (!cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api) + && let body = cx.tcx.hir().body(*body_id) + && let [self_param, ..] = body.params + && !is_local_used(cx, body, self_param.pat.hir_id) + && !contains_todo(cx, body) + { + span_lint_and_help( + cx, + UNUSED_SELF, + self_param.span, + "unused `self` argument", + None, + "consider refactoring to an associated function", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index adbf8281388..9627f4c7454 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; -use if_chain::if_chain; use rustc_ast::visit::FnKind; use rustc_ast::{ast, ClosureBinder}; use rustc_errors::Applicability; @@ -37,40 +36,39 @@ declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); impl EarlyLintPass for UnusedUnit { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { - return; - } - - lint_unneeded_unit_return(cx, ty, span); + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output + && let ast::TyKind::Tup(ref vals) = ty.kind + && vals.is_empty() + && !ty.span.from_expansion() + && get_def(span) == get_def(ty.span) + { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; } + + lint_unneeded_unit_return(cx, ty, span); } } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if_chain! { - if let Some(stmt) = block.stmts.last(); - if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr); - let ctxt = block.span.ctxt(); - if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt; - then { - let sp = expr.span; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - sp, - "unneeded unit expression", - "remove the final `()`", - String::new(), - Applicability::MachineApplicable, - ); - } + if let Some(stmt) = block.stmts.last() + && let ast::StmtKind::Expr(ref expr) = stmt.kind + && is_unit_expr(expr) + && let ctxt = block.span.ctxt() + && stmt.span.ctxt() == ctxt + && expr.span.ctxt() == ctxt + { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); } } @@ -96,16 +94,14 @@ impl EarlyLintPass for UnusedUnit { fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef) { let segments = &poly.trait_ref.path.segments; - if_chain! { - if segments.len() == 1; - if ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()); - if let Some(args) = &segments[0].args; - if let ast::GenericArgs::Parenthesized(generic_args) = &**args; - if let ast::FnRetTy::Ty(ty) = &generic_args.output; - if ty.kind.is_unit(); - then { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } + if segments.len() == 1 + && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) + && let Some(args) = &segments[0].args + && let ast::GenericArgs::Parenthesized(generic_args) = &**args + && let ast::FnRetTy::Ty(ty) = &generic_args.output + && ty.kind.is_unit() + { + lint_unneeded_unit_return(cx, ty, generic_args.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index cdfcb8500c4..6e1d0e09fe2 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; @@ -155,41 +154,35 @@ fn collect_unwrap_info<'tcx>( } } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind { return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false); - } else { - if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind; - if let Some(local_id) = path_to_local(receiver); - let ty = cx.typeck_results().expr_ty(receiver); - let name = method_name.ident.as_str(); - if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name); - then { - assert!(args.is_empty()); - let unwrappable = match name { - "is_some" | "is_ok" => true, - "is_err" | "is_none" => false, - _ => unreachable!(), - }; - let safe_to_unwrap = unwrappable != invert; - let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { - UnwrappableKind::Option - } else { - UnwrappableKind::Result - }; + } else if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind + && let Some(local_id) = path_to_local(receiver) + && let ty = cx.typeck_results().expr_ty(receiver) + && let name = method_name.ident.as_str() + && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) + { + assert!(args.is_empty()); + let unwrappable = match name { + "is_some" | "is_ok" => true, + "is_err" | "is_none" => false, + _ => unreachable!(), + }; + let safe_to_unwrap = unwrappable != invert; + let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { + UnwrappableKind::Option + } else { + UnwrappableKind::Result + }; - return vec![ - UnwrapInfo { - local_id, - if_expr, - check: expr, - check_name: method_name, - branch, - safe_to_unwrap, - kind, - is_entire_condition, - } - ] - } - } + return vec![UnwrapInfo { + local_id, + if_expr, + check: expr, + check_name: method_name, + branch, + safe_to_unwrap, + kind, + is_entire_condition, + }]; } Vec::new() } @@ -319,73 +312,72 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } } else { // find `unwrap[_err]()` calls: - if_chain! { - if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; - let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg); - if let Some(id) = path_to_local(self_arg); - if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); - let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); - if let Some(unwrappable) = self.unwrappables.iter() - .find(|u| u.local_id == id); + if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind + && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) + && let Some(id) = path_to_local(self_arg) + && [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name) + && let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name) + && let Some(unwrappable) = self.unwrappables.iter() + .find(|u| u.local_id == id) // Span contexts should not differ with the conditional branch - let span_ctxt = expr.span.ctxt(); - if unwrappable.branch.span.ctxt() == span_ctxt; - if unwrappable.check.span.ctxt() == span_ctxt; - then { - if call_to_unwrap == unwrappable.safe_to_unwrap { - let is_entire_condition = unwrappable.is_entire_condition; - let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); - let suggested_pattern = if call_to_unwrap { - unwrappable.kind.success_variant_pattern() - } else { - unwrappable.kind.error_variant_pattern() - }; - - span_lint_hir_and_then( - self.cx, - UNNECESSARY_UNWRAP, - expr.hir_id, - expr.span, - &format!( - "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", - method_name.ident.name, - unwrappable.check_name.ident.as_str(), - ), - |diag| { - if is_entire_condition { - diag.span_suggestion( - unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), - "try", - format!( - "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", - borrow_prefix = match as_ref_kind { - Some(AsRefKind::AsRef) => "&", - Some(AsRefKind::AsMut) => "&mut ", - None => "", - }, - ), - // We don't track how the unwrapped value is used inside the - // block or suggest deleting the unwrap, so we can't offer a - // fixable solution. - Applicability::Unspecified, - ); - } else { - diag.span_label(unwrappable.check.span, "the check is happening here"); - diag.help("try using `if let` or `match`"); - } - }, - ); + && let span_ctxt = expr.span.ctxt() + && unwrappable.branch.span.ctxt() == span_ctxt + && unwrappable.check.span.ctxt() == span_ctxt + { + if call_to_unwrap == unwrappable.safe_to_unwrap { + let is_entire_condition = unwrappable.is_entire_condition; + let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); + let suggested_pattern = if call_to_unwrap { + unwrappable.kind.success_variant_pattern() } else { - span_lint_hir_and_then( - self.cx, - PANICKING_UNWRAP, - expr.hir_id, - expr.span, - &format!("this call to `{}()` will always panic", - method_name.ident.name), - |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, - ); - } + unwrappable.kind.error_variant_pattern() + }; + + span_lint_hir_and_then( + self.cx, + UNNECESSARY_UNWRAP, + expr.hir_id, + expr.span, + &format!( + "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", + method_name.ident.name, + unwrappable.check_name.ident.as_str(), + ), + |diag| { + if is_entire_condition { + diag.span_suggestion( + unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), + "try", + format!( + "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", + borrow_prefix = match as_ref_kind { + Some(AsRefKind::AsRef) => "&", + Some(AsRefKind::AsMut) => "&mut ", + None => "", + }, + ), + // We don't track how the unwrapped value is used inside the + // block or suggest deleting the unwrap, so we can't offer a + // fixable solution. + Applicability::Unspecified, + ); + } else { + diag.span_label(unwrappable.check.span, "the check is happening here"); + diag.help("try using `if let` or `match`"); + } + }, + ); + } else { + span_lint_hir_and_then( + self.cx, + PANICKING_UNWRAP, + expr.hir_id, + expr.span, + &format!("this call to `{}()` will always panic", method_name.ident.name), + |diag| { + diag.span_label(unwrappable.check.span, "because of this check"); + }, + ); } } walk_expr(self, expr); diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index 21592abbf16..df4b42133f8 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -3,7 +3,6 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; use clippy_utils::{method_chain_args, return_ty}; use core::ops::ControlFlow; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; @@ -60,15 +59,13 @@ declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if_chain! { + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind // first check if it's a method or function - if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option); - then { - lint_impl_body(cx, impl_item.span, impl_item); - } + && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option)) + { + lint_impl_body(cx, impl_item.span, impl_item); } } } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index c3fe16ad5c3..ac1d9acc70b 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -2,7 +2,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::same_type_and_consts; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -93,32 +92,40 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // relevant for linting, since this is the self type of the `impl` we're currently in. To // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // we're in an `impl` or nested item, that we don't want to lint - let stack_item = if_chain! { - if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind; - if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; - let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; - if parameters.as_ref().map_or(true, |params| { - params.parenthesized == GenericArgsParentheses::No + let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind + && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind + && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args + && parameters.as_ref().map_or(true, |params| { + params.parenthesized == GenericArgsParentheses::No && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) - }); - if !item.span.from_expansion(); - if !is_from_proc_macro(cx, item); // expensive, should be last check - then { - // Self cannot be used inside const generic parameters - let types_to_skip = generics.params.iter().filter_map(|param| { - match param { - GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id), - _ => None, - } - }).chain(std::iter::once(self_ty.hir_id)).collect(); - StackItem::Check { - impl_id: item.owner_id.def_id, - in_body: 0, - types_to_skip, - } - } else { - StackItem::NoCheck + }) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + // expensive, should be last check + { + // Self cannot be used inside const generic parameters + let types_to_skip = generics + .params + .iter() + .filter_map(|param| match param { + GenericParam { + kind: + GenericParamKind::Const { + ty: Ty { hir_id, .. }, .. + }, + .. + } => Some(*hir_id), + _ => None, + }) + .chain(std::iter::once(self_ty.hir_id)) + .collect(); + StackItem::Check { + impl_id: item.owner_id.def_id, + in_body: 0, + types_to_skip, } + } else { + StackItem::NoCheck }; self.stack.push(stack_item); } @@ -132,56 +139,54 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait // declaration. The collection of those types is all this method implementation does. - if_chain! { - if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind; - if let Some(&mut StackItem::Check { + if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind + && let Some(&mut StackItem::Check { impl_id, ref mut types_to_skip, .. - }) = self.stack.last_mut(); - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id); - then { - // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be - // `Self`. - let self_ty = impl_trait_ref.instantiate_identity().self_ty(); + }) = self.stack.last_mut() + && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id) + { + // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be + // `Self`. + let self_ty = impl_trait_ref.instantiate_identity().self_ty(); - // `trait_method_sig` is the signature of the function, how it is declared in the - // trait, not in the impl of the trait. - let trait_method = cx - .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id - .expect("impl method matches a trait method"); - let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); - let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); + // `trait_method_sig` is the signature of the function, how it is declared in the + // trait, not in the impl of the trait. + let trait_method = cx + .tcx + .associated_item(impl_item.owner_id) + .trait_item_def_id + .expect("impl method matches a trait method"); + let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); + let trait_method_sig = cx.tcx.instantiate_bound_regions_with_erased(trait_method_sig); - // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the - // implementation of the trait. - let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output { - Some(&**ty) - } else { - None - }; - let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty); + // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the + // implementation of the trait. + let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output { + Some(&**ty) + } else { + None + }; + let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty); - // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. - // - // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the - // trait declaration. This is used to check if `Self` was used in the trait - // declaration. - // - // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed - // to `Self`), we want to skip linting that type and all subtypes of it. This - // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait - // for u8`, when the trait always uses `Vec<u8>`. - // - // See also https://github.com/rust-lang/rust-clippy/issues/2894. - for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { - if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { - let mut visitor = SkipTyCollector::default(); - visitor.visit_ty(impl_hir_ty); - types_to_skip.extend(visitor.types_to_skip); - } + // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. + // + // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the + // trait declaration. This is used to check if `Self` was used in the trait + // declaration. + // + // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed + // to `Self`), we want to skip linting that type and all subtypes of it. This + // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait + // for u8`, when the trait always uses `Vec<u8>`. + // + // See also https://github.com/rust-lang/rust-clippy/issues/2894. + for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { + if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { + let mut visitor = SkipTyCollector::default(); + visitor.visit_ty(impl_hir_ty); + types_to_skip.extend(visitor.types_to_skip); } } } @@ -203,41 +208,38 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if_chain! { - if !hir_ty.span.from_expansion(); - if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(&StackItem::Check { + if !hir_ty.span.from_expansion() + && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) + && let Some(&StackItem::Check { impl_id, in_body, ref types_to_skip, - }) = self.stack.last(); - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; - if !matches!( + }) = self.stack.last() + && let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind + && !matches!( path.res, - Res::SelfTyParam { .. } - | Res::SelfTyAlias { .. } - | Res::Def(DefKind::TyParam, _) - ); - if !types_to_skip.contains(&hir_ty.hir_id); - let ty = if in_body > 0 { + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::Def(DefKind::TyParam, _) + ) + && !types_to_skip.contains(&hir_ty.hir_id) + && let ty = if in_body > 0 { cx.typeck_results().node_type(hir_ty.hir_id) } else { hir_ty_to_ty(cx.tcx, hir_ty) - }; - if same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()); - then { - span_lint(cx, hir_ty.span); } + && same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()) + { + span_lint(cx, hir_ty.span); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); - if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity(); - then {} else { return; } + if !expr.span.from_expansion() + && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) + && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() + && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() + { + } else { + return; } match expr.kind { ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), @@ -252,18 +254,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { - if_chain! { - if !pat.span.from_expansion(); - if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); - if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); + if !pat.span.from_expansion() + && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) + && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() // get the path from the pattern - if let PatKind::Path(QPath::Resolved(_, path)) + && let PatKind::Path(QPath::Resolved(_, path)) | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) - | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; - if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity(); - then { - check_path(cx, path); - } + | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind + && cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity() + { + check_path(cx, path); } } diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 28f1d487eb5..52327b82e84 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -3,7 +3,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_con use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -311,76 +310,63 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if_chain! { - if is_trait_method(cx, e, sym::TryInto) && name.ident.name == sym::try_into; - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, a, sym::Result); - if let ty::Adt(_, args) = a.kind(); - if let Some(a_type) = args.types().next(); - if same_type_and_consts(a_type, b); + if is_trait_method(cx, e, sym::TryInto) + && name.ident.name == sym::try_into + && let a = cx.typeck_results().expr_ty(e) + && let b = cx.typeck_results().expr_ty(recv) + && is_type_diagnostic_item(cx, a, sym::Result) + && let ty::Adt(_, args) = a.kind() + && let Some(a_type) = args.types().next() + && same_type_and_consts(a_type, b) + { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + &format!("useless conversion to the same type: `{b}`"), + None, + "consider removing `.try_into()`", + ); + } + }, - then { + ExprKind::Call(path, [arg]) => { + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && !is_ty_alias(qpath) + { + let a = cx.typeck_results().expr_ty(e); + let b = cx.typeck_results().expr_ty(arg); + if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id) + && is_type_diagnostic_item(cx, a, sym::Result) + && let ty::Adt(_, args) = a.kind() + && let Some(a_type) = args.types().next() + && same_type_and_consts(a_type, b) + { + let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); span_lint_and_help( cx, USELESS_CONVERSION, e.span, &format!("useless conversion to the same type: `{b}`"), None, - "consider removing `.try_into()`", + &hint, ); } - } - }, - - ExprKind::Call(path, [arg]) => { - if_chain! { - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if !is_ty_alias(qpath); - then { - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(arg); - if_chain! { - if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id); - if is_type_diagnostic_item(cx, a, sym::Result); - if let ty::Adt(_, args) = a.kind(); - if let Some(a_type) = args.types().next(); - if same_type_and_consts(a_type, b); - then { - let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); - span_lint_and_help( - cx, - USELESS_CONVERSION, - e.span, - &format!("useless conversion to the same type: `{b}`"), - None, - &hint, - ); - } - } - - if_chain! { - if cx.tcx.is_diagnostic_item(sym::from_fn, def_id); - if same_type_and_consts(a, b); - - then { - let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_par(); - let sugg_msg = - format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); - span_lint_and_sugg( - cx, - USELESS_CONVERSION, - e.span, - &format!("useless conversion to the same type: `{b}`"), - &sugg_msg, - sugg.to_string(), - app, - ); - } - } + if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_par(); + let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + &format!("useless conversion to the same type: `{b}`"), + &sugg_msg, + sugg.to_string(), + app, + ); } } }, diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 152248afc90..e8842ce10f8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -7,7 +7,7 @@ use rustc_ast::LitIntType; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::{ - ArrayLen, BindingAnnotation, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, CaptureBy + ArrayLen, BindingAnnotation, CaptureBy, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index ddcb9f27c6c..877a77fd6d2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,6 @@ pub mod almost_standard_lint_formulation; pub mod collapsible_calls; pub mod compiler_lint_functions; -pub mod if_chain_style; pub mod interning_defined_symbol; pub mod invalid_paths; pub mod lint_without_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index d7666b77f6e..f514f166cff 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind}; @@ -78,45 +77,43 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { return; } - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion( - cx, - expr, - &and_then_snippets, - &span_suggestion_snippets(cx, span_call_args), - ); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - }, - "note" => { - let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - }, - _ => (), - } + if let ExprKind::Call(func, and_then_args) = expr.kind + && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) + && and_then_args.len() == 5 + && let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind + && let body = cx.tcx.hir().body(body) + && let only_expr = peel_blocks_with_stmt(body.value) + && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind + && let ExprKind::Path(..) = recv.kind + { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + suggest_suggestion( + cx, + expr, + &and_then_snippets, + &span_suggestion_snippets(cx, span_call_args), + ); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + }, + "note" => { + let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + }, + _ => (), } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs index cacd05262a2..5aa1417cfb4 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::match_type; use clippy_utils::{is_lint_allowed, paths}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -56,22 +55,20 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { return; } - if_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - &format!("please use the Clippy variant of this function: `{sugg}`"), - ); - } + if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind + && let fn_name = path.ident + && let Some(sugg) = self.map.get(fn_name.as_str()) + && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() + && (match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT)) + { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{sugg}`"), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs deleted file mode 100644 index 8cdd5ea8903..00000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs +++ /dev/null @@ -1,166 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::{higher, is_else_clause, is_expn_of}; -use if_chain::if_chain; -use rustc_hir as hir; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Span}; - -declare_clippy_lint! { - /// Finds unidiomatic usage of `if_chain!` - pub IF_CHAIN_STYLE, - internal, - "non-idiomatic `if_chain!` usage" -} - -declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); - -impl<'tcx> LateLintPass<'tcx> for IfChainStyle { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let (local, after, if_chain_span) = if_chain! { - if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; - if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); - then { (local, after, if_chain_span) } else { return } - }; - if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be above the `if_chain!`", - ); - } else if local.span.eq_ctxt(block.span) && is_if_chain_then(after, block.expr, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be inside `then { .. }`", - ); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - return; - }; - let ExprKind::Block(then_block, _) = then.kind else { - return; - }; - let if_chain_span = is_expn_of(expr.span, "if_chain"); - if !els { - check_nested_if_chains(cx, expr, then_block, if_chain_span); - } - let Some(if_chain_span) = if_chain_span else { return }; - // check for `if a && b;` - if_chain! { - if let ExprKind::Binary(op, _, _) = cond.kind; - if op.node == BinOpKind::And; - if cx.sess().source_map().is_multiline(cond.span); - then { - span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); - } - } - if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) - && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) - { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); - } - } -} - -fn check_nested_if_chains( - cx: &LateContext<'_>, - if_expr: &Expr<'_>, - then_block: &Block<'_>, - if_chain_span: Option<Span>, -) { - #[rustfmt::skip] - let (head, tail) = match *then_block { - Block { stmts, expr: Some(tail), .. } => (stmts, tail), - Block { - stmts: &[ - ref head @ .., - Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } - ], - .. - } => (head, tail), - _ => return, - }; - if_chain! { - if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - let sm = cx.sess().source_map(); - if head - .iter() - .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); - if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then { - } else { - return; - } - } - let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { - (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), - (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), - (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), - _ => return, - }; - span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { - let (span, msg) = match head { - [] => return, - [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), - [a, .., b] => ( - a.span.to(b.span), - "these `let` statements can also be in the `if_chain!`", - ), - }; - diag.span_help(span, msg); - }); -} - -fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { - cx.tcx - .hir() - .parent_iter(hir_id) - .find(|(_, node)| { - #[rustfmt::skip] - !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) - }) - .map_or(false, |(id, _)| { - is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) - }) -} - -/// Checks a trailing slice of statements and expression of a `Block` to see if they are part -/// of the `then {..}` portion of an `if_chain!` -fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { - let span = if let [stmt, ..] = stmts { - stmt.span - } else if let Some(expr) = expr { - expr.span - } else { - // empty `then {}` - return true; - }; - is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) -} - -/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. -fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { - let mut span = local.pat.span; - if let Some(init) = local.init { - span = span.to(init.span); - } - span.adjust(if_chain_span.ctxt().outer_expn()); - let sm = cx.sess().source_map(); - let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} 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 fc9afe5ca8b..16d0636b834 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 @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; -use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -77,15 +76,13 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { for def_id in def_path_def_ids(cx, module) { for item in cx.tcx.module_children(def_id) { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id).instantiate_identity(); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } + if let Res::Def(DefKind::Const, item_def_id) = item.res + && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() + && match_type(cx, ty, &paths::SYMBOL) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) + && let Ok(value) = value.to_u32() + { + self.symbol_map.insert(value, item_def_id); } } } @@ -93,24 +90,22 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } + 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 value = Symbol::intern(&arg).as_u32() + && let Some(&def_id) = self.symbol_map.get(&value) + { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); } if let ExprKind::Binary(op, left, right) = expr.kind { if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { @@ -163,27 +158,28 @@ impl InterningDefinedSymbol { fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> { static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR]; static SYMBOL_STR_PATHS: &[&[&str]] = &[&paths::SYMBOL_AS_STR, &paths::SYMBOL_TO_IDENT_STRING]; - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } + let call = if let ExprKind::AddrOf(_, _, e) = expr.kind + && let ExprKind::Unary(UnOp::Deref, e) = e.kind + { + e + } else { + expr }; - if_chain! { + if let ExprKind::MethodCall(_, item, [], _) = call.kind // is a method call - if let ExprKind::MethodCall(_, item, [], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); + && let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id) + && let ty = cx.typeck_results().expr_ty(item) // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + && let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { Some(false) } else if match_type(cx, ty, &paths::IDENT) { Some(true) } else { None - }; + } // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(is_to_owned) = paths + && let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS } + && let Some(is_to_owned) = paths .iter() .find_map(|path| if match_def_path(cx, did, path) { Some(path == &paths::SYMBOL_TO_IDENT_STRING) @@ -194,14 +190,13 @@ impl InterningDefinedSymbol { Some(true) } else { None - }); - then { - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } + }) + { + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); } // is a string constant if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { 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 25077223885..66d32087fcd 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,7 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; -use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::Item; @@ -31,13 +30,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let local_def_id = &cx.tcx.parent_module(item.hir_id()); let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if 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); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - if let Some(path) = path + 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(path) = path .iter() .map(|x| { if let Constant::Str(s) = x { @@ -46,11 +44,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { None } }) - .collect::<Option<Vec<&str>>>(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } + .collect::<Option<Vec<&str>>>() + && !check_path(cx, &path[..]) + { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); } } } 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 00e352961bd..486e8220484 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 @@ -2,7 +2,6 @@ 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}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; @@ -309,14 +308,16 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> { let attrs = cx.tcx.hir().attrs(item.hir_id()); attrs.iter().find_map(|attr| { - if_chain! { + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { Some(version) } else { None } + && let [tool_name, attr_name] = &attr_kind.item.path.segments[..] + && tool_name.ident.name == sym::clippy + && attr_name.ident.name == sym::version + && let Some(version) = attr.value_str() + { + Some(version) + } else { + None } }) } 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 51abe0c1dc3..8ecdba47f89 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 @@ -14,7 +14,6 @@ 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 if_chain::if_chain; use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -543,49 +542,45 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { // Normal lint - if_chain! { + if is_lint_ref_type(cx, ty) // item validation - if is_lint_ref_type(cx, ty); // disallow check - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() // metadata extraction - if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item); - if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item); - then { - if let Some(configuration_section) = self.get_lint_configs(&lint_name) { - raw_docs.push_str(&configuration_section); - } - let version = get_lint_version(cx, item); - - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - group, - level, - version, - raw_docs, - )); + && let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item) + && let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item) + { + if let Some(configuration_section) = self.get_lint_configs(&lint_name) { + raw_docs.push_str(&configuration_section); } + let version = get_lint_version(cx, item); + + self.lints.push(LintMetadata::new( + lint_name, + SerializableSpan::from_item(cx, item), + group, + level, + version, + raw_docs, + )); } - if_chain! { - if is_deprecated_lint(cx, ty); + if is_deprecated_lint(cx, ty) // disallow check - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() // Metadata the little we can get from a deprecated lint - if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item); - then { - 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, - )); - } + && 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, + )); } } } @@ -789,15 +784,13 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) { loop { if let Some(lint_name) = names.pop() { for (k, v) in RENAMED_LINTS { - if_chain! { - if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); - if name == lint_name; - if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); - then { - lint.former_ids.insert(past_name.to_owned()); - writeln!(collected, "* `{past_name}`").unwrap(); - names.push(past_name.to_string()); - } + 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) + { + lint.former_ids.insert(past_name.to_owned()); + writeln!(collected, "* `{past_name}`").unwrap(); + names.push(past_name.to_string()); } } @@ -927,20 +920,17 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { } fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - if_chain! { - if let ExprKind::Path(qpath) = &expr.kind; - if let QPath::Resolved(_, path) = qpath; - - let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); - if match_type(self.cx, expr_ty, &paths::LINT); - then { - if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { - let lint_name = last_path_segment(qpath).ident.name; - self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); - } else if let Some(local) = get_parent_local(self.cx, expr) { - if let Some(local_init) = local.init { - intravisit::walk_expr(self, local_init); - } + if let ExprKind::Path(qpath) = &expr.kind + && let QPath::Resolved(_, path) = qpath + && let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)) + && match_type(self.cx, expr_ty, &paths::LINT) + { + if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { + let lint_name = last_path_segment(qpath).ident.name; + self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); + } else if let Some(local) = get_parent_local(self.cx, expr) { + if let Some(local_init) = local.init { + intravisit::walk_expr(self, local_init); } } } @@ -992,13 +982,11 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); - if_chain! { - if match_type(self.cx, expr_ty, &paths::APPLICABILITY); - if let Some(local) = get_parent_local(self.cx, expr); - if let Some(local_init) = local.init; - then { - intravisit::walk_expr(self, local_init); - } + if match_type(self.cx, expr_ty, &paths::APPLICABILITY) + && let Some(local) = get_parent_local(self.cx, expr) + && let Some(local_init) = local.init + { + intravisit::walk_expr(self, local_init); }; intravisit::walk_expr(self, expr); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 86b77a77f17..86b1a0ae624 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -22,40 +21,41 @@ declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); impl LateLintPass<'_> for MsrvAttrImpl { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(_), - items, - .. - }) = &item.kind; - if let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity); - let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS); - if let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(_), + items, + .. + }) = &item.kind + && let Some(trait_ref) = cx + .tcx + .impl_trait_ref(item.owner_id) + .map(EarlyBinder::instantiate_identity) + && let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS) + && (is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS)) + && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() + && self_ty_def.is_struct() + && self_ty_def.all_fields().any(|f| { cx.tcx .type_of(f.did) .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } + }) + && !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)) + { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs index 2b13fad8066..77b95e51f62 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::match_type; use clippy_utils::{is_lint_allowed, method_calls, paths}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -40,23 +39,21 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { let (method_names, arg_lists, spans) = method_calls(expr, 2); let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args) = arg_lists[1]; - if args.is_empty(); - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } + if let ["expn_data", "outer_expn"] = method_names.as_slice() + && let (self_arg, args) = arg_lists[1] + && args.is_empty() + && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() + && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT) + { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 81be04659b9..9aa23b6efe3 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::Applicability; @@ -102,108 +101,106 @@ impl UnnecessaryDefPath { &["clippy_utils", "is_expr_path_def_path"], ]; - if_chain! { - if let [cx_arg, def_arg, args @ ..] = args; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + if let [cx_arg, def_arg, args @ ..] = args + && let ExprKind::Path(path) = &func.kind + && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && let Some(which_path) = match_any_def_paths(cx, id, PATHS) + && let item_arg = if which_path == 4 { &args[1] } else { &args[0] } // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next(); - then { - // Check if the target item is a diagnostic item or LangItem. - #[rustfmt::skip] - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(item_name) = get_lang_item_name(cx, def_id) { - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; + && let Some(segments) = path_to_matched_type(cx, item_arg) + && let segments = segments.iter().map(|sym| &**sym).collect::<Vec<_>>() + && let Some(def_id) = def_path_def_ids(cx, &segments[..]).next() + { + // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - _ => false, - }; + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + _ => false, + }; - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => ( - format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), - has_ctor, - ), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), - has_ctor, - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => { - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) - }, - // match_type - (2, Item::DiagnosticItem(item)) => ( - format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (2, Item::LangItem(item)) => ( - format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), - false, - ), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), - false, - ), - (3, Item::DiagnosticItem(item)) => ( - format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", - ), - false, + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), + has_ctor, + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, + // match_type + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", ), - _ => return, - }; + false, + ), + _ => return, + }; - span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead", - ); - } - }); + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); - self.linted_def_ids.insert(def_id); - } + self.linted_def_ids.insert(def_id); } } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index a9a3aaad366..f58641289f8 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -7,7 +7,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, higher, is_trait_method}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -71,21 +70,19 @@ fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` - if_chain! { - if adjusts_to_slice(cx, expr); - if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()); - then { - let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { - // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) - (SuggestedType::SliceRef(mutability), expr.span) - } else { - // `expr` is the `vec![_]` expansion, so suggest `[_]` - // and also use the span of the actual `vec![_]` expression - (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site) - }; - - self.check_vec_macro(cx, &vec_args, span, suggest_slice); - } + if adjusts_to_slice(cx, expr) + && let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) + { + let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind { + // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.) + (SuggestedType::SliceRef(mutability), expr.span) + } else { + // `expr` is the `vec![_]` expansion, so suggest `[_]` + // and also use the span of the actual `vec![_]` expression + (SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site) + }; + + self.check_vec_macro(cx, &vec_args, span, suggest_slice); } // search for `let foo = vec![_]` expressions where all uses of `foo` @@ -123,15 +120,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { } // search for `for _ in vec![…]` - if_chain! { - if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr); - if let Some(vec_args) = higher::VecArgs::hir(cx, arg); - if self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR); - then { - // report the error around the `vec!` not inside `<std macros>:` - let span = arg.span.ctxt().outer_expn_data().call_site; - self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array); - } + if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) + && let Some(vec_args) = higher::VecArgs::hir(cx, arg) + && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR) + { + // report the error around the `vec!` not inside `<std macros>:` + let span = arg.span.ctxt().outer_expn_data().call_site; + self.check_vec_macro(cx, &vec_args, span, SuggestedType::Array); } } diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index d88ede76398..5c1bea74486 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_test_module_or_function; use clippy_utils::source::{snippet, snippet_with_applicability}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, PathSegment, UseKind}; @@ -127,70 +126,55 @@ impl LateLintPass<'_> for WildcardImports { if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { return; } - if_chain! { - if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if self.warn_on_all || !self.check_exceptions(item, use_path.segments); - let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id); - if !used_imports.is_empty(); // Already handled by `unused_imports` - if !used_imports.contains(&kw::Underscore); - then { - let mut applicability = Applicability::MachineApplicable; - let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); - let (span, braced_glob) = if import_source_snippet.is_empty() { - // This is a `_::{_, *}` import - // In this case `use_path.span` is empty and ends directly in front of the `*`, - // so we need to extend it by one byte. - ( - use_path.span.with_hi(use_path.span.hi() + BytePos(1)), - true, - ) - } else { - // In this case, the `use_path.span` ends right before the `::*`, so we need to - // extend it up to the `*`. Since it is hard to find the `*` in weird - // formattings like `use _ :: *;`, we extend it up to, but not including the - // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we - // can just use the end of the item span - let mut span = use_path.span.with_hi(item.span.hi()); - if snippet(cx, span, "").ends_with(';') { - span = use_path.span.with_hi(item.span.hi() - BytePos(1)); - } - ( - span, false, - ) - }; + if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind + && (self.warn_on_all || !self.check_exceptions(item, use_path.segments)) + && let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id) + && !used_imports.is_empty() // Already handled by `unused_imports` + && !used_imports.contains(&kw::Underscore) + { + let mut applicability = Applicability::MachineApplicable; + let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); + let (span, braced_glob) = if import_source_snippet.is_empty() { + // This is a `_::{_, *}` import + // In this case `use_path.span` is empty and ends directly in front of the `*`, + // so we need to extend it by one byte. + (use_path.span.with_hi(use_path.span.hi() + BytePos(1)), true) + } else { + // In this case, the `use_path.span` ends right before the `::*`, so we need to + // extend it up to the `*`. Since it is hard to find the `*` in weird + // formattings like `use _ :: *;`, we extend it up to, but not including the + // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we + // can just use the end of the item span + let mut span = use_path.span.with_hi(item.span.hi()); + if snippet(cx, span, "").ends_with(';') { + span = use_path.span.with_hi(item.span.hi() - BytePos(1)); + } + (span, false) + }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); - let imports_string = if imports.len() == 1 { - imports.pop().unwrap() - } else if braced_glob { - imports.join(", ") - } else { - format!("{{{}}}", imports.join(", ")) - }; + let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); + let imports_string = if imports.len() == 1 { + imports.pop().unwrap() + } else if braced_glob { + imports.join(", ") + } else { + format!("{{{}}}", imports.join(", ")) + }; - let sugg = if braced_glob { - imports_string - } else { - format!("{import_source_snippet}::{imports_string}") - }; + let sugg = if braced_glob { + imports_string + } else { + format!("{import_source_snippet}::{imports_string}") + }; - // Glob imports always have a single resolution. - let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { - (ENUM_GLOB_USE, "usage of wildcard import for enum variants") - } else { - (WILDCARD_IMPORTS, "usage of wildcard import") - }; + // Glob imports always have a single resolution. + let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { + (ENUM_GLOB_USE, "usage of wildcard import for enum variants") + } else { + (WILDCARD_IMPORTS, "usage of wildcard import") + }; - span_lint_and_sugg( - cx, - lint, - span, - message, - "try", - sugg, - applicability, - ); - } + span_lint_and_sugg(cx, lint, span, message, "try", sugg, applicability); } } 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 f2f0699ef48..1d6217d186c 100644 --- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -1,6 +1,5 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -32,35 +31,30 @@ declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]); impl<'tcx> LateLintPass<'tcx> for ZeroDiv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for instances of 0.0/0.0 - if_chain! { - if let ExprKind::Binary(ref op, left, right) = expr.kind; - if op.node == BinOpKind::Div; + if let ExprKind::Binary(ref op, left, right) = expr.kind + && op.node == BinOpKind::Div // 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. - if let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left); - if let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right); - if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value; - if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value; - then { - // since we're about to suggest a use of f32::NAN or f64::NAN, - // match the precision of the literals that are given. - let float_type = match (lhs_value, rhs_value) { - (Constant::F64(_), _) - | (_, Constant::F64(_)) => "f64", - _ => "f32" - }; - span_lint_and_help( - cx, - ZERO_DIVIDED_BY_ZERO, - expr.span, - "constant division of `0.0` with `0.0` will always result in NaN", - None, - &format!( - "consider using `{float_type}::NAN` if you would like a constant representing NaN", - ), - ); - } + && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left) + && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) + && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) + && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) + { + // since we're about to suggest a use of f32::NAN or f64::NAN, + // match the precision of the literals that are given. + let float_type = match (lhs_value, rhs_value) { + (Constant::F64(_), _) | (_, Constant::F64(_)) => "f64", + _ => "f32", + }; + span_lint_and_help( + cx, + ZERO_DIVIDED_BY_ZERO, + expr.span, + "constant division of `0.0` with `0.0` will always result in NaN", + None, + &format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index fee100fe1ea..41c1757fde8 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; -use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -46,23 +45,28 @@ declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); impl LateLintPass<'_> for ZeroSizedMapValues { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if_chain! { - if !hir_ty.span.from_expansion(); - if !in_trait_impl(cx, hir_ty.hir_id); - let ty = ty_from_hir_ty(cx, hir_ty); - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap); - if let Adt(_, args) = ty.kind(); - let ty = args.type_at(1); + if !hir_ty.span.from_expansion() + && !in_trait_impl(cx, hir_ty.hir_id) + && let ty = ty_from_hir_ty(cx, hir_ty) + && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && let Adt(_, args) = ty.kind() + && let ty = args.type_at(1) // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 - if !ty.has_escaping_bound_vars(); + && !ty.has_escaping_bound_vars() // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. - if is_normalizable(cx, cx.param_env, ty); - if let Ok(layout) = cx.layout_of(ty); - if layout.is_zst(); - then { - span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead"); - } + && is_normalizable(cx, cx.param_env, ty) + && let Ok(layout) = cx.layout_of(ty) + && layout.is_zst() + { + span_lint_and_help( + cx, + ZERO_SIZED_MAP_VALUES, + hir_ty.span, + "map with zero-sized value type", + None, + "consider using a set instead", + ); } } } diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index c9b01a68f42..d7053d3ff84 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,13 +1,12 @@ [package] name = "clippy_utils" -version = "0.1.75" +version = "0.1.76" edition = "2021" publish = false [dependencies] clippy_config = { path = "../clippy_config" } arrayvec = { version = "0.7", default-features = false } -if_chain = "1.0" itertools = "0.10.1" rustc-semver = "1.1" diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index b581a60de6e..dcfce45fc9a 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -2,7 +2,7 @@ use crate::source::{get_source_text, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; -use if_chain::if_chain; + use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; @@ -372,27 +372,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Binary(op, left, right) => self.binop(op, left, right), ExprKind::Call(callee, args) => { // We only handle a few const functions for now. - if_chain! { - if args.is_empty(); - if let ExprKind::Path(qpath) = &callee.kind; - let res = self.typeck_results.qpath_res(qpath, callee.hir_id); - if let Some(def_id) = res.opt_def_id(); - let def_path = self.lcx.get_def_path(def_id); - let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect(); - if let ["core", "num", int_impl, "max_value"] = *def_path; - then { - 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)) - } else { - None - } + 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 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)) + } else { + None } }, ExprKind::Index(arr, index, _) => self.index(arr, index), diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 45c7e3a6e1d..fa56e5b0ba2 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -46,6 +46,7 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { /// | ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) { + #[expect(clippy::disallowed_methods)] cx.struct_span_lint(lint, sp, msg.to_string(), |diag| { docs_link(diag, lint); diag @@ -80,6 +81,7 @@ pub fn span_lint_and_help<T: LintContext>( help_span: Option<Span>, help: &str, ) { + #[expect(clippy::disallowed_methods)] cx.struct_span_lint(lint, span, msg.to_string(), |diag| { let help = help.to_string(); if let Some(help_span) = help_span { @@ -123,6 +125,7 @@ pub fn span_lint_and_note<T: LintContext>( note_span: Option<Span>, note: &str, ) { + #[expect(clippy::disallowed_methods)] cx.struct_span_lint(lint, span, msg.to_string(), |diag| { let note = note.to_string(); if let Some(note_span) = note_span { @@ -145,6 +148,7 @@ where S: Into<MultiSpan>, F: FnOnce(&mut Diagnostic), { + #[expect(clippy::disallowed_methods)] cx.struct_span_lint(lint, sp, msg.to_string(), |diag| { f(diag); docs_link(diag, lint); @@ -153,6 +157,7 @@ where } pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { + #[expect(clippy::disallowed_methods)] cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| { docs_link(diag, lint); diag @@ -167,6 +172,7 @@ pub fn span_lint_hir_and_then( msg: &str, f: impl FnOnce(&mut Diagnostic), ) { + #[expect(clippy::disallowed_methods)] cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| { f(diag); docs_link(diag, lint); diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index edea4b3667f..3135a033648 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -5,7 +5,7 @@ use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; -use if_chain::if_chain; + use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; @@ -30,24 +30,22 @@ pub struct ForLoop<'tcx> { impl<'tcx> ForLoop<'tcx> { /// Parses a desugared `for` loop pub fn hir(expr: &Expr<'tcx>) -> Option<Self> { - if_chain! { - if let hir::ExprKind::DropTemps(e) = expr.kind; - if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind; - if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind; - if let hir::ExprKind::Loop(block, ..) = arm.body.kind; - if let [stmt] = block.stmts; - if let hir::StmtKind::Expr(e) = stmt.kind; - if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind; - if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind; - then { - return Some(Self { - pat: field.pat, - arg, - body: some_arm.body, - loop_id: arm.body.hir_id, - span: expr.span.ctxt().outer_expn_data().call_site, - }); - } + if let hir::ExprKind::DropTemps(e) = expr.kind + && let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind + && let hir::ExprKind::Call(_, [arg]) = iterexpr.kind + && let hir::ExprKind::Loop(block, ..) = arm.body.kind + && let [stmt] = block.stmts + && let hir::StmtKind::Expr(e) = stmt.kind + && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind + && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind + { + return Some(Self { + pat: field.pat, + arg, + body: some_arm.body, + loop_id: arm.body.hir_id, + span: expr.span.ctxt().outer_expn_data().call_site, + }); } None } @@ -277,29 +275,28 @@ impl<'a> VecArgs<'a> { /// Returns the arguments of the `vec!` macro if this expression was expanded /// from `vec!`. pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> { - if_chain! { - if let hir::ExprKind::Call(fun, args) = expr.kind; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - if is_expn_of(fun.span, "vec").is_some(); - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - then { - return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { - // `vec![elem; size]` case - Some(VecArgs::Repeat(&args[0], &args[1])) - } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { - // `vec![a, b, c]` case - if let hir::ExprKind::Call(_, [arg]) = &args[0].kind - && let hir::ExprKind::Array(args) = arg.kind { - Some(VecArgs::Vec(args)) - } else { - None - } - } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { - Some(VecArgs::Vec(&[])) + if let hir::ExprKind::Call(fun, args) = expr.kind + && let hir::ExprKind::Path(ref qpath) = fun.kind + && is_expn_of(fun.span, "vec").is_some() + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + { + return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { + // `vec![elem; size]` case + Some(VecArgs::Repeat(&args[0], &args[1])) + } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { + // `vec![a, b, c]` case + if let hir::ExprKind::Call(_, [arg]) = &args[0].kind + && let hir::ExprKind::Array(args) = arg.kind + { + Some(VecArgs::Vec(args)) } else { None - }; - } + } + } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { + Some(VecArgs::Vec(&[])) + } else { + None + }; } None diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 2a8b2ebd5fb..8031e6faa74 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -247,7 +247,7 @@ impl HirEqInterExpr<'_, '_, '_> { res } - #[expect(clippy::similar_names)] + #[expect(clippy::similar_names, clippy::too_many_lines)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { return false; @@ -271,9 +271,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, - (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { - both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) - }, + (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => { self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, @@ -294,9 +292,15 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => { + (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) => { self.eq_expr(lx, rx) && self.eq_ty(lt, rt) }, + (&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false, + (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body), + (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) + }, + (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => { l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) }, @@ -329,24 +333,70 @@ impl HirEqInterExpr<'_, '_, '_> { && self.eq_expr(l_receiver, r_receiver) && self.eq_exprs(l_args, r_args) }, + (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { + self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name) + }, + (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_array_length(ll, rl) }, (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)), - (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) && over(lf, rf, |l, r| self.eq_expr_field(l, r)) }, (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), + (&ExprKind::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt), (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), - (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), - (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), - (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { - self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name) - }, - _ => false, + (&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re), + ( + // Else branches for branches above, grouped as per `match_same_arms`. + | &ExprKind::AddrOf(..) + | &ExprKind::Array(..) + | &ExprKind::Assign(..) + | &ExprKind::AssignOp(..) + | &ExprKind::Binary(..) + | &ExprKind::Become(..) + | &ExprKind::Block(..) + | &ExprKind::Break(..) + | &ExprKind::Call(..) + | &ExprKind::Cast(..) + | &ExprKind::ConstBlock(..) + | &ExprKind::Continue(..) + | &ExprKind::DropTemps(..) + | &ExprKind::Field(..) + | &ExprKind::Index(..) + | &ExprKind::If(..) + | &ExprKind::Let(..) + | &ExprKind::Lit(..) + | &ExprKind::Loop(..) + | &ExprKind::Match(..) + | &ExprKind::MethodCall(..) + | &ExprKind::OffsetOf(..) + | &ExprKind::Path(..) + | &ExprKind::Repeat(..) + | &ExprKind::Ret(..) + | &ExprKind::Struct(..) + | &ExprKind::Tup(..) + | &ExprKind::Type(..) + | &ExprKind::Unary(..) + | &ExprKind::Yield(..) + + // --- Special cases that do not have a positive branch. + + // `Err` represents an invalid expression, so let's never assume that + // an invalid expressions is equal to anything. + | &ExprKind::Err(..) + + // For the time being, we always consider that two closures are unequal. + // This behavior may change in the future. + | &ExprKind::Closure(..) + // For the time being, we always consider that two instances of InlineAsm are different. + // This behavior may change in the future. + | &ExprKind::InlineAsm(_) + , _ + ) => false, }; (is_eq && (!self.should_ignore(left) || !self.should_ignore(right))) || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) @@ -684,6 +734,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(i.ident.name); } }, + ExprKind::Array(v) => { + self.hash_exprs(v); + }, ExprKind::Assign(l, r, _) => { self.hash_expr(l); self.hash_expr(r); @@ -693,6 +746,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, + ExprKind::Become(f) => { + self.hash_expr(f); + }, ExprKind::Block(b, _) => { self.hash_block(b); }, @@ -709,9 +765,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(j); } }, - ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { - self.hash_expr(e); - }, ExprKind::Call(fun, args) => { self.hash_expr(fun); self.hash_exprs(args); @@ -727,6 +780,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // closures inherit TypeckResults self.hash_expr(self.cx.tcx.hir().body(body).value); }, + ExprKind::ConstBlock(ref l_id) => { + self.hash_body(l_id.body); + }, + ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { + self.hash_expr(e); + }, ExprKind::Field(e, ref f) => { self.hash_expr(e); self.hash_name(f.name); @@ -788,12 +847,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } }, - ExprKind::OffsetOf(container, fields) => { - self.hash_ty(container); - for field in fields { - self.hash_name(field.name); - } - }, ExprKind::Let(Let { pat, init, ty, .. }) => { self.hash_expr(init); if let Some(ty) = ty { @@ -801,7 +854,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } self.hash_pat(pat); }, - ExprKind::Err(_) => {}, ExprKind::Lit(l) => { l.node.hash(&mut self.s); }, @@ -836,8 +888,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(receiver); self.hash_exprs(args); }, - ExprKind::ConstBlock(ref l_id) => { - self.hash_body(l_id.body); + ExprKind::OffsetOf(container, fields) => { + self.hash_ty(container); + for field in fields { + self.hash_name(field.name); + } + }, + ExprKind::Path(ref qpath) => { + self.hash_qpath(qpath); }, ExprKind::Repeat(e, len) => { self.hash_expr(e); @@ -848,12 +906,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); } }, - ExprKind::Become(f) => { - self.hash_expr(f); - }, - ExprKind::Path(ref qpath) => { - self.hash_qpath(qpath); - }, ExprKind::Struct(path, fields, ref expr) => { self.hash_qpath(path); @@ -869,13 +921,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Tup(tup) => { self.hash_exprs(tup); }, - ExprKind::Array(v) => { - self.hash_exprs(v); - }, ExprKind::Unary(lop, le) => { std::mem::discriminant(&lop).hash(&mut self.s); self.hash_expr(le); }, + ExprKind::Err(_) => {}, } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1181dfc0ef9..2466e8bb339 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -71,12 +71,12 @@ pub use self::hir_utils::{ both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; +use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::sync::{Mutex, MutexGuard, OnceLock}; -use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_data_structures::fx::FxHashMap; @@ -176,14 +176,12 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr /// canonical binding `HirId`. pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { let hir = cx.tcx.hir(); - if_chain! { - if let Some(Node::Pat(pat)) = hir.find(hir_id); - if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)); - let parent = hir.parent_id(hir_id); - if let Some(Node::Local(local)) = hir.find(parent); - then { - return local.init; - } + if let Some(Node::Pat(pat)) = hir.find(hir_id) + && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) + && let parent = hir.parent_id(hir_id) + && let Some(Node::Local(local)) = hir.find(parent) + { + return local.init; } None } @@ -713,13 +711,11 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> // Get the implemented trait for the current function let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); let parent_impl = cx.tcx.hir().get_parent_item(hir_id); - if_chain! { - if parent_impl != hir::CRATE_OWNER_ID; - if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id); - if let hir::ItemKind::Impl(impl_) = &item.kind; - then { - return impl_.of_trait.as_ref(); - } + if parent_impl != hir::CRATE_OWNER_ID + && let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id) + && let hir::ItemKind::Impl(impl_) = &item.kind + { + return impl_.of_trait.as_ref(); } None } @@ -823,12 +819,14 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< /// Returns true if the expr is equal to `Default::default` when evaluated. pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool { - if_chain! { - if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - then { true } else { false } + if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind + && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() + && (is_diag_trait_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) + { + true + } else { + false } } @@ -843,14 +841,14 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { _ => false, }, ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), - ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! { - if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind; - if let LitKind::Int(v, _) = const_lit.node; - if v <= 32 && is_default_equivalent(cx, x); - then { + ExprKind::Repeat(x, ArrayLen::Body(len)) => { + if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind + && let LitKind::Int(v, _) = const_lit.node + && v <= 32 + && is_default_equivalent(cx, x) + { true - } - else { + } else { false } }, @@ -1489,6 +1487,43 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { } } +/// Checks if the given expression is a part of `let else` +/// returns `true` for both the `init` and the `else` part +pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + let mut child_id = expr.hir_id; + for (parent_id, node) in tcx.hir().parent_iter(child_id) { + if let Node::Local(Local { + init: Some(init), + els: Some(els), + .. + }) = node + && (init.hir_id == child_id || els.hir_id == child_id) + { + return true; + } + + child_id = parent_id; + } + + false +} + +/// Checks if the given expression is the else clause of a `let else` expression +pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + let mut child_id = expr.hir_id; + for (parent_id, node) in tcx.hir().parent_iter(child_id) { + if let Node::Local(Local { els: Some(els), .. }) = node + && els.hir_id == child_id + { + return true; + } + + child_id = parent_id; + } + + false +} + /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. /// For the lower bound, this means that: /// - either there is none @@ -1632,13 +1667,13 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> { /// Convenience function to get the return type of a function. pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId) -> Ty<'tcx> { let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output(); - cx.tcx.erase_late_bound_regions(ret_ty) + cx.tcx.instantiate_bound_regions_with_erased(ret_ty) } /// Convenience function to get the nth argument type of a function. pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId, nth: usize) -> Ty<'tcx> { let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth); - cx.tcx.erase_late_bound_regions(arg) + cx.tcx.instantiate_bound_regions_with_erased(arg) } /// Checks if an expression is constructing a tuple-like enum variant or struct @@ -1736,15 +1771,13 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It /// operator or the `try` macro. pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - if_chain! { - if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind; - if ddpos.as_opt_usize().is_none(); - if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk); - if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; - if path_to_local_id(arm.body, hir_id); - then { - return true; - } + if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind + && ddpos.as_opt_usize().is_none() + && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk) + && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind + && path_to_local_id(arm.body, hir_id) + { + return true; } false } @@ -1763,14 +1796,12 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc return Some(expr); } - if_chain! { - if arms.len() == 2; - if arms[0].guard.is_none(); - if arms[1].guard.is_none(); - if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])); - then { - return Some(expr); - } + if arms.len() == 2 + && arms[0].guard.is_none() + && arms[1].guard.is_none() + && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]))) + { + return Some(expr); } } @@ -1887,14 +1918,12 @@ pub fn match_function_call<'tcx>( expr: &'tcx Expr<'_>, path: &[&str], ) -> Option<&'tcx [Expr<'tcx>]> { - if_chain! { - if let ExprKind::Call(fun, args) = expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); - if match_def_path(cx, fun_def_id, path); - then { - return Some(args); - } + if let ExprKind::Call(fun, args) = expr.kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && match_def_path(cx, fun_def_id, path) + { + return Some(args); }; None } @@ -1904,13 +1933,11 @@ pub fn match_function_call_with_def_id<'tcx>( expr: &'tcx Expr<'_>, fun_def_id: DefId, ) -> Option<&'tcx [Expr<'tcx>]> { - if_chain! { - if let ExprKind::Call(fun, args) = expr.kind; - if let ExprKind::Path(ref qpath) = fun.kind; - if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); - then { - return Some(args); - } + if let ExprKind::Call(fun, args) = expr.kind + && let ExprKind::Path(ref qpath) = fun.kind + && cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id) + { + return Some(args); }; None } @@ -2008,10 +2035,10 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t // check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { - ExprKind::Call(path, _) => if_chain! { - if let ExprKind::Path(ref qpath) = path.kind; - if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id); - then { + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref qpath) = path.kind + && let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id) + { Some(did) } else { None @@ -2034,6 +2061,18 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead. fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool { + if cx + .typeck_results() + .pat_binding_modes() + .get(pat.hir_id) + .is_some_and(|mode| matches!(mode, BindingMode::BindByReference(_))) + { + // If a tuple `(x, y)` is of type `&(i32, i32)`, then due to match ergonomics, + // the inner patterns become references. Don't consider this the identity function + // as that changes types. + return false; + } + match (pat.kind, expr.kind) { (PatKind::Binding(_, id, _, _), _) => { path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() @@ -2071,14 +2110,12 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { }, _, ) => { - if_chain! { - if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind; - if let ExprKind::Ret(Some(ret_val)) = e.kind; - then { - expr = ret_val; - } else { - return false; - } + if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind + && let ExprKind::Ret(Some(ret_val)) = e.kind + { + expr = ret_val; + } else { + return false; } }, _ => return check_pat(cx, param.pat, expr), @@ -2974,3 +3011,248 @@ pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: i _ => false, } } + +#[derive(Clone, Copy)] +pub enum RequiresSemi { + Yes, + No, +} +impl RequiresSemi { + pub fn requires_semi(self) -> bool { + matches!(self, Self::Yes) + } +} + +/// Check if the expression return `!`, a type coerced from `!`, or could return `!` if the final +/// expression were turned into a statement. +#[expect(clippy::too_many_lines)] +pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> { + struct BreakTarget { + id: HirId, + unused: bool, + } + + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + break_targets: Vec<BreakTarget>, + break_targets_for_result_ty: u32, + in_final_expr: bool, + requires_semi: bool, + is_never: bool, + } + + impl<'tcx> V<'_, 'tcx> { + fn push_break_target(&mut self, id: HirId) { + self.break_targets.push(BreakTarget { id, unused: true }); + self.break_targets_for_result_ty += u32::from(self.in_final_expr); + } + } + + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + // Note: Part of the complexity here comes from the fact that + // coercions are applied to the innermost expression. + // e.g. In `let x: u32 = { break () };` the never-to-any coercion + // is applied to the break expression. This means we can't just + // check the block's type as it will be `u32` despite the fact + // that the block always diverges. + + // The rest of the complexity comes from checking blocks which + // syntactically return a value, but will always diverge before + // reaching that point. + // e.g. In `let x = { foo(panic!()) };` the block's type will be the + // return type of `foo` even though it will never actually run. This + // can be trivially fixed by adding a semicolon after the call, but + // we must first detect that a semicolon is needed to make that + // suggestion. + + if self.is_never && self.break_targets.is_empty() { + if self.in_final_expr && !self.requires_semi { + // This expression won't ever run, but we still need to check + // if it can affect the type of the final expression. + match e.kind { + ExprKind::DropTemps(e) => self.visit_expr(e), + ExprKind::If(_, then, Some(else_)) => { + self.visit_expr(then); + self.visit_expr(else_); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.visit_expr(arm.body); + } + }, + ExprKind::Loop(b, ..) => { + self.push_break_target(e.hir_id); + self.in_final_expr = false; + self.visit_block(b); + self.break_targets.pop(); + }, + ExprKind::Block(b, _) => { + if b.targeted_by_break { + self.push_break_target(b.hir_id); + self.visit_block(b); + self.break_targets.pop(); + } else { + self.visit_block(b); + } + }, + _ => { + self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never(); + }, + } + } + return; + } + match e.kind { + ExprKind::DropTemps(e) => self.visit_expr(e), + ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true, + ExprKind::Ret(Some(e)) | ExprKind::Become(e) => { + self.in_final_expr = false; + self.visit_expr(e); + self.is_never = true; + }, + ExprKind::Break(dest, e) => { + if let Some(e) = e { + self.in_final_expr = false; + self.visit_expr(e); + } + if let Ok(id) = dest.target_id + && let Some((i, target)) = self + .break_targets + .iter_mut() + .enumerate() + .find(|(_, target)| target.id == id) + { + target.unused &= self.is_never; + if i < self.break_targets_for_result_ty as usize { + self.requires_semi = true; + } + } + self.is_never = true; + }, + ExprKind::If(cond, then, else_) => { + let in_final_expr = mem::replace(&mut self.in_final_expr, false); + self.visit_expr(cond); + self.in_final_expr = in_final_expr; + + if self.is_never { + self.visit_expr(then); + if let Some(else_) = else_ { + self.visit_expr(else_); + } + } else { + self.visit_expr(then); + let is_never = mem::replace(&mut self.is_never, false); + if let Some(else_) = else_ { + self.visit_expr(else_); + self.is_never &= is_never; + } + } + }, + ExprKind::Match(scrutinee, arms, _) => { + let in_final_expr = mem::replace(&mut self.in_final_expr, false); + self.visit_expr(scrutinee); + self.in_final_expr = in_final_expr; + + if self.is_never { + for arm in arms { + self.visit_arm(arm); + } + } else { + let mut is_never = true; + for arm in arms { + self.is_never = false; + if let Some(guard) = arm.guard { + let in_final_expr = mem::replace(&mut self.in_final_expr, false); + self.visit_expr(guard.body()); + self.in_final_expr = in_final_expr; + // The compiler doesn't consider diverging guards as causing the arm to diverge. + self.is_never = false; + } + self.visit_expr(arm.body); + is_never &= self.is_never; + } + self.is_never = is_never; + } + }, + ExprKind::Loop(b, _, _, _) => { + self.push_break_target(e.hir_id); + self.in_final_expr = false; + self.visit_block(b); + self.is_never = self.break_targets.pop().unwrap().unused; + }, + ExprKind::Block(b, _) => { + if b.targeted_by_break { + self.push_break_target(b.hir_id); + self.visit_block(b); + self.is_never &= self.break_targets.pop().unwrap().unused; + } else { + self.visit_block(b); + } + }, + _ => { + self.in_final_expr = false; + walk_expr(self, e); + self.is_never |= self.cx.typeck_results().expr_ty(e).is_never(); + }, + } + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + let in_final_expr = mem::replace(&mut self.in_final_expr, false); + for s in b.stmts { + self.visit_stmt(s); + } + self.in_final_expr = in_final_expr; + if let Some(e) = b.expr { + self.visit_expr(e); + } + } + + fn visit_local(&mut self, l: &'tcx Local<'_>) { + if let Some(e) = l.init { + self.visit_expr(e); + } + if let Some(else_) = l.els { + let is_never = self.is_never; + self.visit_block(else_); + self.is_never = is_never; + } + } + + fn visit_arm(&mut self, arm: &Arm<'tcx>) { + if let Some(guard) = arm.guard { + let in_final_expr = mem::replace(&mut self.in_final_expr, false); + self.visit_expr(guard.body()); + self.in_final_expr = in_final_expr; + } + self.visit_expr(arm.body); + } + } + + if cx.typeck_results().expr_ty(e).is_never() { + Some(RequiresSemi::No) + } else if let ExprKind::Block(b, _) = e.kind + && !b.targeted_by_break + && b.expr.is_none() + { + // If a block diverges without a final expression then it's type is `!`. + None + } else { + let mut v = V { + cx, + break_targets: Vec::new(), + break_targets_for_result_ty: 0, + in_final_expr: true, + requires_semi: false, + is_never: false, + }; + v.visit_expr(e); + v.is_never + .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) { + RequiresSemi::Yes + } else { + RequiresSemi::No + }) + } +} diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 5bca554378e..0a820a1754c 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -32,7 +32,15 @@ pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncRead pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; +pub const HASHMAP_ITER: [&str; 5] = ["std", "collections", "hash", "map", "Iter"]; +pub const HASHMAP_ITER_MUT: [&str; 5] = ["std", "collections", "hash", "map", "IterMut"]; +pub const HASHMAP_KEYS: [&str; 5] = ["std", "collections", "hash", "map", "Keys"]; +pub const HASHMAP_VALUES: [&str; 5] = ["std", "collections", "hash", "map", "Values"]; +pub const HASHMAP_DRAIN: [&str; 5] = ["std", "collections", "hash", "map", "Drain"]; +pub const HASHMAP_VALUES_MUT: [&str; 5] = ["std", "collections", "hash", "map", "ValuesMut"]; +pub const HASHSET_ITER_TY: [&str; 5] = ["std", "collections", "hash", "set", "Iter"]; pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; +pub const HASHSET_DRAIN: [&str; 5] = ["std", "collections", "hash", "set", "Drain"]; pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; @@ -99,3 +107,4 @@ pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; #[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"]; +pub const ALLOCATOR_GLOBAL: [&str; 3] = ["alloc", "alloc", "Global"]; diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 2ff979f2dcb..20588b63a78 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -1160,11 +1160,16 @@ pub fn make_normalized_projection<'tcx>( ) -> Option<Ty<'tcx>> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> { #[cfg(debug_assertions)] - if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_bound_regions()) { + if let Some((i, arg)) = ty + .args + .iter() + .enumerate() + .find(|(_, arg)| arg.has_escaping_bound_vars()) + { debug_assert!( false, "args contain late-bound region at index `{i}` which can't be normalized.\n\ - use `TyCtxt::erase_late_bound_regions`\n\ + use `TyCtxt::instantiate_bound_regions_with_erased`\n\ note: arg is `{arg:#?}`", ); return None; @@ -1233,11 +1238,16 @@ pub fn make_normalized_projection_with_regions<'tcx>( ) -> Option<Ty<'tcx>> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> { #[cfg(debug_assertions)] - if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_bound_regions()) { + if let Some((i, arg)) = ty + .args + .iter() + .enumerate() + .find(|(_, arg)| arg.has_escaping_bound_vars()) + { debug_assert!( false, "args contain late-bound region at index `{i}` which can't be normalized.\n\ - use `TyCtxt::erase_late_bound_regions`\n\ + use `TyCtxt::instantiate_bound_regions_with_erased`\n\ note: arg is `{arg:#?}`", ); return None; @@ -1266,3 +1276,8 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx> Err(_) => ty, } } + +/// Checks if the type is `core::mem::ManuallyDrop<_>` +pub fn is_manually_drop(ty: Ty<'_>) -> bool { + ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop) +} diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index beea9fd00e7..8c1150ed010 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.75" +version = "0.1.76" edition = "2021" publish = false diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 293fcbf3992..d40e176b4b0 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-11-02" +channel = "nightly-2023-11-16" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 7bb49d08da6..1ae8ac81695 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -148,7 +148,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } let conf = clippy_config::Conf::read(sess, &conf_path); - clippy_lints::register_plugins(lint_store, sess, conf); + clippy_lints::register_lints(lint_store, conf); clippy_lints::register_pre_expansion_lints(lint_store, conf); clippy_lints::register_renamed(lint_store); })); diff --git a/src/tools/clippy/tests/ui-internal/disallow_struct_span_lint.rs b/src/tools/clippy/tests/ui-internal/disallow_struct_span_lint.rs new file mode 100644 index 00000000000..3155c0235ff --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/disallow_struct_span_lint.rs @@ -0,0 +1,27 @@ +#![feature(rustc_private)] + +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; + +use rustc_errors::{DiagnosticMessage, MultiSpan}; +use rustc_hir::hir_id::HirId; +use rustc_lint::{Lint, LintContext}; +use rustc_middle::ty::TyCtxt; + +pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { + cx.struct_span_lint(lint, span, msg, |b| b); +} + +pub fn b( + tcx: TyCtxt<'_>, + lint: &'static Lint, + hir_id: HirId, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, +) { + tcx.struct_span_lint_hir(lint, hir_id, span, msg, |b| b); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/disallow_struct_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_struct_span_lint.stderr new file mode 100644 index 00000000000..76c487fb135 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/disallow_struct_span_lint.stderr @@ -0,0 +1,17 @@ +error: use of a disallowed method `rustc_lint::context::LintContext::struct_span_lint` + --> $DIR/disallow_struct_span_lint.rs:14:5 + | +LL | cx.struct_span_lint(lint, span, msg, |b| b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-methods` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` + +error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::struct_span_lint_hir` + --> $DIR/disallow_struct_span_lint.rs:24:5 + | +LL | tcx.struct_span_lint_hir(lint, hir_id, span, msg, |b| b); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.rs b/src/tools/clippy/tests/ui-internal/if_chain_style.rs deleted file mode 100644 index b462b20e04c..00000000000 --- a/src/tools/clippy/tests/ui-internal/if_chain_style.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![warn(clippy::if_chain_style)] -#![allow( - clippy::needless_if, - clippy::no_effect, - clippy::nonminimal_bool, - clippy::missing_clippy_version_attribute -)] - -extern crate if_chain; - -use if_chain::if_chain; - -fn main() { - if true { - let x = ""; - // `if_chain!` inside `if` - if_chain! { - if true; - if true; - then {} - } - } - if_chain! { - if true - // multi-line AND'ed conditions - && false; - if let Some(1) = Some(1); - // `let` before `then` - let x = ""; - then { - (); - } - } - if_chain! { - // single `if` condition - if true; - then { - let x = ""; - // nested if - if true {} - } - } - if_chain! { - // starts with `let ..` - let x = ""; - if let Some(1) = Some(1); - then { - let x = ""; - let x = ""; - // nested if_chain! - if_chain! { - if true; - if true; - then {} - } - } - } -} - -fn negative() { - if true { - (); - if_chain! { - if true; - if true; - then { (); } - } - } - if_chain! { - if true; - let x = ""; - if true; - then { (); } - } - if_chain! { - if true; - if true; - then { - if true { 1 } else { 2 } - } else { - 3 - } - }; - if true { - if_chain! { - if true; - if true; - then {} - } - } else if false { - if_chain! { - if true; - if false; - then {} - } - } -} diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr deleted file mode 100644 index ea04955323d..00000000000 --- a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr +++ /dev/null @@ -1,86 +0,0 @@ -error: this `if` can be part of the inner `if_chain!` - --> $DIR/if_chain_style.rs:14:5 - | -LL | / if true { -LL | | let x = ""; -LL | | // `if_chain!` inside `if` -LL | | if_chain! { -... | -LL | | } -LL | | } - | |_____^ - | -help: this `let` statement can also be in the `if_chain!` - --> $DIR/if_chain_style.rs:15:9 - | -LL | let x = ""; - | ^^^^^^^^^^^ - = note: `-D clippy::if-chain-style` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::if_chain_style)]` - -error: `if a && b;` should be `if a; if b;` - --> $DIR/if_chain_style.rs:24:12 - | -LL | if true - | ____________^ -LL | | // multi-line AND'ed conditions -LL | | && false; - | |____________________^ - -error: `let` expression should be inside `then { .. }` - --> $DIR/if_chain_style.rs:29:9 - | -LL | let x = ""; - | ^^^^^^^^^^^ - -error: this `if` can be part of the outer `if_chain!` - --> $DIR/if_chain_style.rs:40:13 - | -LL | if true {} - | ^^^^^^^^^^ - | -help: this `let` statement can also be in the `if_chain!` - --> $DIR/if_chain_style.rs:38:13 - | -LL | let x = ""; - | ^^^^^^^^^^^ - -error: `if_chain!` only has one `if` - --> $DIR/if_chain_style.rs:34:5 - | -LL | / if_chain! { -LL | | // single `if` condition -LL | | if true; -LL | | then { -... | -LL | | } -LL | | } - | |_____^ - | - = note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: `let` expression should be above the `if_chain!` - --> $DIR/if_chain_style.rs:45:9 - | -LL | let x = ""; - | ^^^^^^^^^^^ - -error: this `if_chain!` can be merged with the outer `if_chain!` - --> $DIR/if_chain_style.rs:51:13 - | -LL | / if_chain! { -LL | | if true; -LL | | if true; -LL | | then {} -LL | | } - | |_____________^ - | -help: these `let` statements can also be in the `if_chain!` - --> $DIR/if_chain_style.rs:48:13 - | -LL | / let x = ""; -LL | | let x = ""; - | |_______________________^ - -error: aborting due to 7 previous errors - diff --git a/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs b/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs index d03a577c454..349e81912e3 100644 --- a/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs +++ b/src/tools/clippy/tests/ui/arc_with_non_send_sync.rs @@ -33,16 +33,16 @@ fn main() { let _ = Arc::new(42); let _ = Arc::new(RefCell::new(42)); - //~^ ERROR: usage of an `Arc` that is not `Send` or `Sync` + //~^ ERROR: usage of an `Arc` that is not `Send` and `Sync` //~| NOTE: the trait `Sync` is not implemented for `RefCell<i32>` let mutex = Mutex::new(1); let _ = Arc::new(mutex.lock().unwrap()); - //~^ ERROR: usage of an `Arc` that is not `Send` or `Sync` + //~^ ERROR: usage of an `Arc` that is not `Send` and `Sync` //~| NOTE: the trait `Send` is not implemented for `MutexGuard<'_, i32>` let _ = Arc::new(&42 as *const i32); - //~^ ERROR: usage of an `Arc` that is not `Send` or `Sync` + //~^ ERROR: usage of an `Arc` that is not `Send` and `Sync` //~| NOTE: the trait `Send` is not implemented for `*const i32` //~| NOTE: the trait `Sync` is not implemented for `*const i32` } diff --git a/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr b/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr index fd239580d28..a7f91abda4e 100644 --- a/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr +++ b/src/tools/clippy/tests/ui/arc_with_non_send_sync.stderr @@ -1,35 +1,41 @@ -error: usage of an `Arc` that is not `Send` or `Sync` +error: usage of an `Arc` that is not `Send` and `Sync` --> $DIR/arc_with_non_send_sync.rs:35:13 | LL | let _ = Arc::new(RefCell::new(42)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the trait `Sync` is not implemented for `RefCell<i32>` - = note: required for `Arc<RefCell<i32>>` to implement `Send` and `Sync` - = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + = note: `Arc<RefCell<i32>>` is not `Send` and `Sync` as: + = note: - the trait `Sync` is not implemented for `RefCell<i32>` + = help: consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types + = note: if you intend to use `Arc` with `Send` and `Sync` traits + = note: wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `RefCell<i32>` = note: `-D clippy::arc-with-non-send-sync` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::arc_with_non_send_sync)]` -error: usage of an `Arc` that is not `Send` or `Sync` +error: usage of an `Arc` that is not `Send` and `Sync` --> $DIR/arc_with_non_send_sync.rs:40:13 | LL | let _ = Arc::new(mutex.lock().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the trait `Send` is not implemented for `MutexGuard<'_, i32>` - = note: required for `Arc<MutexGuard<'_, i32>>` to implement `Send` and `Sync` - = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + = note: `Arc<MutexGuard<'_, i32>>` is not `Send` and `Sync` as: + = note: - the trait `Send` is not implemented for `MutexGuard<'_, i32>` + = help: consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types + = note: if you intend to use `Arc` with `Send` and `Sync` traits + = note: wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `MutexGuard<'_, i32>` -error: usage of an `Arc` that is not `Send` or `Sync` +error: usage of an `Arc` that is not `Send` and `Sync` --> $DIR/arc_with_non_send_sync.rs:44:13 | LL | let _ = Arc::new(&42 as *const i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the trait `Send` is not implemented for `*const i32` - = note: the trait `Sync` is not implemented for `*const i32` - = note: required for `Arc<*const i32>` to implement `Send` and `Sync` - = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + = note: `Arc<*const i32>` is not `Send` and `Sync` as: + = note: - the trait `Send` is not implemented for `*const i32` + = note: - the trait `Sync` is not implemented for `*const i32` + = help: consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types + = note: if you intend to use `Arc` with `Send` and `Sync` traits + = note: wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `*const i32` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs new file mode 100644 index 00000000000..5761882273e --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs @@ -0,0 +1,6 @@ +/// Test for https://github.com/rust-lang/rust-clippy/issues/11230 + +fn main() { + const A: &[for<'a> fn(&'a ())] = &[]; + for v in A.iter() {} +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11755.rs b/src/tools/clippy/tests/ui/crashes/ice-11755.rs new file mode 100644 index 00000000000..367cb699857 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11755.rs @@ -0,0 +1,5 @@ +#![warn(clippy::unused_enumerate_index)] + +fn main() { + for () in [()].iter() {} +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11803.rs b/src/tools/clippy/tests/ui/crashes/ice-11803.rs new file mode 100644 index 00000000000..1bb8bf0c746 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11803.rs @@ -0,0 +1,9 @@ +//@no-rustfix + +#![warn(clippy::impl_trait_in_params)] + +pub fn g<T: IntoIterator<Item = impl Iterator<Item = impl Clone>>>() { + extern "C" fn implementation_detail() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11803.stderr b/src/tools/clippy/tests/ui/crashes/ice-11803.stderr new file mode 100644 index 00000000000..b8289048a8b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11803.stderr @@ -0,0 +1,26 @@ +error: `impl Trait` used as a function parameter + --> $DIR/ice-11803.rs:5:54 + | +LL | pub fn g<T: IntoIterator<Item = impl Iterator<Item = impl Clone>>>() { + | ^^^^^^^^^^ + | + = note: `-D clippy::impl-trait-in-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::impl_trait_in_params)]` +help: add a type parameter + | +LL | pub fn g<T: IntoIterator<Item = impl Iterator<Item = impl Clone>>, { /* Generic name */ }: Clone>() { + | +++++++++++++++++++++++++++++++ + +error: `impl Trait` used as a function parameter + --> $DIR/ice-11803.rs:5:33 + | +LL | pub fn g<T: IntoIterator<Item = impl Iterator<Item = impl Clone>>>() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add a type parameter + | +LL | pub fn g<T: IntoIterator<Item = impl Iterator<Item = impl Clone>>, { /* Generic name */ }: Iterator<Item = impl Clone>>() { + | +++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/dbg_macro/auxiliary/submodule.rs b/src/tools/clippy/tests/ui/dbg_macro/auxiliary/submodule.rs new file mode 100644 index 00000000000..b1df24737a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro/auxiliary/submodule.rs @@ -0,0 +1,3 @@ +fn f() { + dbg!(); +} diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs index 149b0847619..3f4770c63d0 100644 --- a/src/tools/clippy/tests/ui/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs @@ -2,10 +2,12 @@ #![warn(clippy::dbg_macro)] +#[path = "auxiliary/submodule.rs"] +mod submodule; + fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } //~^ ERROR: the `dbg!` macro is intended as a debugging tool - //~| NOTE: `-D clippy::dbg-macro` implied by `-D warnings` } fn bar(_: ()) {} diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index f45a7ba1fae..4d00421c711 100644 --- a/src/tools/clippy/tests/ui/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr @@ -1,18 +1,30 @@ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:6:22 + --> $DIR/auxiliary/submodule.rs:2:5 | -LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | dbg!(); + | ^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` help: remove the invocation before committing it to a version control system | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:9:22 + | +LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:13:8 + --> $DIR/dbg_macro.rs:15:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -23,7 +35,7 @@ LL | if n <= 1 { | ~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:15:9 + --> $DIR/dbg_macro.rs:17:9 | LL | dbg!(1) | ^^^^^^^ @@ -34,7 +46,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:18:9 + --> $DIR/dbg_macro.rs:20:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +57,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:24:5 + --> $DIR/dbg_macro.rs:26:5 | LL | dbg!(42); | ^^^^^^^^ @@ -56,7 +68,7 @@ LL | 42; | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:26:5 + --> $DIR/dbg_macro.rs:28:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ @@ -67,7 +79,7 @@ LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:28:14 + --> $DIR/dbg_macro.rs:30:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -78,7 +90,7 @@ LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:30:5 + --> $DIR/dbg_macro.rs:32:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +101,7 @@ LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:32:5 + --> $DIR/dbg_macro.rs:34:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -100,7 +112,7 @@ LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:53:5 + --> $DIR/dbg_macro.rs:55:5 | LL | dbg!(); | ^^^^^^^ @@ -112,7 +124,7 @@ LL + | error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:56:13 + --> $DIR/dbg_macro.rs:58:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -123,7 +135,7 @@ LL | let _ = (); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:58:9 + --> $DIR/dbg_macro.rs:60:9 | LL | bar(dbg!()); | ^^^^^^ @@ -134,7 +146,7 @@ LL | bar(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:60:10 + --> $DIR/dbg_macro.rs:62:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -145,7 +157,7 @@ LL | foo!(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:62:16 + --> $DIR/dbg_macro.rs:64:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -156,7 +168,7 @@ LL | foo2!(foo!(())); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:84:9 + --> $DIR/dbg_macro.rs:86:9 | LL | dbg!(2); | ^^^^^^^ @@ -167,7 +179,7 @@ LL | 2; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:91:5 + --> $DIR/dbg_macro.rs:93:5 | LL | dbg!(1); | ^^^^^^^ @@ -178,7 +190,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:97:5 + --> $DIR/dbg_macro.rs:99:5 | LL | dbg!(1); | ^^^^^^^ @@ -189,7 +201,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:104:9 + --> $DIR/dbg_macro.rs:106:9 | LL | dbg!(1); | ^^^^^^^ @@ -199,5 +211,5 @@ help: remove the invocation before committing it to a version control system LL | 1; | ~ -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed index 12158d0d12a..e6ca4bb66cc 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed @@ -301,24 +301,47 @@ fn main() { }; // Issue #11474 - pub struct Variant { - pub anonymous: Variant0, + #[derive(Clone, Copy)] + struct Wrap<T>(T); + impl<T> core::ops::Deref for Wrap<T> { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } } - - pub union Variant0 { - pub anonymous: std::mem::ManuallyDrop<Variant00>, + impl<T> core::ops::DerefMut for Wrap<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - pub struct Variant00 { - pub anonymous: Variant000, + union U<T: Copy> { + u: T, } - pub union Variant000 { - pub val: i32, + #[derive(Clone, Copy)] + struct S8 { + x: &'static str, } unsafe { - let mut p = core::mem::zeroed::<Variant>(); - (*p.anonymous.anonymous).anonymous.val = 1; + let mut x = U { + u: core::mem::ManuallyDrop::new(S8 { x: "" }), + }; + let _ = &mut (*x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ *x.u }).x; + + let mut x = U { + u: Wrap(core::mem::ManuallyDrop::new(S8 { x: "" })), + }; + let _ = &mut (*x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ **x.u }).x; + + let mut x = U { u: Wrap(S8 { x: "" }) }; + let _ = &mut x.u.x; + let _ = &mut { x.u }.x; + let _ = &mut ({ *x.u }).x; } } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs index dec021c1834..7531e1f87b7 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs @@ -301,24 +301,47 @@ fn main() { }; // Issue #11474 - pub struct Variant { - pub anonymous: Variant0, + #[derive(Clone, Copy)] + struct Wrap<T>(T); + impl<T> core::ops::Deref for Wrap<T> { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } } - - pub union Variant0 { - pub anonymous: std::mem::ManuallyDrop<Variant00>, + impl<T> core::ops::DerefMut for Wrap<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - pub struct Variant00 { - pub anonymous: Variant000, + union U<T: Copy> { + u: T, } - pub union Variant000 { - pub val: i32, + #[derive(Clone, Copy)] + struct S8 { + x: &'static str, } unsafe { - let mut p = core::mem::zeroed::<Variant>(); - (*p.anonymous.anonymous).anonymous.val = 1; + let mut x = U { + u: core::mem::ManuallyDrop::new(S8 { x: "" }), + }; + let _ = &mut (*x.u).x; + let _ = &mut (*{ x.u }).x; + let _ = &mut ({ *x.u }).x; + + let mut x = U { + u: Wrap(core::mem::ManuallyDrop::new(S8 { x: "" })), + }; + let _ = &mut (**x.u).x; + let _ = &mut (**{ x.u }).x; + let _ = &mut ({ **x.u }).x; + + let mut x = U { u: Wrap(S8 { x: "" }) }; + let _ = &mut (*x.u).x; + let _ = &mut (*{ x.u }).x; + let _ = &mut ({ *x.u }).x; } } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr index 3d2a7b0d9f4..cc9eeeb5042 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr @@ -241,5 +241,35 @@ error: deref which would be done by auto-deref LL | Some(x) => &mut *x, | ^^^^^^^ help: try: `x` -error: aborting due to 40 previous errors +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:332:22 + | +LL | let _ = &mut (*{ x.u }).x; + | ^^^^^^^^^^ help: try: `{ x.u }` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:338:22 + | +LL | let _ = &mut (**x.u).x; + | ^^^^^^^ help: try: `(*x.u)` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:339:22 + | +LL | let _ = &mut (**{ x.u }).x; + | ^^^^^^^^^^^ help: try: `{ x.u }` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:343:22 + | +LL | let _ = &mut (*x.u).x; + | ^^^^^^ help: try: `x.u` + +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:344:22 + | +LL | let _ = &mut (*{ x.u }).x; + | ^^^^^^^^^^ help: try: `{ x.u }` + +error: aborting due to 45 previous errors diff --git a/src/tools/clippy/tests/ui/iter_over_hash_type.rs b/src/tools/clippy/tests/ui/iter_over_hash_type.rs new file mode 100644 index 00000000000..7000c8bf96f --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_over_hash_type.rs @@ -0,0 +1,74 @@ +//@aux-build:proc_macros.rs +#![feature(rustc_private)] +#![warn(clippy::iter_over_hash_type)] +use std::collections::{HashMap, HashSet}; + +extern crate rustc_data_structures; + +extern crate proc_macros; + +fn main() { + let mut hash_set = HashSet::<i32>::new(); + let mut hash_map = HashMap::<i32, i32>::new(); + let mut fx_hash_map = rustc_data_structures::fx::FxHashMap::<i32, i32>::default(); + let mut fx_hash_set = rustc_data_structures::fx::FxHashMap::<i32, i32>::default(); + let vec = Vec::<i32>::new(); + + // test hashset + for x in &hash_set { + let _ = x; + } + for x in hash_set.iter() { + let _ = x; + } + for x in hash_set.clone() { + let _ = x; + } + for x in hash_set.drain() { + let _ = x; + } + + // test hashmap + for (x, y) in &hash_map { + let _ = (x, y); + } + for x in hash_map.keys() { + let _ = x; + } + for x in hash_map.values() { + let _ = x; + } + for x in hash_map.values_mut() { + *x += 1; + } + for x in hash_map.iter() { + let _ = x; + } + for x in hash_map.clone() { + let _ = x; + } + for x in hash_map.drain() { + let _ = x; + } + + // test type-aliased hashers + for x in fx_hash_set { + let _ = x; + } + for x in fx_hash_map { + let _ = x; + } + + // shouldnt fire + for x in &vec { + let _ = x; + } + for x in vec { + let _ = x; + } + + // should not lint, this comes from an external crate + proc_macros::external! { + for _ in HashMap::<i32, i32>::new() {} + } +} diff --git a/src/tools/clippy/tests/ui/iter_over_hash_type.stderr b/src/tools/clippy/tests/ui/iter_over_hash_type.stderr new file mode 100644 index 00000000000..cf420fb8e99 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_over_hash_type.stderr @@ -0,0 +1,109 @@ +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:18:5 + | +LL | / for x in &hash_set { +LL | | let _ = x; +LL | | } + | |_____^ + | + = note: `-D clippy::iter-over-hash-type` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::iter_over_hash_type)]` + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:21:5 + | +LL | / for x in hash_set.iter() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:24:5 + | +LL | / for x in hash_set.clone() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:27:5 + | +LL | / for x in hash_set.drain() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:32:5 + | +LL | / for (x, y) in &hash_map { +LL | | let _ = (x, y); +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:35:5 + | +LL | / for x in hash_map.keys() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:38:5 + | +LL | / for x in hash_map.values() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:41:5 + | +LL | / for x in hash_map.values_mut() { +LL | | *x += 1; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:44:5 + | +LL | / for x in hash_map.iter() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:47:5 + | +LL | / for x in hash_map.clone() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:50:5 + | +LL | / for x in hash_map.drain() { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:55:5 + | +LL | / for x in fx_hash_set { +LL | | let _ = x; +LL | | } + | |_____^ + +error: iteration over unordered hash-based type + --> $DIR/iter_over_hash_type.rs:58:5 + | +LL | / for x in fx_hash_map { +LL | | let _ = x; +LL | | } + | |_____^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr index 6de869699ec..bafe8cfddb4 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports.stderr +++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr @@ -1,23 +1,23 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:25:5 + --> $DIR/macro_use_imports.rs:23:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::mut_mut, inner::try_err};` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::macro_use_imports)]` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:23:5 + --> $DIR/macro_use_imports.rs:21:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::mut_mut, inner::try_err};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:21:5 + --> $DIR/macro_use_imports.rs:25:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:19:5 diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index 27717ab3a73..5d94660ec89 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -5,7 +5,9 @@ clippy::let_unit_value, clippy::match_single_binding, clippy::never_loop, - clippy::needless_if + clippy::needless_if, + clippy::diverging_sub_expression, + clippy::single_match )] #![warn(clippy::manual_let_else)] //@no-rustfix @@ -24,7 +26,7 @@ fn main() {} fn fire() { let v = if let Some(v_some) = g() { v_some } else { return }; //~^ ERROR: this could be rewritten as `let...else` - //~| NOTE: `-D clippy::manual-let-else` implied by `-D warnings` + let v = if let Some(v_some) = g() { //~^ ERROR: this could be rewritten as `let...else` v_some @@ -79,22 +81,76 @@ fn fire() { panic!(); }; + // The final expression will need to be turned into a statement. + let v = if let Some(v_some) = g() { + //~^ ERROR: this could be rewritten as `let...else` + v_some + } else { + panic!(); + () + }; + + // Even if the result is buried multiple expressions deep. + let v = if let Some(v_some) = g() { + //~^ ERROR: this could be rewritten as `let...else` + v_some + } else { + panic!(); + if true { + match 0 { + 0 => (), + _ => (), + } + } else { + panic!() + } + }; + + // Or if a break gives the value. + let v = if let Some(v_some) = g() { + //~^ ERROR: this could be rewritten as `let...else` + v_some + } else { + loop { + panic!(); + break (); + } + }; + + // Even if the break is in a weird position. + let v = if let Some(v_some) = g() { + //~^ ERROR: this could be rewritten as `let...else` + v_some + } else { + 'a: loop { + panic!(); + loop { + match 0 { + 0 if (return break 'a ()) => {}, + _ => {}, + } + } + } + }; + // A match diverges if all branches diverge: - // Note: the corresponding let-else requires a ; at the end of the match - // as otherwise the type checker does not turn it into a ! type. let v = if let Some(v_some) = g() { //~^ ERROR: this could be rewritten as `let...else` v_some } else { - match () { - _ if panic!() => {}, + match 0 { + 0 if true => panic!(), _ => panic!(), - } + }; }; // An if's expression can cause divergence: - let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; - //~^ ERROR: this could be rewritten as `let...else` + let v = if let Some(v_some) = g() { + //~^ ERROR: this could be rewritten as `let...else` + v_some + } else { + if panic!() {}; + }; // An expression of a match can cause divergence: let v = if let Some(v_some) = g() { @@ -103,7 +159,7 @@ fn fire() { } else { match panic!() { _ => {}, - } + }; }; // Top level else if @@ -342,6 +398,43 @@ fn not_fire() { } else { return; }; + + // A break that skips the divergent statement will cause the expression to be non-divergent. + let _x = if let Some(x) = Some(0) { + x + } else { + 'foo: loop { + break 'foo 0; + panic!(); + } + }; + + // Even in inner loops. + let _x = if let Some(x) = Some(0) { + x + } else { + 'foo: { + loop { + break 'foo 0; + } + panic!(); + } + }; + + // But a break that can't ever be reached still affects divergence checking. + let _x = if let Some(x) = g() { + x + } else { + 'foo: { + 'bar: loop { + loop { + break 'bar (); + } + break 'foo (); + } + panic!(); + }; + }; } struct S<T> { diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index 2b6504a1827..3beaf766efb 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -1,5 +1,5 @@ error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:25:5 + --> $DIR/manual_let_else.rs:27:5 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` @@ -8,7 +8,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return }; = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:28:5 + --> $DIR/manual_let_else.rs:30:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -26,7 +26,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:35:5 + --> $DIR/manual_let_else.rs:37:5 | LL | / let v = if let Some(v) = g() { LL | | @@ -47,25 +47,25 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:47:9 + --> $DIR/manual_let_else.rs:49:9 | LL | let v = if let Some(v_some) = g() { v_some } else { continue }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:49:9 + --> $DIR/manual_let_else.rs:51:9 | LL | let v = if let Some(v_some) = g() { v_some } else { break }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:54:5 + --> $DIR/manual_let_else.rs:56:5 | LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:58:5 + --> $DIR/manual_let_else.rs:60:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -83,7 +83,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:66:5 + --> $DIR/manual_let_else.rs:68:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -101,7 +101,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:74:5 + --> $DIR/manual_let_else.rs:76:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -127,6 +127,26 @@ LL | / let v = if let Some(v_some) = g() { LL | | LL | | v_some LL | | } else { +LL | | panic!(); +LL | | () +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + panic!(); +LL + () +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:94:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | +LL | | v_some +LL | | } else { ... | LL | | } LL | | }; @@ -135,21 +155,42 @@ LL | | }; help: consider writing | LL ~ let Some(v) = g() else { -LL + match () { -LL + _ if panic!() => {}, -LL + _ => panic!(), +LL + panic!(); +LL + if true { +LL + match 0 { +LL + 0 => (), +LL + _ => (), +LL + } +LL + } else { +LL + panic!() LL + } LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:96:5 + --> $DIR/manual_let_else.rs:110:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | +LL | | v_some +LL | | } else { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + loop { +LL + panic!(); +LL + break (); +LL + } +LL + }; | -LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { if panic!() {} };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:100:5 + --> $DIR/manual_let_else.rs:121:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -163,14 +204,81 @@ LL | | }; help: consider writing | LL ~ let Some(v) = g() else { +LL + 'a: loop { +LL + panic!(); +LL + loop { +LL + match 0 { +LL + 0 if (return break 'a ()) => {}, +LL + _ => {}, +LL + } +LL + } +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:137:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | +LL | | v_some +LL | | } else { +... | +LL | | }; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + match 0 { +LL + 0 if true => panic!(), +LL + _ => panic!(), +LL + }; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:148:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | +LL | | v_some +LL | | } else { +LL | | if panic!() {}; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + if panic!() {}; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:156:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | +LL | | v_some +LL | | } else { +... | +LL | | }; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { LL + match panic!() { LL + _ => {}, -LL + } +LL + }; LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:110:5 + --> $DIR/manual_let_else.rs:166:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -191,7 +299,7 @@ LL + } }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:120:5 + --> $DIR/manual_let_else.rs:176:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -220,7 +328,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:138:5 + --> $DIR/manual_let_else.rs:194:5 | LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { LL | | @@ -238,7 +346,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:146:5 + --> $DIR/manual_let_else.rs:202:5 | LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) { LL | | @@ -256,7 +364,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:156:13 + --> $DIR/manual_let_else.rs:212:13 | LL | let $n = if let Some(v) = $e { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };` @@ -267,19 +375,19 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:165:5 + --> $DIR/manual_let_else.rs:221:5 | LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:169:5 + --> $DIR/manual_let_else.rs:225:5 | LL | let mut v = if let Variant::B(b) = e() { b } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:174:5 + --> $DIR/manual_let_else.rs:230:5 | LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { LL | | @@ -297,19 +405,19 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:181:5 + --> $DIR/manual_let_else.rs:237:5 | LL | let v = if let Variant::A(.., a) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:185:5 + --> $DIR/manual_let_else.rs:241:5 | LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:189:5 + --> $DIR/manual_let_else.rs:245:5 | LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -327,7 +435,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:197:5 + --> $DIR/manual_let_else.rs:253:5 | LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -345,7 +453,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:205:5 + --> $DIR/manual_let_else.rs:261:5 | LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> { LL | | @@ -363,7 +471,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:322:5 + --> $DIR/manual_let_else.rs:378:5 | LL | / let _ = match ff { LL | | @@ -372,5 +480,5 @@ LL | | _ => macro_call!(), LL | | }; | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` -error: aborting due to 26 previous errors +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs index a224001a3df..8146091a2bb 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs @@ -138,6 +138,26 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in 0..dst.len() { dst[i] = src[i]; } + + // Range is equal to array length + let src = [0, 1, 2, 3, 4]; + let mut dst = [0; 4]; + for i in 0..4 { + //~^ ERROR: it looks like you're manually copying between slices + dst[i] = src[i]; + } + + let mut dst = [0; 6]; + for i in 0..5 { + //~^ ERROR: it looks like you're manually copying between slices + dst[i] = src[i]; + } + + let mut dst = [0; 5]; + for i in 0..5 { + //~^ ERROR: it looks like you're manually copying between slices + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr index b9dbda6ede7..4b5cd274da7 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -106,7 +106,7 @@ LL | / for i in 0..5 { LL | | LL | | dst[i - 0] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);` + | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:121:5 @@ -120,11 +120,38 @@ LL | | } error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:145:5 | +LL | / for i in 0..4 { +LL | | +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..4]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:151:5 + | +LL | / for i in 0..5 { +LL | | +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:157:5 + | +LL | / for i in 0..5 { +LL | | +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:165:5 + | LL | / for i in 0..src.len() { LL | | LL | | dst[i] = src[i].clone(); LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` -error: aborting due to 13 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index 62b0ba01860..53ebfb40ba0 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -24,28 +24,40 @@ fn main() { fn issue7189() { // should lint - let x = [(1, 2), (3, 4)]; - let _ = x.iter(); - let _ = x.iter(); - let _ = x.iter(); + let x = [(1, 2), (3, 4)].iter().copied(); + let _ = x.clone(); + let _ = x.clone(); + let _ = x.clone(); - let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))]; - let _ = y.iter(); + let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))].iter().copied(); + let _ = y.clone(); // should not lint - let _ = x.iter().map(|(x, y)| (x, y, y)); - let _ = x.iter().map(|(x, _y)| (x,)); - let _ = x.iter().map(|(x, _)| (x,)); - let _ = x.iter().map(|(x, ..)| (x,)); - let _ = y.iter().map(|(x, y, (z, _))| (x, y, (z, z))); + let _ = x.clone().map(|(x, y)| (x, y, y)); + let _ = x.clone().map(|(x, _y)| (x,)); + let _ = x.clone().map(|(x, _)| (x,)); + let _ = x.clone().map(|(x, ..)| (x,)); + let _ = y.clone().map(|(x, y, (z, _))| (x, y, (z, z))); let _ = y - .iter() - .map(|(x, y, (z, _)): &(i32, i32, (i32, (i32,)))| (x, y, (z, z))); + .clone() + .map(|(x, y, (z, _)): (i32, i32, (i32, (i32,)))| (x, y, (z, z))); let _ = y - .iter() - .map(|(x, y, (z, (w,))): &(i32, i32, (i32, (i32,)))| (x, y, (z, (w,)))); + .clone() + .map(|(x, y, (z, (w,))): (i32, i32, (i32, (i32,)))| (x, y, (z, (w,)))); } fn not_identity(x: &u16) -> u16 { *x } + +fn issue11764() { + let x = [(1, 2), (3, 4)]; + // don't lint: this is an `Iterator<Item = &(i32, i32)>` + // match ergonomics makes the binding patterns into references + // so that its type changes to `Iterator<Item = (&i32, &i32)>` + let _ = x.iter().map(|(x, y)| (x, y)); + let _ = x.iter().map(|x| (x.0,)).map(|(x,)| x); + + // no match ergonomics for `(i32, i32)` + let _ = x.iter().copied(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index b7f4c99f273..c646c056859 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -26,30 +26,42 @@ fn main() { fn issue7189() { // should lint - let x = [(1, 2), (3, 4)]; - let _ = x.iter().map(|(x, y)| (x, y)); - let _ = x.iter().map(|(x, y)| { + let x = [(1, 2), (3, 4)].iter().copied(); + let _ = x.clone().map(|(x, y)| (x, y)); + let _ = x.clone().map(|(x, y)| { return (x, y); }); - let _ = x.iter().map(|(x, y)| return (x, y)); + let _ = x.clone().map(|(x, y)| return (x, y)); - let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))]; - let _ = y.iter().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); + let y = [(1, 2, (3, (4,))), (5, 6, (7, (8,)))].iter().copied(); + let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); // should not lint - let _ = x.iter().map(|(x, y)| (x, y, y)); - let _ = x.iter().map(|(x, _y)| (x,)); - let _ = x.iter().map(|(x, _)| (x,)); - let _ = x.iter().map(|(x, ..)| (x,)); - let _ = y.iter().map(|(x, y, (z, _))| (x, y, (z, z))); + let _ = x.clone().map(|(x, y)| (x, y, y)); + let _ = x.clone().map(|(x, _y)| (x,)); + let _ = x.clone().map(|(x, _)| (x,)); + let _ = x.clone().map(|(x, ..)| (x,)); + let _ = y.clone().map(|(x, y, (z, _))| (x, y, (z, z))); let _ = y - .iter() - .map(|(x, y, (z, _)): &(i32, i32, (i32, (i32,)))| (x, y, (z, z))); + .clone() + .map(|(x, y, (z, _)): (i32, i32, (i32, (i32,)))| (x, y, (z, z))); let _ = y - .iter() - .map(|(x, y, (z, (w,))): &(i32, i32, (i32, (i32,)))| (x, y, (z, (w,)))); + .clone() + .map(|(x, y, (z, (w,))): (i32, i32, (i32, (i32,)))| (x, y, (z, (w,)))); } fn not_identity(x: &u16) -> u16 { *x } + +fn issue11764() { + let x = [(1, 2), (3, 4)]; + // don't lint: this is an `Iterator<Item = &(i32, i32)>` + // match ergonomics makes the binding patterns into references + // so that its type changes to `Iterator<Item = (&i32, &i32)>` + let _ = x.iter().map(|(x, y)| (x, y)); + let _ = x.iter().map(|x| (x.0,)).map(|(x,)| x); + + // no match ergonomics for `(i32, i32)` + let _ = x.iter().copied().map(|(x, y)| (x, y)); +} diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr index 4ca24b0b04c..ea077d66d64 100644 --- a/src/tools/clippy/tests/ui/map_identity.stderr +++ b/src/tools/clippy/tests/ui/map_identity.stderr @@ -41,31 +41,37 @@ LL | let _: Result<u32, u32> = Ok(1).map_err(|a| a); | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` error: unnecessary map of the identity function - --> $DIR/map_identity.rs:30:21 + --> $DIR/map_identity.rs:30:22 | -LL | let _ = x.iter().map(|(x, y)| (x, y)); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` +LL | let _ = x.clone().map(|(x, y)| (x, y)); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> $DIR/map_identity.rs:31:21 + --> $DIR/map_identity.rs:31:22 | -LL | let _ = x.iter().map(|(x, y)| { - | _____________________^ +LL | let _ = x.clone().map(|(x, y)| { + | ______________________^ LL | | return (x, y); LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> $DIR/map_identity.rs:34:21 + --> $DIR/map_identity.rs:34:22 | -LL | let _ = x.iter().map(|(x, y)| return (x, y)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` +LL | let _ = x.clone().map(|(x, y)| return (x, y)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> $DIR/map_identity.rs:37:21 + --> $DIR/map_identity.rs:37:22 | -LL | let _ = y.iter().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` +LL | let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 10 previous errors +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:66:30 + | +LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index 7866c89bd61..244a88e6691 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -48,7 +48,8 @@ LL | } else { LL | | a.field = true; LL | | } | |_____^ - = note: `#[deny(clippy::if_same_then_else)]` on by default + = note: `-D clippy::if-same-then-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed index c2c5f765abf..ff1e2dc8875 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.fixed +++ b/src/tools/clippy/tests/ui/needless_borrow.fixed @@ -190,27 +190,48 @@ fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; - union Coral { - crab: ManuallyDrop<Vec<i32>>, + #[derive(Clone, Copy)] + struct Wrap<T>(T); + impl<T> core::ops::Deref for Wrap<T> { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl<T> core::ops::DerefMut for Wrap<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - union Ocean { - coral: ManuallyDrop<Coral>, + union U<T: Copy> { + u: T, } - let mut ocean = Ocean { - coral: ManuallyDrop::new(Coral { - crab: ManuallyDrop::new(vec![1, 2, 3]), - }), - }; + #[derive(Clone, Copy)] + struct Foo { + x: u32, + } unsafe { - ManuallyDrop::drop(&mut (&mut ocean.coral).crab); - - (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); - ManuallyDrop::drop(&mut (*ocean.coral).crab); - - ManuallyDrop::drop(&mut ocean.coral); + let mut x = U { + u: ManuallyDrop::new(Foo { x: 0 }), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ &mut x.u }).x; + + let mut x = U { + u: Wrap(ManuallyDrop::new(Foo { x: 0 })), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut { x.u }.x; + let _ = &mut ({ &mut x.u }).x; + + let mut x = U { u: Wrap(Foo { x: 0 }) }; + let _ = &mut x.u.x; + let _ = &mut { x.u }.x; + let _ = &mut ({ &mut x.u }).x; } } diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs index 0cd6e41b8a4..597021539ac 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.rs +++ b/src/tools/clippy/tests/ui/needless_borrow.rs @@ -190,27 +190,48 @@ fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; - union Coral { - crab: ManuallyDrop<Vec<i32>>, + #[derive(Clone, Copy)] + struct Wrap<T>(T); + impl<T> core::ops::Deref for Wrap<T> { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl<T> core::ops::DerefMut for Wrap<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - union Ocean { - coral: ManuallyDrop<Coral>, + union U<T: Copy> { + u: T, } - let mut ocean = Ocean { - coral: ManuallyDrop::new(Coral { - crab: ManuallyDrop::new(vec![1, 2, 3]), - }), - }; + #[derive(Clone, Copy)] + struct Foo { + x: u32, + } unsafe { - ManuallyDrop::drop(&mut (&mut ocean.coral).crab); - - (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); - ManuallyDrop::drop(&mut (*ocean.coral).crab); - - ManuallyDrop::drop(&mut ocean.coral); + let mut x = U { + u: ManuallyDrop::new(Foo { x: 0 }), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut (&mut { x.u }).x; + let _ = &mut ({ &mut x.u }).x; + + let mut x = U { + u: Wrap(ManuallyDrop::new(Foo { x: 0 })), + }; + let _ = &mut (&mut x.u).x; + let _ = &mut (&mut { x.u }).x; + let _ = &mut ({ &mut x.u }).x; + + let mut x = U { u: Wrap(Foo { x: 0 }) }; + let _ = &mut (&mut x.u).x; + let _ = &mut (&mut { x.u }).x; + let _ = &mut ({ &mut x.u }).x; } } diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr index e91b78b0a15..44552ee6abe 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.stderr +++ b/src/tools/clippy/tests/ui/needless_borrow.stderr @@ -133,5 +133,29 @@ error: this expression borrows a value the compiler would automatically borrow LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` -error: aborting due to 22 previous errors +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:221:22 + | +LL | let _ = &mut (&mut { x.u }).x; + | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:228:22 + | +LL | let _ = &mut (&mut { x.u }).x; + | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:232:22 + | +LL | let _ = &mut (&mut x.u).x; + | ^^^^^^^^^^ help: change this to: `x.u` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:233:22 + | +LL | let _ = &mut (&mut { x.u }).x; + | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed b/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed index 52d54180921..d5932ebcf12 100644 --- a/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed +++ b/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed @@ -4,6 +4,7 @@ clippy::no_effect, clippy::unit_arg, clippy::useless_conversion, + clippy::diverging_sub_expression, unused )] @@ -35,5 +36,26 @@ fn main() -> Result<(), ()> { with_span! { return Err(())?; } + + // Issue #11765 + // Should not lint + let Some(s) = Some("") else { + return Err(())?; + }; + + let Some(s) = Some("") else { + let Some(s) = Some("") else { + return Err(())?; + }; + + return Err(())?; + }; + + let Some(_): Option<()> = ({ + return Err(())?; + }) else { + panic!(); + }; + Err(()) } diff --git a/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs b/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs index d253cae4dc2..2485e25f05c 100644 --- a/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs +++ b/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs @@ -4,6 +4,7 @@ clippy::no_effect, clippy::unit_arg, clippy::useless_conversion, + clippy::diverging_sub_expression, unused )] @@ -35,5 +36,26 @@ fn main() -> Result<(), ()> { with_span! { return Err(())?; } + + // Issue #11765 + // Should not lint + let Some(s) = Some("") else { + return Err(())?; + }; + + let Some(s) = Some("") else { + let Some(s) = Some("") else { + return Err(())?; + }; + + return Err(())?; + }; + + let Some(_): Option<()> = ({ + return Err(())?; + }) else { + panic!(); + }; + Err(()) } diff --git a/src/tools/clippy/tests/ui/needless_return_with_question_mark.stderr b/src/tools/clippy/tests/ui/needless_return_with_question_mark.stderr index 0de0633803b..580970a41aa 100644 --- a/src/tools/clippy/tests/ui/needless_return_with_question_mark.stderr +++ b/src/tools/clippy/tests/ui/needless_return_with_question_mark.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement with `?` operator - --> $DIR/needless_return_with_question_mark.rs:27:5 + --> $DIR/needless_return_with_question_mark.rs:28:5 | LL | return Err(())?; | ^^^^^^^ help: remove it diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr index b918fdf774b..26b152515ac 100644 --- a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions.stderr @@ -4,6 +4,7 @@ error: use of a fallible conversion when an infallible one could be used LL | let _: i64 = 0i32.try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: use: `into()` | + = note: converting `i32` to `i64` cannot fail = note: `-D clippy::unnecessary-fallible-conversions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fallible_conversions)]` @@ -12,6 +13,8 @@ error: use of a fallible conversion when an infallible one could be used | LL | let _: i64 = 0i32.try_into().expect("can't happen"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `into()` + | + = note: converting `i32` to `i64` cannot fail error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr index 286decf8f35..033de0e9250 100644 --- a/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_fallible_conversions_unfixable.stderr @@ -4,6 +4,7 @@ error: use of a fallible conversion when an infallible one could be used LL | let _: Result<Foo, _> = 0i64.try_into(); | ^^^^^^^^ help: use: `into` | + = note: converting `i64` to `Foo` cannot fail = note: `-D clippy::unnecessary-fallible-conversions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fallible_conversions)]` @@ -12,30 +13,40 @@ error: use of a fallible conversion when an infallible one could be used | LL | let _: Result<Foo, _> = i64::try_into(0i64); | ^^^^^^^^^^^^^ help: use: `Into::into` + | + = note: converting `i64` to `Foo` cannot fail error: use of a fallible conversion when an infallible one could be used --> $DIR/unnecessary_fallible_conversions_unfixable.rs:31:29 | LL | let _: Result<Foo, _> = Foo::try_from(0i64); | ^^^^^^^^^^^^^ help: use: `From::from` + | + = note: converting `i64` to `Foo` cannot fail error: use of a fallible conversion when an infallible one could be used --> $DIR/unnecessary_fallible_conversions_unfixable.rs:34:34 | LL | let _: Result<i64, _> = 0i32.try_into(); | ^^^^^^^^ help: use: `into` + | + = note: converting `i32` to `i64` cannot fail error: use of a fallible conversion when an infallible one could be used --> $DIR/unnecessary_fallible_conversions_unfixable.rs:36:29 | LL | let _: Result<i64, _> = i32::try_into(0i32); | ^^^^^^^^^^^^^ help: use: `Into::into` + | + = note: converting `i32` to `i64` cannot fail error: use of a fallible conversion when an infallible one could be used --> $DIR/unnecessary_fallible_conversions_unfixable.rs:38:29 | LL | let _: Result<i64, _> = <_>::try_from(0i32); | ^^^^^^^^^^^^^ help: use: `From::from` + | + = note: converting `i32` to `i64` cannot fail error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/vec_box_sized.fixed b/src/tools/clippy/tests/ui/vec_box_sized.fixed deleted file mode 100644 index 4363d2224af..00000000000 --- a/src/tools/clippy/tests/ui/vec_box_sized.fixed +++ /dev/null @@ -1,57 +0,0 @@ -#![allow(dead_code)] - -struct SizedStruct(i32); -struct UnsizedStruct([i32]); -struct BigStruct([i32; 10000]); - -/// The following should trigger the lint -mod should_trigger { - use super::SizedStruct; - const C: Vec<i32> = Vec::new(); - static S: Vec<i32> = Vec::new(); - - struct StructWithVecBox { - sized_type: Vec<SizedStruct>, - } - - struct A(Vec<SizedStruct>); - struct B(Vec<Vec<u32>>); -} - -/// The following should not trigger the lint -mod should_not_trigger { - use super::{BigStruct, UnsizedStruct}; - - struct C(Vec<Box<UnsizedStruct>>); - struct D(Vec<Box<BigStruct>>); - - struct StructWithVecBoxButItsUnsized { - unsized_type: Vec<Box<UnsizedStruct>>, - } - - struct TraitVec<T: ?Sized> { - // Regression test for #3720. This was causing an ICE. - inner: Vec<Box<T>>, - } -} - -mod inner_mod { - mod inner { - pub struct S; - } - - mod inner2 { - use super::inner::S; - - pub fn f() -> Vec<S> { - vec![] - } - } -} - -// https://github.com/rust-lang/rust-clippy/issues/11417 -fn in_closure() { - let _ = |_: Vec<Box<dyn ToString>>| {}; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/vec_box_sized.rs b/src/tools/clippy/tests/ui/vec_box_sized.rs index f4e27fe4bd5..49eaf8e062a 100644 --- a/src/tools/clippy/tests/ui/vec_box_sized.rs +++ b/src/tools/clippy/tests/ui/vec_box_sized.rs @@ -1,12 +1,28 @@ +//@no-rustfix + #![allow(dead_code)] +#![feature(allocator_api)] + +use std::alloc::{AllocError, Allocator, Layout}; +use std::ptr::NonNull; struct SizedStruct(i32); struct UnsizedStruct([i32]); struct BigStruct([i32; 10000]); +struct DummyAllocator; +unsafe impl Allocator for DummyAllocator { + fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { + todo!() + } + unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) { + todo!() + } +} + /// The following should trigger the lint mod should_trigger { - use super::SizedStruct; + use super::{DummyAllocator, SizedStruct}; const C: Vec<Box<i32>> = Vec::new(); static S: Vec<Box<i32>> = Vec::new(); @@ -16,11 +32,21 @@ mod should_trigger { struct A(Vec<Box<SizedStruct>>); struct B(Vec<Vec<Box<(u32)>>>); + + fn allocator_global_defined_vec() -> Vec<Box<i32>, std::alloc::Global> { + Vec::new() + } + fn allocator_global_defined_box() -> Vec<Box<i32, std::alloc::Global>> { + Vec::new() + } + fn allocator_match() -> Vec<Box<i32, DummyAllocator>, DummyAllocator> { + Vec::new_in(DummyAllocator) + } } /// The following should not trigger the lint mod should_not_trigger { - use super::{BigStruct, UnsizedStruct}; + use super::{BigStruct, DummyAllocator, UnsizedStruct}; struct C(Vec<Box<UnsizedStruct>>); struct D(Vec<Box<BigStruct>>); @@ -33,6 +59,13 @@ mod should_not_trigger { // Regression test for #3720. This was causing an ICE. inner: Vec<Box<T>>, } + + fn allocator_mismatch() -> Vec<Box<i32, DummyAllocator>> { + Vec::new() + } + fn allocator_mismatch_2() -> Vec<Box<i32>, DummyAllocator> { + Vec::new_in(DummyAllocator) + } } mod inner_mod { diff --git a/src/tools/clippy/tests/ui/vec_box_sized.stderr b/src/tools/clippy/tests/ui/vec_box_sized.stderr index 9118f284bb9..d6479271fa6 100644 --- a/src/tools/clippy/tests/ui/vec_box_sized.stderr +++ b/src/tools/clippy/tests/ui/vec_box_sized.stderr @@ -1,5 +1,5 @@ error: `Vec<T>` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:10:14 + --> $DIR/vec_box_sized.rs:26:14 | LL | const C: Vec<Box<i32>> = Vec::new(); | ^^^^^^^^^^^^^ help: try: `Vec<i32>` @@ -8,34 +8,52 @@ LL | const C: Vec<Box<i32>> = Vec::new(); = help: to override `-D warnings` add `#[allow(clippy::vec_box)]` error: `Vec<T>` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:11:15 + --> $DIR/vec_box_sized.rs:27:15 | LL | static S: Vec<Box<i32>> = Vec::new(); | ^^^^^^^^^^^^^ help: try: `Vec<i32>` error: `Vec<T>` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:14:21 + --> $DIR/vec_box_sized.rs:30:21 | LL | sized_type: Vec<Box<SizedStruct>>, | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` error: `Vec<T>` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:17:14 + --> $DIR/vec_box_sized.rs:33:14 | LL | struct A(Vec<Box<SizedStruct>>); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` error: `Vec<T>` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:18:18 + --> $DIR/vec_box_sized.rs:34:18 | LL | struct B(Vec<Vec<Box<(u32)>>>); | ^^^^^^^^^^^^^^^ help: try: `Vec<u32>` error: `Vec<T>` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:46:23 + --> $DIR/vec_box_sized.rs:36:42 + | +LL | fn allocator_global_defined_vec() -> Vec<Box<i32>, std::alloc::Global> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<i32>` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:39:42 + | +LL | fn allocator_global_defined_box() -> Vec<Box<i32, std::alloc::Global>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<i32>` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:42:29 + | +LL | fn allocator_match() -> Vec<Box<i32, DummyAllocator>, DummyAllocator> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<i32>` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:79:23 | LL | pub fn f() -> Vec<Box<S>> { | ^^^^^^^^^^^ help: try: `Vec<S>` -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 419b3c30deb..ab2fb1a3291 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -33,5 +33,4 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB "@dswij", "@Jarcho", "@blyxyas", - "@Centri3", ] diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 1e9684555f1..e7d2e1aab3a 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -410,9 +410,6 @@ impl Config { pub fn matches_arch(&self, arch: &str) -> bool { self.target_cfg().arch == arch || - // Shorthand for convenience. The arch for - // asmjs-unknown-emscripten is actually wasm32. - (arch == "asmjs" && self.target.starts_with("asmjs")) || // Matching all the thumb variants as one can be convenient. // (thumbv6m, thumbv7em, thumbv7m, etc.) (arch == "thumb" && self.target.starts_with("thumb")) diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs index 77c2866b366..3a1b9dff3a6 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -146,8 +146,7 @@ pub(super) fn parse_cfg_name_directive<'a>( } // `wasm32-bare` is an alias to refer to just wasm32-unknown-unknown - // (in contrast to `wasm32` which also matches non-bare targets like - // asmjs-unknown-emscripten). + // (in contrast to `wasm32` which also matches non-bare targets) condition! { name: "wasm32-bare", condition: config.target == "wasm32-unknown-unknown", @@ -155,11 +154,6 @@ pub(super) fn parse_cfg_name_directive<'a>( } condition! { - name: "asmjs", - condition: config.target.starts_with("asmjs"), - message: "when the architecture is asm.js", - } - condition! { name: "thumb", condition: config.target.starts_with("thumb"), message: "when the architecture is part of the Thumb family" diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 85e745bed11..295134c78dc 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -398,8 +398,6 @@ fn ignore_arch() { ("x86_64-unknown-linux-gnu", "x86_64"), ("i686-unknown-linux-gnu", "x86"), ("nvptx64-nvidia-cuda", "nvptx64"), - ("asmjs-unknown-emscripten", "wasm32"), - ("asmjs-unknown-emscripten", "asmjs"), ("thumbv7m-none-eabi", "thumb"), ]; for (target, arch) in archs { @@ -492,9 +490,6 @@ fn wasm_special() { ("wasm32-unknown-unknown", "wasm32", true), ("wasm32-unknown-unknown", "wasm32-bare", true), ("wasm32-unknown-unknown", "wasm64", false), - ("asmjs-unknown-emscripten", "emscripten", true), - ("asmjs-unknown-emscripten", "wasm32", true), - ("asmjs-unknown-emscripten", "wasm32-bare", false), ("wasm32-unknown-emscripten", "emscripten", true), ("wasm32-unknown-emscripten", "wasm32", true), ("wasm32-unknown-emscripten", "wasm32-bare", false), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8854908c430..10a11e8e291 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3701,9 +3701,7 @@ impl<'test> TestCx<'test> { let stderr_bits = format!("{}bit.stderr", self.config.get_pointer_width()); let (stderr_kind, stdout_kind) = match output_kind { TestOutput::Compile => ( - { - if self.props.stderr_per_bitwidth { &stderr_bits } else { UI_STDERR } - }, + { if self.props.stderr_per_bitwidth { &stderr_bits } else { UI_STDERR } }, UI_STDOUT, ), TestOutput::Run => (UI_RUN_STDERR, UI_RUN_STDOUT), diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 62a58576da5..3a23ff7fe6a 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -1,6 +1,7 @@ #![feature(rustc_private)] extern crate rustc_driver; +extern crate rustc_log; extern crate rustc_session; use std::env; @@ -173,7 +174,7 @@ fn parse_args() -> (OutputFormat, PathBuf) { fn main() { let handler = rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default()); - rustc_driver::init_env_logger(&handler, "RUST_LOG"); + rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUST_LOG")); let (format, dst) = parse_args(); let result = main_with_result(format, &dst); if let Err(e) = result { diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index ccabe6483d7..6326a9b1e79 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -jsonpath_lib = "0.2" +jsonpath_lib = "0.3" getopts = "0.2" regex = "1.4" shlex = "1.0" diff --git a/src/tools/miri/ci.sh b/src/tools/miri/ci.sh index f0917556c64..7808c9acf93 100755 --- a/src/tools/miri/ci.sh +++ b/src/tools/miri/ci.sh @@ -108,7 +108,7 @@ case $HOST_TARGET in MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthreads atomic env/var + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc atomic env align MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2beb3bfef9e..2a19781775b 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3aaa0f57b7b877ef58532a8de075d1e5a79142bf +820f06b21f8373060ff7b515715b8440a6a6c197 diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index cd628444fed..7f777cd4727 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -9,11 +9,12 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; extern crate rustc_interface; +extern crate rustc_log; extern crate rustc_metadata; extern crate rustc_middle; extern crate rustc_session; -use std::env; +use std::env::{self, VarError}; use std::num::NonZeroU64; use std::path::PathBuf; use std::str::FromStr; @@ -183,45 +184,54 @@ macro_rules! show_error { ($($tt:tt)*) => { show_error(&format_args!($($tt)*)) }; } -fn init_early_loggers(handler: &EarlyErrorHandler) { - // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to - // initialize them both, and we always initialize `miri`'s first. - let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE"); - env_logger::init_from_env(env); - // Enable verbose entry/exit logging by default if MIRI_LOG is set. - if env::var_os("MIRI_LOG").is_some() && env::var_os("RUSTC_LOG_ENTRY_EXIT").is_none() { - env::set_var("RUSTC_LOG_ENTRY_EXIT", "1"); - } - // We only initialize `rustc` if the env var is set (so the user asked for it). - // If it is not set, we avoid initializing now so that we can initialize - // later with our custom settings, and *not* log anything for what happens before - // `miri` gets started. - if env::var_os("RUSTC_LOG").is_some() { - rustc_driver::init_rustc_env_logger(handler); - } -} +fn rustc_logger_config() -> rustc_log::LoggerConfig { + // Start with the usual env vars. + let mut cfg = rustc_log::LoggerConfig::from_env("RUSTC_LOG"); -fn init_late_loggers(handler: &EarlyErrorHandler, tcx: TyCtxt<'_>) { - // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG` - // env var if it is not set, control it based on `MIRI_LOG`. - // (FIXME: use `var_os`, but then we need to manually concatenate instead of `format!`.) + // Overwrite if MIRI_LOG is set. if let Ok(var) = env::var("MIRI_LOG") { - if env::var_os("RUSTC_LOG").is_none() { + // MIRI_LOG serves as default for RUSTC_LOG, if that is not set. + if matches!(cfg.filter, Err(VarError::NotPresent)) { // We try to be a bit clever here: if `MIRI_LOG` is just a single level // used for everything, we only apply it to the parts of rustc that are // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`. // This way, if you set `MIRI_LOG=trace`, you get only the right parts of // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_const_eval::interpret=debug`. if log::Level::from_str(&var).is_ok() { - env::set_var( - "RUSTC_LOG", - format!("rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}"), - ); + cfg.filter = Ok(format!( + "rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}" + )); } else { - env::set_var("RUSTC_LOG", &var); + cfg.filter = Ok(var); } - rustc_driver::init_rustc_env_logger(handler); } + // Enable verbose entry/exit logging by default if MIRI_LOG is set. + if matches!(cfg.verbose_entry_exit, Err(VarError::NotPresent)) { + cfg.verbose_entry_exit = Ok(format!("1")); + } + } + + cfg +} + +fn init_early_loggers(handler: &EarlyErrorHandler) { + // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to + // initialize them both, and we always initialize `miri`'s first. + let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE"); + env_logger::init_from_env(env); + // Now for rustc. We only initialize `rustc` if the env var is set (so the user asked for it). + // If it is not set, we avoid initializing now so that we can initialize later with our custom + // settings, and *not* log anything for what happens before `miri` gets started. + if env::var_os("RUSTC_LOG").is_some() { + rustc_driver::init_logger(handler, rustc_logger_config()); + } +} + +fn init_late_loggers(handler: &EarlyErrorHandler, tcx: TyCtxt<'_>) { + // If `RUSTC_LOG` is not set, then `init_early_loggers` did not call + // `rustc_driver::init_logger`, so we have to do this now. + if env::var_os("RUSTC_LOG").is_none() { + rustc_driver::init_logger(handler, rustc_logger_config()); } // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`. diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index a95571572d6..f9cc3acbd51 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -59,7 +59,7 @@ impl fmt::Debug for BorTag { #[derive(Debug)] pub struct FrameState { /// The ID of the call this frame corresponds to. - pub call_id: CallId, + call_id: CallId, /// If this frame is protecting any tags, they are listed here. We use this list to do /// incremental updates of the global list of protected tags stored in the @@ -72,7 +72,7 @@ pub struct FrameState { /// /// This will contain one tag per reference passed to the function, so /// a size of 2 is enough for the vast majority of functions. - pub protected_tags: SmallVec<[(AllocId, BorTag); 2]>, + protected_tags: SmallVec<[(AllocId, BorTag); 2]>, } impl VisitTags for FrameState { @@ -85,29 +85,29 @@ impl VisitTags for FrameState { #[derive(Debug)] pub struct GlobalStateInner { /// Borrow tracker method currently in use. - pub borrow_tracker_method: BorrowTrackerMethod, + borrow_tracker_method: BorrowTrackerMethod, /// Next unused pointer ID (tag). - pub next_ptr_tag: BorTag, + next_ptr_tag: BorTag, /// Table storing the "base" tag for each allocation. /// The base tag is the one used for the initial pointer. /// We need this in a separate table to handle cyclic statics. - pub base_ptr_tags: FxHashMap<AllocId, BorTag>, + base_ptr_tags: FxHashMap<AllocId, BorTag>, /// Next unused call ID (for protectors). - pub next_call_id: CallId, + next_call_id: CallId, /// All currently protected tags. /// An item is protected if its tag is in this set, *and* it has the "protected" bit set. /// We add tags to this when they are created with a protector in `reborrow`, and /// we remove tags from this when the call which is protecting them returns, in /// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details. - pub protected_tags: FxHashMap<BorTag, ProtectorKind>, + protected_tags: FxHashMap<BorTag, ProtectorKind>, /// The pointer ids to trace - pub tracked_pointer_tags: FxHashSet<BorTag>, + tracked_pointer_tags: FxHashSet<BorTag>, /// The call ids to trace - pub tracked_call_ids: FxHashSet<CallId>, + tracked_call_ids: FxHashSet<CallId>, /// Whether to recurse into datatypes when searching for pointers to retag. - pub retag_fields: RetagFields, + retag_fields: RetagFields, /// Whether `core::ptr::Unique` gets special (`Box`-like) handling. - pub unique_is_unique: bool, + unique_is_unique: bool, } impl VisitTags for GlobalStateInner { @@ -194,7 +194,7 @@ impl GlobalStateInner { } /// Generates a new pointer tag. Remember to also check track_pointer_tags and log its creation! - pub fn new_ptr(&mut self) -> BorTag { + fn new_ptr(&mut self) -> BorTag { let id = self.next_ptr_tag; self.next_ptr_tag = id.succ().unwrap(); id @@ -210,7 +210,7 @@ impl GlobalStateInner { FrameState { call_id, protected_tags: SmallVec::new() } } - pub fn end_call(&mut self, frame: &machine::FrameExtra<'_>) { + fn end_call(&mut self, frame: &machine::FrameExtra<'_>) { for (_, tag) in &frame .borrow_tracker .as_ref() @@ -355,6 +355,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { BorrowTrackerMethod::TreeBorrows => this.print_tree(alloc_id, show_unnamed), } } + + fn on_stack_pop( + &self, + frame: &Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_ref(); + let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap(); + // The body of this loop needs `borrow_tracker` immutably + // so we can't move this code inside the following `end_call`. + for (alloc_id, tag) in &frame + .extra + .borrow_tracker + .as_ref() + .expect("we should have borrow tracking data") + .protected_tags + { + // Just because the tag is protected doesn't guarantee that + // the allocation still exists (weak protectors allow deallocations) + // so we must check that the allocation exists. + // If it does exist, then we have the guarantee that the + // pointer is readable, and the implicit read access inserted + // will never cause UB on the pointer itself. + let (_, _, kind) = this.get_alloc_info(*alloc_id); + if matches!(kind, AllocKind::LiveData) { + let alloc_extra = this.get_alloc_extra(*alloc_id).unwrap(); + let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap(); + alloc_borrow_tracker.release_protector(&this.machine, borrow_tracker, *tag)?; + } + } + borrow_tracker.borrow_mut().end_call(&frame.extra); + Ok(()) + } } /// Extra per-allocation data for borrow tracking diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index d5775912eab..2085df7d06f 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1409,34 +1409,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { ) -> InterpResult<'tcx> { // We want this *before* the return value copy, because the return place itself is protected // until we do `end_call` here. - if let Some(global_borrow_tracker) = &ecx.machine.borrow_tracker { - // The body of this loop needs `global_borrow_tracker` immutably - // so we can't move this code inside the following `end_call`. - for (alloc_id, tag) in &frame - .extra - .borrow_tracker - .as_ref() - .expect("we should have borrow tracking data") - .protected_tags - { - // Just because the tag is protected doesn't guarantee that - // the allocation still exists (weak protectors allow deallocations) - // so we must check that the allocation exists. - // If it does exist, then we have the guarantee that the - // pointer is readable, and the implicit read access inserted - // will never cause UB on the pointer itself. - let (_, _, kind) = ecx.get_alloc_info(*alloc_id); - if matches!(kind, AllocKind::LiveData) { - let alloc_extra = ecx.get_alloc_extra(*alloc_id).unwrap(); - let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap(); - alloc_borrow_tracker.release_protector( - &ecx.machine, - global_borrow_tracker, - *tag, - )?; - } - } - global_borrow_tracker.borrow_mut().end_call(&frame.extra); + if ecx.machine.borrow_tracker.is_some() { + ecx.on_stack_pop(frame)?; } Ok(()) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 2d5df303745..329a30a9faf 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -345,7 +345,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // List taken from `library/std/src/sys/common/alloc.rs`. // This list should be kept in sync with the one from libstd. let min_align = match this.tcx.sess.target.arch.as_ref() { - "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "asmjs" | "wasm32" => 8, + "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => 16, arch => bug!("unsupported target architecture for malloc: `{}`", arch), diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index c013d275029..d155623eb7b 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -27,6 +27,8 @@ fn is_dyn_sym(name: &str, target_os: &str) -> bool { // `signal` is set up as a weak symbol in `init_extern_statics` (on Android) so we might as // well allow it in `dlsym`. "signal" => true, + // needed at least on macOS to avoid file-based fallback in getrandom + "getentropy" => true, // Give specific OSes a chance to allow their symbols. _ => match target_os { @@ -250,6 +252,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(result, dest)?; } + "reallocarray" => { + // Currently this function does not exist on all Unixes, e.g. on macOS. + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd") { + throw_unsup_format!( + "`reallocarray` is not supported on {}", + this.tcx.sess.target.os + ); + } + let [ptr, nmemb, size] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let nmemb = this.read_target_usize(nmemb)?; + let size = this.read_target_usize(size)?; + // reallocarray checks a possible overflow and returns ENOMEM + // if that happens. + // + // Linux: https://www.unix.com/man-page/linux/3/reallocarray/ + // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray + match nmemb.checked_mul(size) { + None => { + let einval = this.eval_libc("ENOMEM"); + this.set_last_error(einval)?; + this.write_null(dest)?; + } + Some(len) => { + let res = this.realloc(ptr, len, MiriMemoryKind::C)?; + this.write_pointer(res, dest)?; + } + } + } + // Dynamic symbol loading "dlsym" => { let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -525,6 +558,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.getpid()?; this.write_scalar(Scalar::from_i32(result), dest)?; } + "getentropy" => { + // This function is non-standard but exists with the same signature and behavior on + // Linux, macOS, and FreeBSD. + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd") { + throw_unsup_format!( + "`getentropy` is not supported on {}", + this.tcx.sess.target.os + ); + } + + let [buf, bufsize] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let buf = this.read_pointer(buf)?; + let bufsize = this.read_target_usize(bufsize)?; + + // getentropy sets errno to EIO when the buffer size exceeds 256 bytes. + // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html + // Linux: https://man7.org/linux/man-pages/man3/getentropy.3.html + // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html + if bufsize > 256 { + let err = this.eval_libc("EIO"); + this.set_last_error(err)?; + this.write_scalar(Scalar::from_i32(-1), dest)? + } else { + this.gen_random(buf, bufsize)?; + this.write_scalar(Scalar::from_i32(0), dest)?; + } + } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. @@ -594,7 +655,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_int(super::UID, dest)?; } - "getpwuid_r" if this.frame_in_std() => { + "getpwuid_r" + if this.frame_in_std() => { let [uid, pwd, buf, buflen, result] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 96e322c4cf5..7c843e106ea 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -47,6 +47,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.read_scalar(len)?, )?; } + "getrandom" => { + let [ptr, len, flags] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let len = this.read_target_usize(len)?; + let _flags = this.read_scalar(flags)?.to_i32()?; + // flags on freebsd does not really matter + // in practice, GRND_RANDOM does not particularly draw from /dev/random + // since it is the same as to /dev/urandom. + // GRND_INSECURE is only an alias of GRND_NONBLOCK, which + // does not affect the RNG. + // https://man.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&n=1 + this.gen_random(ptr, len)?; + this.write_scalar(Scalar::from_target_usize(len, this), dest)?; + } // errno "__error" => { diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 5881a3f46f2..e8f35e7ba57 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -6,8 +6,8 @@ use shims::foreign_items::EmulateForeignItemResult; use shims::unix::fs::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _; -pub fn is_dyn_sym(name: &str) -> bool { - matches!(name, "getentropy") +pub fn is_dyn_sym(_name: &str) -> bool { + false } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -113,18 +113,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(result, dest)?; } - // Random generation related shims - "getentropy" => { - let [buf, bufsize] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let buf = this.read_pointer(buf)?; - let bufsize = this.read_target_usize(bufsize)?; - - this.gen_random(buf, bufsize)?; - - this.write_scalar(Scalar::from_i32(0), dest)?; // KERN_SUCCESS - } - // Access to command-line arguments "_NSGetArgc" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index 670f5c895cb..d54560608dc 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -12,8 +12,8 @@ edition = "2021" libc = "0.2" num_cpus = "1.10.1" -getrandom_1 = { package = "getrandom", version = "0.1" } -getrandom = { version = "0.2", features = ["js"] } +getrandom_01 = { package = "getrandom", version = "0.1" } +getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } rand = { version = "0.8", features = ["small_rng"] } [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies] diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index dbf559631ea..91df4985c0f 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -79,6 +79,11 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> program.args.push(flag); } + // Add a test env var to do environment communication tests. + program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into()))); + // Let the tests know where to store temp files (they might run for a different target, which can make this hard to find). + program.envs.push(("MIRI_TEMP".into(), Some(env::temp_dir().into()))); + let mut config = Config { target: Some(target.to_owned()), stderr_filters: STDERR.clone(), @@ -232,11 +237,6 @@ fn main() -> Result<()> { } } - // Add a test env var to do environment communication tests. - env::set_var("MIRI_ENV_VAR_TEST", "0"); - // Let the tests know where to store temp files (they might run for a different target, which can make this hard to find). - env::set_var("MIRI_TEMP", env::temp_dir()); - ui(Mode::Pass, "tests/pass", &target, WithoutDependencies)?; ui(Mode::Pass, "tests/pass-dep", &target, WithDependencies)?; ui(Mode::Panic, "tests/panic", &target, WithDependencies)?; diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs index d47af50d407..e79bd70e915 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.rs @@ -14,7 +14,7 @@ fn main() { let ptr = std::ptr::addr_of_mut!(non_copy); // Inside `callee`, the first argument and `*ptr` are basically // aliasing places! - Call(_unit = callee(Move(*ptr), ptr), after_call) + Call(_unit = callee(Move(*ptr), ptr), after_call, UnwindContinue()) } after_call = { Return() diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr index 381442e69b1..ccf9732ed07 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.stack.stderr @@ -27,8 +27,8 @@ LL | unsafe { ptr.write(S(0)) }; note: inside `main` --> $DIR/arg_inplace_mutate.rs:LL:CC | -LL | Call(_unit = callee(Move(*ptr), ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = callee(Move(*ptr), ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr index 3d8ba68547b..e7baf6e23db 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr @@ -35,8 +35,8 @@ LL | unsafe { ptr.write(S(0)) }; note: inside `main` --> $DIR/arg_inplace_mutate.rs:LL:CC | -LL | Call(_unit = callee(Move(*ptr), ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = callee(Move(*ptr), ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs index ea773048dd4..e4c00fdd845 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.rs @@ -11,7 +11,7 @@ fn main() { { let non_copy = S(42); // This could change `non_copy` in-place - Call(_unit = change_arg(Move(non_copy)), after_call) + Call(_unit = change_arg(Move(non_copy)), after_call, UnwindContinue()) } after_call = { // So now we must not be allowed to observe non-copy again. diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr index cba23c21d12..f8140d0236a 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr @@ -11,8 +11,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/arg_inplace_observe_during.rs:LL:CC | -LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs index 8c6a7df7a6d..517abd733a9 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.rs @@ -14,7 +14,7 @@ fn main() { let non_copy = S(42); let ptr = std::ptr::addr_of_mut!(non_copy); // This could change `non_copy` in-place - Call(_unit = change_arg(Move(*ptr), ptr), after_call) + Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue()) } after_call = { Return() diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr index f8532186be2..c37e788e6b4 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.stack.stderr @@ -27,8 +27,8 @@ LL | x.0 = 0; note: inside `main` --> $DIR/arg_inplace_observe_during.rs:LL:CC | -LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr index 7b1846a32db..7557d3710d1 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr @@ -35,8 +35,8 @@ LL | x.0 = 0; note: inside `main` --> $DIR/arg_inplace_observe_during.rs:LL:CC | -LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr index 0a31adabf73..dd951066c32 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.none.stderr @@ -11,8 +11,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/return_pointer_aliasing.rs:LL:CC | -LL | Call(*ptr = myfun(ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(*ptr = myfun(ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs index 3d560af3d5e..23b1e38b99f 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.rs @@ -15,7 +15,7 @@ pub fn main() { let ptr = &raw mut x; // We arrange for `myfun` to have a pointer that aliases // its return place. Even just reading from that pointer is UB. - Call(*ptr = myfun(ptr), after_call) + Call(*ptr = myfun(ptr), after_call, UnwindContinue()) } after_call = { diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr index 875cc5edad9..cf13be6da01 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.stack.stderr @@ -27,8 +27,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/return_pointer_aliasing.rs:LL:CC | -LL | Call(*ptr = myfun(ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(*ptr = myfun(ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr index deafbf02077..e16c4c0ebb6 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing.tree.stderr @@ -35,8 +35,8 @@ LL | unsafe { ptr.read() }; note: inside `main` --> $DIR/return_pointer_aliasing.rs:LL:CC | -LL | Call(*ptr = myfun(ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(*ptr = myfun(ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs index c1bbc748e1a..56706cdb63b 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs @@ -15,7 +15,7 @@ pub fn main() { let ptr = &raw mut _x; // We arrange for `myfun` to have a pointer that aliases // its return place. Even just reading from that pointer is UB. - Call(_x = myfun(ptr), after_call) + Call(_x = myfun(ptr), after_call, UnwindContinue()) } after_call = { diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stack.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stack.stderr index 0666db34fec..5d76d9eab67 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stack.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.stack.stderr @@ -30,8 +30,8 @@ LL | unsafe { ptr.write(0) }; note: inside `main` --> $DIR/return_pointer_aliasing2.rs:LL:CC | -LL | Call(_x = myfun(ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_x = myfun(ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.tree.stderr index e1b40a6bc18..e8165a73ff4 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.tree.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.tree.stderr @@ -35,8 +35,8 @@ LL | unsafe { ptr.write(0) }; note: inside `main` --> $DIR/return_pointer_aliasing2.rs:LL:CC | -LL | Call(_x = myfun(ptr), after_call) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Call(_x = myfun(ptr), after_call, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.rs index 79e29b79d6a..923c59e7429 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.rs +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.rs @@ -14,7 +14,7 @@ struct S(i32, [u8; 128]); fn docall(out: &mut S) { mir! { { - Call(*out = callee(), after_call) + Call(*out = callee(), after_call, UnwindContinue()) } after_call = { @@ -37,7 +37,7 @@ fn callee() -> S { // become visible to the outside. In codegen we can see them // but Miri should detect this as UB! RET.0 = 42; - Call(_unit = startpanic(), after_call) + Call(_unit = startpanic(), after_call, UnwindContinue()) } after_call = { diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs index ee80186d4b5..9357b372505 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs @@ -20,7 +20,7 @@ fn call(f: fn(NonZeroU32)) { let tmp = ptr::addr_of!(c); let ptr = tmp as *const NonZeroU32; // The call site now is a NonZeroU32-to-u32 transmute. - Call(_res = f(*ptr), retblock) //~ERROR: expected something greater or equal to 1 + Call(_res = f(*ptr), retblock, UnwindContinue()) //~ERROR: expected something greater or equal to 1 } retblock = { Return() diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr index 234c2804008..b40d99f7bc9 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: constructing invalid value: encountered 0, but expected something greater or equal to 1 --> $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC | -LL | Call(_res = f(*ptr), retblock) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 +LL | Call(_res = f(*ptr), retblock, UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 | = 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-dep/getrandom.rs b/src/tools/miri/tests/pass-dep/getrandom.rs new file mode 100644 index 00000000000..c0d9296a9a6 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/getrandom.rs @@ -0,0 +1,10 @@ +// mac-os `getrandom_01` does some pointer shenanigans +//@compile-flags: -Zmiri-permissive-provenance + +/// Test direct calls of getrandom 0.1 and 0.2. +/// Make sure they work even with isolation enabled (i.e., we do not hit a file-based fallback path). +fn main() { + let mut data = vec![0; 16]; + getrandom_01::getrandom(&mut data).unwrap(); + getrandom_02::getrandom(&mut data).unwrap(); +} diff --git a/src/tools/miri/tests/pass-dep/getrandom_1.rs b/src/tools/miri/tests/pass-dep/getrandom_1.rs deleted file mode 100644 index 2c7bd93fbdb..00000000000 --- a/src/tools/miri/tests/pass-dep/getrandom_1.rs +++ /dev/null @@ -1,8 +0,0 @@ -// mac-os `getrandom_1` does some pointer shenanigans -//@compile-flags: -Zmiri-permissive-provenance - -/// Test old version of `getrandom`. -fn main() { - let mut data = vec![0; 16]; - getrandom_1::getrandom(&mut data).unwrap(); -} diff --git a/src/tools/miri/tests/pass-dep/random.rs b/src/tools/miri/tests/pass-dep/rand.rs index 0cd8b06d63d..0dce6d86cf4 100644 --- a/src/tools/miri/tests/pass-dep/random.rs +++ b/src/tools/miri/tests/pass-dep/rand.rs @@ -1,10 +1,13 @@ //@compile-flags: -Zmiri-strict-provenance -use rand::{rngs::SmallRng, Rng, SeedableRng}; +use rand::prelude::*; +// Test using the `rand` crate to generate randomness. fn main() { - // Test `getrandom` directly. - let mut data = vec![0; 16]; - getrandom::getrandom(&mut data).unwrap(); + // Fully deterministic seeding. + let mut rng = SmallRng::seed_from_u64(42); + let _val = rng.gen::<i32>(); + let _val = rng.gen::<isize>(); + let _val = rng.gen::<i128>(); // Try seeding with "real" entropy. let mut rng = SmallRng::from_entropy(); diff --git a/src/tools/miri/tests/pass-dep/shims/libc-getentropy.rs b/src/tools/miri/tests/pass-dep/shims/libc-getentropy.rs new file mode 100644 index 00000000000..4b863f68516 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/libc-getentropy.rs @@ -0,0 +1,20 @@ +//@ignore-target-windows: no libc + +// on macOS this is not in the `libc` crate. +#[cfg(target_os = "macos")] +extern "C" { + fn getentropy(bytes: *mut libc::c_void, count: libc::size_t) -> libc::c_int; +} + +#[cfg(not(target_os = "macos"))] +use libc::getentropy; + +fn main() { + let mut buf1 = [0u8; 256]; + let mut buf2 = [0u8; 257]; + unsafe { + assert_eq!(getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0); + assert_eq!(getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::EIO); + } +} diff --git a/src/tools/miri/tests/pass-dep/shims/libc-getrandom.rs b/src/tools/miri/tests/pass-dep/shims/libc-getrandom.rs index a1436c7319d..9c670cbd507 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-getrandom.rs +++ b/src/tools/miri/tests/pass-dep/shims/libc-getrandom.rs @@ -1,10 +1,12 @@ -//@only-target-linux +//@ignore-target-windows: no libc +//@ignore-target-apple: no getrandom use std::ptr; fn main() { let mut buf = [0u8; 5]; unsafe { + #[cfg(target_os = "linux")] assert_eq!( libc::syscall( libc::SYS_getrandom, @@ -14,6 +16,7 @@ fn main() { ), 0, ); + #[cfg(target_os = "linux")] assert_eq!( libc::syscall( libc::SYS_getrandom, diff --git a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs index 40d3fa19e53..de1acb13cbe 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs @@ -172,6 +172,7 @@ fn test_thread_local_errno() { } /// Tests whether clock support exists at all +#[cfg(not(target_os = "freebsd"))] fn test_clocks() { let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit(); let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) }; @@ -237,6 +238,7 @@ fn test_isatty() { } } +#[cfg(not(target_os = "freebsd"))] fn test_posix_mkstemp() { use std::ffi::CString; use std::ffi::OsStr; @@ -388,8 +390,23 @@ fn test_dlsym() { assert_eq!(errno, libc::EBADF); } +#[cfg(not(target_os = "macos"))] +fn test_reallocarray() { + unsafe { + let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); + assert!(!p.is_null()); + libc::free(p); + p = libc::malloc(16); + let r = libc::reallocarray(p, 2, 32); + assert!(!r.is_null()); + libc::free(r); + } +} + fn main() { test_posix_gettimeofday(); + + #[cfg(not(target_os = "freebsd"))] // FIXME we should support this on FreeBSD as well test_posix_mkstemp(); test_posix_realpath_alloc(); @@ -399,12 +416,19 @@ fn main() { test_thread_local_errno(); test_isatty(); + + #[cfg(not(target_os = "freebsd"))] // FIXME we should support this on FreeBSD as well test_clocks(); + test_dlsym(); test_memcpy(); test_strcpy(); + #[cfg(not(target_os = "macos"))] // reallocarray does not exist on macOS + test_reallocarray(); + + // These are Linux-specific #[cfg(target_os = "linux")] { test_posix_fadvise(); diff --git a/src/tools/miri/tests/pass-dep/shims/pthreads.rs b/src/tools/miri/tests/pass-dep/shims/pthread-sync.rs index 3bb6f83ec2a..4cc5b7d68a3 100644 --- a/src/tools/miri/tests/pass-dep/shims/pthreads.rs +++ b/src/tools/miri/tests/pass-dep/shims/pthread-sync.rs @@ -1,26 +1,15 @@ //@ignore-target-windows: No libc on Windows -use std::ffi::CStr; -#[cfg(not(target_os = "freebsd"))] -use std::ffi::CString; -use std::thread; fn main() { - test_named_thread_truncation(); - - #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_recursive(); - #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_normal(); - #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_errorcheck(); - #[cfg(not(target_os = "freebsd"))] test_rwlock_libc_static_initializer(); #[cfg(target_os = "linux")] test_mutex_libc_static_initializer_recursive(); } -#[cfg(not(target_os = "freebsd"))] fn test_mutex_libc_init_recursive() { unsafe { let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -45,7 +34,6 @@ fn test_mutex_libc_init_recursive() { } } -#[cfg(not(target_os = "freebsd"))] fn test_mutex_libc_init_normal() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -68,7 +56,6 @@ fn test_mutex_libc_init_normal() { } } -#[cfg(not(target_os = "freebsd"))] fn test_mutex_libc_init_errorcheck() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -114,7 +101,6 @@ fn test_mutex_libc_static_initializer_recursive() { // Testing the behavior of std::sync::RwLock does not fully exercise the pthread rwlock shims, we // need to go a layer deeper and test the behavior of the libc functions, because // std::sys::unix::rwlock::RWLock itself keeps track of write_locked and num_readers. -#[cfg(not(target_os = "freebsd"))] fn test_rwlock_libc_static_initializer() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { @@ -139,49 +125,3 @@ fn test_rwlock_libc_static_initializer() { assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0); } } - -fn test_named_thread_truncation() { - let long_name = std::iter::once("test_named_thread_truncation") - .chain(std::iter::repeat(" yada").take(100)) - .collect::<String>(); - - fn set_thread_name(name: &CStr) -> i32 { - #[cfg(target_os = "linux")] - return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }; - #[cfg(target_os = "freebsd")] - unsafe { - // pthread_set_name_np does not return anything - libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast()); - return 0; - }; - #[cfg(target_os = "macos")] - return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) }; - } - - let result = thread::Builder::new().name(long_name.clone()).spawn(move || { - // Rust remembers the full thread name itself. - assert_eq!(thread::current().name(), Some(long_name.as_str())); - - // But the system is limited -- make sure we successfully set a truncation. - let mut buf = vec![0u8; long_name.len() + 1]; - #[cfg(not(target_os = "freebsd"))] - unsafe { - libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) - }; - #[cfg(target_os = "freebsd")] - unsafe { - libc::pthread_get_name_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) - }; - let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); - assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars - assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); - - // Also test directly calling pthread_setname to check its return value. - assert_eq!(set_thread_name(&cstr), 0); - // But with a too long name it should fail (except on FreeBSD where the - // function has no return, hence cannot indicate failure). - #[cfg(not(target_os = "freebsd"))] - assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0); - }); - result.unwrap().join().unwrap(); -} diff --git a/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs new file mode 100644 index 00000000000..bc782044d4d --- /dev/null +++ b/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs @@ -0,0 +1,51 @@ +//@ignore-target-windows: No libc on Windows +use std::ffi::CStr; +#[cfg(not(target_os = "freebsd"))] +use std::ffi::CString; +use std::thread; + +fn main() { + let long_name = std::iter::once("test_named_thread_truncation") + .chain(std::iter::repeat(" yada").take(100)) + .collect::<String>(); + + fn set_thread_name(name: &CStr) -> i32 { + #[cfg(target_os = "linux")] + return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }; + #[cfg(target_os = "freebsd")] + unsafe { + // pthread_set_name_np does not return anything + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast()); + return 0; + }; + #[cfg(target_os = "macos")] + return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) }; + } + + let result = thread::Builder::new().name(long_name.clone()).spawn(move || { + // Rust remembers the full thread name itself. + assert_eq!(thread::current().name(), Some(long_name.as_str())); + + // But the system is limited -- make sure we successfully set a truncation. + let mut buf = vec![0u8; long_name.len() + 1]; + #[cfg(not(target_os = "freebsd"))] + unsafe { + libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) + }; + #[cfg(target_os = "freebsd")] + unsafe { + libc::pthread_get_name_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) + }; + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); + assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars + assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); + + // Also test directly calling pthread_setname to check its return value. + assert_eq!(set_thread_name(&cstr), 0); + // But with a too long name it should fail (except on FreeBSD where the + // function has no return, hence cannot indicate failure). + #[cfg(not(target_os = "freebsd"))] + assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0); + }); + result.unwrap().join().unwrap(); +} diff --git a/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs b/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs index d410a875b1b..89ee689fabe 100644 --- a/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs +++ b/src/tools/miri/tests/pass/function_calls/return_place_on_heap.rs @@ -11,7 +11,7 @@ pub fn main() { { let x = 0; let ptr = &raw mut x; - Call(*ptr = myfun(), after_call) + Call(*ptr = myfun(), after_call, UnwindContinue()) } after_call = { diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 310a8a5be7a..9a5015005b3 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -19,6 +19,10 @@ jobs: with: fetch-depth: 0 + # https://github.com/jlumbroso/free-disk-space/blob/main/action.yml + - name: Free up some disk space + run: sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /usr/local/.ghcup + - name: Install Rust toolchain run: rustup update --no-self-update stable diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index fb7b4b07f98..1f2a7796d11 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -8,6 +8,7 @@ on: branches: - auto - try + - automation/bors/try env: CARGO_INCREMENTAL: 0 @@ -84,6 +85,7 @@ jobs: run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }} - name: Test + if: matrix.os == 'ubuntu-latest' || github.event_name == 'push' run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet - name: Switch to stable toolchain diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml index bbeccd1621d..741e559953f 100644 --- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml +++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml @@ -140,11 +140,14 @@ jobs: - name: Combine json run: | - git clone --depth 1 https://$METRICS_TOKEN@github.com/rust-analyzer/metrics.git + mkdir ~/.ssh + echo "${{ secrets.METRICS_DEPLOY_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + chmod 700 ~/.ssh + + git clone --depth 1 git@github.com:rust-analyzer/metrics.git jq -s ".[0] * .[1] * .[2] * .[3] * .[4] * .[5]" build.json self.json ripgrep-13.0.0.json webrender-2022.json diesel-1.4.8.json hyper-0.14.18.json -c >> metrics/metrics.json cd metrics git add . git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈 git push origin master - env: - METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }} diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index b5dbe30c322..6a3cdfe3a3c 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -30,7 +30,6 @@ jobs: code-target: win32-x64 - os: windows-latest target: i686-pc-windows-msvc - code-target: win32-ia32 - os: windows-latest target: aarch64-pc-windows-msvc code-target: win32-arm64 @@ -102,12 +101,12 @@ jobs: working-directory: editors/code - name: Package Extension (release) - if: github.ref == 'refs/heads/release' + if: github.ref == 'refs/heads/release' && matrix.code-target run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} working-directory: editors/code - name: Package Extension (nightly) - if: github.ref != 'refs/heads/release' + if: github.ref != 'refs/heads/release' && matrix.code-target run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release working-directory: editors/code diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index bd6554bf889..701e36d74a6 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -28,21 +28,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "anymap" -version = "1.0.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arbitrary" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "arrayvec" @@ -51,17 +45,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -107,9 +90,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "byteorder" @@ -137,9 +120,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", @@ -177,21 +160,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.92.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff5053a8a42dbff5279a82423946fc56dc1253b76cf211b2b3c14b3aad4e1281" +checksum = "c0322d5289ceba3217a03c9af72aa403d87542822b753daa1da32e4b992a4e80" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", "synstructure", ] [[package]] name = "chalk-ir" -version = "0.92.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56de2146a8ed0fcd54f4bd50db852f1de4eac9e1efe568494f106c21b77d2a" +checksum = "0946cbc6d9136980a24a2dddf1888b5f0aa978dda300a3aa470b55b777b6bf3c" dependencies = [ "bitflags 1.3.2", "chalk-derive", @@ -200,9 +183,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.92.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc09e6e9531f3544989ef89b189e80fbc7ad9e2f73f1c5e03ddc9ffb0527463" +checksum = "4cd93fedbeeadc0cd4d0eb73bd061b621af99f5324a6a518264c8ef5e526e0ec" dependencies = [ "chalk-derive", "chalk-ir", @@ -213,15 +196,15 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.92.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b392e02b4c81ec76d3748da839fc70a5539b83d27c9030668463d34d5110b860" +checksum = "a254cff72303c58c82df421cfe9465606372b81588923fcf179922b7eaad9a53" dependencies = [ "chalk-derive", "chalk-ir", "ena", - "indexmap 1.9.3", - "itertools", + "indexmap 2.1.0", + "itertools 0.10.5", "petgraph", "rustc-hash", "tracing", @@ -321,20 +304,20 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] name = "dissimilar" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" +checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "dot" @@ -350,9 +333,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "ena" @@ -393,9 +376,9 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.2.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" @@ -461,9 +444,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" @@ -476,15 +459,6 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" @@ -503,7 +477,7 @@ dependencies = [ "hir-def", "hir-expand", "hir-ty", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "rustc-hash", @@ -518,10 +492,9 @@ dependencies = [ name = "hir-def" version = "0.0.0" dependencies = [ - "anymap", "arrayvec", "base-db", - "bitflags 2.3.2", + "bitflags 2.4.1", "cfg", "cov-mark", "dashmap", @@ -531,17 +504,15 @@ dependencies = [ "fst", "hashbrown 0.12.3", "hir-expand", - "hkalbasi-rustc-ap-rustc_abi", - "hkalbasi-rustc-ap-rustc_index", - "indexmap 2.0.0", + "indexmap 2.1.0", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", "once_cell", "profile", - "ra-ap-rustc_parse_format", + "rustc-dependencies", "rustc-hash", "smallvec", "stdx", @@ -563,7 +534,7 @@ dependencies = [ "expect-test", "hashbrown 0.12.3", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", @@ -583,7 +554,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.3.2", + "bitflags 2.4.1", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -594,9 +565,8 @@ dependencies = [ "expect-test", "hir-def", "hir-expand", - "hkalbasi-rustc-ap-rustc_index", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "nohash-hasher", @@ -604,6 +574,7 @@ dependencies = [ "oorandom", "profile", "project-model", + "rustc-dependencies", "rustc-hash", "scoped-tls", "smallvec", @@ -618,27 +589,6 @@ dependencies = [ ] [[package]] -name = "hkalbasi-rustc-ap-rustc_abi" -version = "0.0.20221221" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adabaadad9aa7576f97af02241cdf5554d62fb3d51a84cb05d77ba28edd3013f" -dependencies = [ - "bitflags 1.3.2", - "hkalbasi-rustc-ap-rustc_index", - "tracing", -] - -[[package]] -name = "hkalbasi-rustc-ap-rustc_index" -version = "0.0.20221221" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d3c48474e09afb0f5efbd6f758e05411699301a113c47d454d28ec7059d00e" -dependencies = [ - "arrayvec", - "smallvec", -] - -[[package]] name = "home" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -663,7 +613,7 @@ dependencies = [ "ide-db", "ide-diagnostics", "ide-ssr", - "itertools", + "itertools 0.12.0", "nohash-hasher", "oorandom", "profile", @@ -689,7 +639,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "profile", "smallvec", "sourcegen", @@ -708,7 +658,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "smallvec", @@ -729,8 +679,8 @@ dependencies = [ "expect-test", "fst", "hir", - "indexmap 2.0.0", - "itertools", + "indexmap 2.1.0", + "itertools 0.12.0", "limit", "line-index 0.1.0-pre.1", "memchr", @@ -761,7 +711,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "serde_json", @@ -780,7 +730,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "nohash-hasher", "parser", "stdx", @@ -812,12 +762,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -869,6 +819,15 @@ dependencies = [ ] [[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -918,9 +877,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -972,7 +931,7 @@ dependencies = [ "crossbeam-channel", "ide", "ide-db", - "itertools", + "itertools 0.12.0", "proc-macro-api", "project-model", "tracing", @@ -1050,9 +1009,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -1153,7 +1112,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.3.2", + "bitflags 2.4.1", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1168,12 +1127,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" dependencies = [ - "overload", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1182,7 +1140,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -1217,12 +1175,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] name = "parking_lot" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1277,7 +1229,7 @@ dependencies = [ "drop_bomb", "expect-test", "limit", - "ra-ap-rustc_lexer", + "rustc-dependencies", "sourcegen", "stdx", ] @@ -1319,12 +1271,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.5.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 1.9.3", + "indexmap 2.1.0", ] [[package]] @@ -1389,9 +1341,9 @@ version = "0.0.0" [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1419,7 +1371,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "profile", @@ -1435,9 +1387,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee4a7d8b91800c8f167a6268d1a1026607368e1adc84e98fe044aeb905302f7" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" dependencies = [ "once_cell", "protobuf-support", @@ -1446,9 +1398,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca157fe12fc7ee2e315f2f735e27df41b3d97cdd70ea112824dac1ffb08ee1c" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" dependencies = [ "thiserror", ] @@ -1483,20 +1435,51 @@ dependencies = [ ] [[package]] +name = "ra-ap-rustc_abi" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2ea80a299f04a896000ce17b76f3aa1d2fe59f347fbc99c4b8970316ef5a0d" +dependencies = [ + "bitflags 1.3.2", + "ra-ap-rustc_index 0.19.0", + "tracing", +] + +[[package]] name = "ra-ap-rustc_index" -version = "0.10.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b5fa61d34da18e148dc3a81f654488ea07f40938d8aefb17f8b64bb78c6120" +checksum = "643ca3609870b1778d9cd1f2a8e4ccb4af0f48f3637cc257a09494d087bd93dc" dependencies = [ "arrayvec", "smallvec", ] [[package]] +name = "ra-ap-rustc_index" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4556489ef652e5eb6cdad6078fff08507badac80bfc1f79085c85a6d8b77ab5c" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "ra-ap-rustc_lexer" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ffd24f9ba4f1d25ff27ca1469b8d22a3bdfb12cf644fc8bfcb63121fa5da6b" +dependencies = [ + "unicode-properties", + "unicode-xid", +] + +[[package]] name = "ra-ap-rustc_lexer" -version = "0.10.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e2f6b48422e4eed5218277ab7cc9733e60dd8f3167f4f36a49a0cafe4dc195" +checksum = "90e573bf707e01fe2841dbdedeed42012004274db0edc0314e6e3e58a40598fc" dependencies = [ "unicode-properties", "unicode-xid", @@ -1504,19 +1487,19 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.10.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7369ad01cc79f9e3513c9f6a6326f6b980100e4862a7ac71b9991c88108bb" +checksum = "207b5ac1a21d4926695e03b605ffb9f63d4968e0488e9197c04c512c37303aa7" dependencies = [ - "ra-ap-rustc_index", - "ra-ap-rustc_lexer", + "ra-ap-rustc_index 0.14.0", + "ra-ap-rustc_lexer 0.14.0", ] [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -1524,14 +1507,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -1582,7 +1563,7 @@ dependencies = [ "ide", "ide-db", "ide-ssr", - "itertools", + "itertools 0.12.0", "load-cargo", "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", @@ -1594,10 +1575,12 @@ dependencies = [ "oorandom", "parking_lot 0.12.1", "parking_lot_core 0.9.6", + "parser", "proc-macro-api", "profile", "project-model", "rayon", + "rustc-dependencies", "rustc-hash", "scip", "serde", @@ -1627,6 +1610,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] +name = "rustc-dependencies" +version = "0.0.0" +dependencies = [ + "ra-ap-rustc_abi", + "ra-ap-rustc_index 0.19.0", + "ra-ap-rustc_lexer 0.19.0", + "ra-ap-rustc_parse_format", +] + +[[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1678,9 +1671,9 @@ dependencies = [ [[package]] name = "scip" -version = "0.1.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bfbb10286f69fad7c78db71004b7839bf957788359fe0c479f029f9849136b" +checksum = "3e84d21062a3ba08d58870c8c36b0c005b2b2261c6ad1bf7042585427c781883" dependencies = [ "protobuf", ] @@ -1708,31 +1701,31 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.156" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.156" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.97" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "indexmap 1.9.3", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -1746,7 +1739,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] @@ -1818,9 +1811,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -1835,7 +1828,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", "unicode-xid", ] @@ -1846,16 +1839,16 @@ dependencies = [ "cov-mark", "either", "expect-test", - "indexmap 2.0.0", - "itertools", + "indexmap 2.1.0", + "itertools 0.12.0", "once_cell", "parser", "proc-macro2", "profile", "quote", - "ra-ap-rustc_lexer", "rayon", "rowan", + "rustc-dependencies", "rustc-hash", "smol_str", "sourcegen", @@ -1881,15 +1874,15 @@ dependencies = [ name = "text-edit" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.12.0", "text-size", ] [[package]] name = "text-size" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" @@ -1908,7 +1901,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] @@ -1992,11 +1985,10 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2004,20 +1996,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2025,20 +2017,20 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "sharded-slab", "thread_local", @@ -2048,11 +2040,10 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9742d8df709837409dbb22aa25dd7769c260406f20ff48a2320b80a4a6aed0" +checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" dependencies = [ - "atty", "nu-ansi-term", "tracing-core", "tracing-log", @@ -2162,7 +2153,7 @@ name = "vfs" version = "0.0.0" dependencies = [ "fst", - "indexmap 2.0.0", + "indexmap 2.1.0", "nohash-hasher", "paths", "rustc-hash", @@ -2375,18 +2366,18 @@ checksum = "f58e7b3ca8977093aae6b87b6a7730216fc4c53a6530bab5c43a783cd810c1a8" [[package]] name = "xshell" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962c039b3a7b16cf4e9a4248397c6585c07547412e7d6a6e035389a802dcfe90" +checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbabb1cbd15a1d6d12d9ed6b35cc6777d4af87ab3ba155ea37215f20beab80c" +checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" [[package]] name = "xtask" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index cab88fc18ce..73bb9c84d2c 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -79,6 +79,7 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } +rustc-dependencies = { path = "./crates/rustc-dependencies", version = "0.0.0" } # local crates that aren't published to crates.io. These should not have versions. proc-macro-test = { path = "./crates/proc-macro-test" } @@ -89,23 +90,35 @@ la-arena = { version = "0.3.1" } lsp-server = { version = "0.7.4" } # non-local crates +anyhow = "1.0.75" +bitflags = "2.4.1" +cargo_metadata = "0.18.1" +dissimilar = "1.0.7" +either = "1.9.0" +indexmap = "2.1.0" +itertools = "0.12.0" +libc = "0.2.150" smallvec = { version = "1.10.0", features = [ "const_new", "union", "const_generics", ] } +tracing = "0.1.40" +tracing-tree = "0.3.0" +tracing-subscriber = { version = "0.3.18", default-features = false, features = [ + "registry", + "fmt", + "tracing-log", +] } smol_str = "0.2.0" nohash-hasher = "0.2.0" -text-size = "1.1.0" -serde = { version = "1.0.156", features = ["derive"] } -serde_json = "1.0.96" +text-size = "1.1.1" +rayon = "1.8.0" +serde = { version = "1.0.192", features = ["derive"] } +serde_json = "1.0.108" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently -hashbrown = { version = "0.12.3", features = ["inline-more"], default-features = false } - -rustc_lexer = { version = "0.10.0", package = "ra-ap-rustc_lexer" } -rustc_parse_format = { version = "0.10.0", package = "ra-ap-rustc_parse_format", default-features = false } - -# Upstream broke this for us so we can't update it -rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } -rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } +hashbrown = { version = "0.12.3", features = [ + "inline-more", +], default-features = false } +xshell = "0.2.5" diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index b75c7079be7..65db5c0fc7d 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -257,6 +257,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { ) -> Result<Subtree, ProcMacroExpansionError>; } +#[derive(Debug)] pub enum ProcMacroExpansionError { Panic(String), /// Things like "proc macro server was killed by OOM". diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index af204e44e6e..c5c4afa30f7 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -75,7 +75,7 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { #[salsa::input] fn crate_graph(&self) -> Arc<CrateGraph>; - /// The crate graph. + /// The proc macros. #[salsa::input] fn proc_macros(&self) -> Arc<ProcMacros>; } diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index ed380897278..4324584df39 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -23,8 +23,8 @@ oorandom = "11.1.3" # We depend on both individually instead of using `features = ["derive"]` to microoptimize the # build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr` # supports `arbitrary`. This way, we avoid feature unification. -arbitrary = "1.3.0" -derive_arbitrary = "1.3.1" +arbitrary = "1.3.2" +derive_arbitrary = "1.3.2" # local deps mbe.workspace = true diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index e7f7adc7847..4322d2d966a 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -12,9 +12,9 @@ rust-version.workspace = true doctest = false [dependencies] +cargo_metadata.workspace = true crossbeam-channel = "0.5.8" -tracing = "0.1.37" -cargo_metadata = "0.15.4" +tracing.workspace = true rustc-hash = "1.1.0" serde_json.workspace = true serde.workspace = true diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index 2de719af92c..0749d91eb32 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -50,6 +50,7 @@ pub enum FlycheckConfig { extra_args: Vec<String>, extra_env: FxHashMap<String, String>, ansi_color_output: bool, + target_dir: Option<PathBuf>, }, CustomCommand { command: String, @@ -308,6 +309,7 @@ impl FlycheckActor { features, extra_env, ansi_color_output, + target_dir, } => { let mut cmd = Command::new(toolchain::cargo()); cmd.arg(command); @@ -340,6 +342,9 @@ impl FlycheckActor { cmd.arg(features.join(" ")); } } + if let Some(target_dir) = target_dir { + cmd.arg("--target-dir").arg(target_dir); + } cmd.envs(extra_env); (cmd, extra_args) } diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 8cf61ee04d4..e4f2e14c51c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -12,29 +12,25 @@ rust-version.workspace = true doctest = false [dependencies] -anymap = "1.0.0-beta.2" arrayvec = "0.7.2" -bitflags = "2.1.0" +bitflags.workspace = true cov-mark = "2.0.0-pre.1" # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.4.0", features = ["raw-api"] } drop_bomb = "0.1.5" -either = "1.7.0" +either.workspace = true fst = { version = "0.4.7", default-features = false } -indexmap = "2.0.0" -itertools = "0.10.5" +indexmap.workspace = true +itertools.workspace = true la-arena.workspace = true once_cell = "1.17.0" rustc-hash = "1.1.0" -tracing = "0.1.35" +tracing.workspace = true smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true -rustc_abi.workspace = true -rustc_index.workspace = true -rustc_parse_format.workspace = true - +rustc-dependencies.workspace = true # local deps stdx.workspace = true @@ -48,8 +44,12 @@ cfg.workspace = true tt.workspace = true limit.workspace = true + [dev-dependencies] expect-test = "1.4.0" # local deps test-utils.workspace = true + +[features] +in-rust-tree = ["rustc-dependencies/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index c6454eb9ea0..fa3025e0303 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -215,6 +215,10 @@ impl Attrs { self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) } + pub fn export_name(&self) -> Option<&SmolStr> { + self.by_key("export_name").string_value() + } + pub fn is_proc_macro(&self) -> bool { self.by_key("proc_macro").exists() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index c0baf6011f7..1942c60c075 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -57,7 +57,7 @@ pub struct Body { pub type ExprPtr = AstPtr<ast::Expr>; pub type ExprSource = InFile<ExprPtr>; -pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; +pub type PatPtr = AstPtr<Either<ast::Pat, ast::SelfParam>>; pub type PatSource = InFile<PatPtr>; pub type LabelPtr = AstPtr<ast::Label>; @@ -356,12 +356,12 @@ impl BodySourceMap { } pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> { - let src = node.map(|it| Either::Left(AstPtr::new(it))); + let src = node.map(|it| AstPtr::new(it).wrap_left()); self.pat_map.get(&src).cloned() } pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> { - let src = node.map(|it| Either::Right(AstPtr::new(it))); + let src = node.map(|it| AstPtr::new(it).wrap_right()); self.pat_map.get(&src).cloned() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index cc02df80a8f..e4158d7564b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -196,16 +196,12 @@ impl ExprCollector<'_> { if let Some(self_param) = param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) { - let ptr = AstPtr::new(&self_param); - let binding_id: la_arena::Idx<Binding> = self.alloc_binding( - name![self], - BindingAnnotation::new( - self_param.mut_token().is_some() && self_param.amp_token().is_none(), - false, - ), - ); - let param_pat = - self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, Either::Right(ptr)); + let is_mutable = + self_param.mut_token().is_some() && self_param.amp_token().is_none(); + let ptr = AstPtr::new(&Either::Right(self_param)); + let binding_id: la_arena::Idx<Binding> = + self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false)); + let param_pat = self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, ptr); self.add_definition_to_binding(binding_id, param_pat); self.body.params.push(param_pat); } @@ -1260,8 +1256,8 @@ impl ExprCollector<'_> { (Some(id), Pat::Bind { id, subpat }) }; - let ptr = AstPtr::new(&pat); - let pat = self.alloc_pat(pattern, Either::Left(ptr)); + let ptr = AstPtr::new(&Either::Left(pat)); + let pat = self.alloc_pat(pattern, ptr); if let Some(binding_id) = binding { self.add_definition_to_binding(binding_id, pat); } @@ -1395,7 +1391,7 @@ impl ExprCollector<'_> { ast::Pat::MacroPat(mac) => match mac.macro_call() { Some(call) => { let macro_ptr = AstPtr::new(&call); - let src = self.expander.to_source(Either::Left(AstPtr::new(&pat))); + let src = self.expander.to_source(AstPtr::new(&Either::Left(pat))); let pat = self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { this.collect_pat_opt(expanded_pat, binding_list) @@ -1430,8 +1426,8 @@ impl ExprCollector<'_> { Pat::Range { start, end } } }; - let ptr = AstPtr::new(&pat); - self.alloc_pat(pattern, Either::Left(ptr)) + let ptr = AstPtr::new(&Either::Left(pat)); + self.alloc_pat(pattern, ptr) } fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index fad4d7a4da6..6ecf1c20d6c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -54,7 +54,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); - body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| { + body.params.iter().zip(db.function_data(it).params.iter()).for_each(|(¶m, ty)| { p.print_pat(param); p.buf.push(':'); p.print_type_ref(ty); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs index 2a90a09f25e..baca293e290 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs @@ -1,7 +1,6 @@ //! Name resolution for expressions. use hir_expand::name::Name; -use la_arena::{Arena, Idx, IdxRange, RawIdx}; -use rustc_hash::FxHashMap; +use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use triomphe::Arc; use crate::{ @@ -17,7 +16,7 @@ pub type ScopeId = Idx<ScopeData>; pub struct ExprScopes { scopes: Arena<ScopeData>, scope_entries: Arena<ScopeEntry>, - scope_by_expr: FxHashMap<ExprId, ScopeId>, + scope_by_expr: ArenaMap<ExprId, ScopeId>, } #[derive(Debug, PartialEq, Eq)] @@ -77,10 +76,10 @@ impl ExprScopes { } pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> { - self.scope_by_expr.get(&expr).copied() + self.scope_by_expr.get(expr).copied() } - pub fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> { + pub fn scope_by_expr(&self) -> &ArenaMap<ExprId, ScopeId> { &self.scope_by_expr } } @@ -94,7 +93,7 @@ impl ExprScopes { let mut scopes = ExprScopes { scopes: Arena::default(), scope_entries: Arena::default(), - scope_by_expr: FxHashMap::default(), + scope_by_expr: ArenaMap::with_capacity(body.exprs.len()), }; let mut root = scopes.root_scope(); scopes.add_params_bindings(body, root, &body.params); @@ -476,10 +475,7 @@ fn foo() { .pat_syntax(*body.bindings[resolved.binding()].definitions.first().unwrap()) .unwrap(); - let local_name = pat_src.value.either( - |it| it.syntax_node_ptr().to_node(file.syntax()), - |it| it.syntax_node_ptr().to_node(file.syntax()), - ); + let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax()); assert_eq!(local_name.text_range(), expected_name.syntax().text_range()); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 68defa3858f..72ccc17486f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -15,9 +15,7 @@ use crate::{ attr::Attrs, db::DefDatabase, expander::{Expander, Mark}, - item_tree::{ - self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId, - }, + item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId}, macro_call_as_call_id, macro_id_to_def_id, nameres::{ attr_resolution::ResolvedAttr, @@ -36,7 +34,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct FunctionData { pub name: Name, - pub params: Vec<Interned<TypeRef>>, + pub params: Box<[Interned<TypeRef>]>, pub ret_type: Interned<TypeRef>, pub attrs: Attrs, pub visibility: RawVisibility, @@ -69,7 +67,7 @@ impl FunctionData { let is_varargs = enabled_params .clone() .next_back() - .map_or(false, |param| matches!(item_tree[param], Param::Varargs)); + .map_or(false, |param| item_tree[param].type_ref.is_none()); let mut flags = func.flags; if is_varargs { @@ -105,10 +103,7 @@ impl FunctionData { name: func.name.clone(), params: enabled_params .clone() - .filter_map(|id| match &item_tree[id] { - Param::Normal(ty) => Some(ty.clone()), - Param::Varargs => None, - }) + .filter_map(|id| item_tree[id].type_ref.clone()) .collect(), ret_type: func.ret_type.clone(), attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), @@ -182,7 +177,7 @@ pub struct TypeAliasData { pub rustc_has_incoherent_inherent_impls: bool, pub rustc_allow_incoherent_impl: bool, /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). - pub bounds: Vec<Interned<TypeBound>>, + pub bounds: Box<[Interned<TypeBound>]>, } impl TypeAliasData { @@ -215,7 +210,7 @@ impl TypeAliasData { is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), rustc_has_incoherent_inherent_impls, rustc_allow_incoherent_impl, - bounds: typ.bounds.to_vec(), + bounds: typ.bounds.clone(), }) } } @@ -332,6 +327,7 @@ pub struct ImplData { pub self_ty: Interned<TypeRef>, pub items: Vec<AssocItemId>, pub is_negative: bool, + pub is_unsafe: bool, // box it as the vec is usually empty anyways pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, } @@ -353,6 +349,7 @@ impl ImplData { let target_trait = impl_def.target_trait.clone(); let self_ty = impl_def.self_ty.clone(); let is_negative = impl_def.is_negative; + let is_unsafe = impl_def.is_unsafe; let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id)); @@ -362,7 +359,14 @@ impl ImplData { let items = items.into_iter().map(|(_, item)| item).collect(); ( - Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls }), + Arc::new(ImplData { + target_trait, + self_ty, + items, + is_negative, + is_unsafe, + attribute_calls, + }), diagnostics.into(), ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 224f7328f8c..b163112db91 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -11,7 +11,7 @@ use hir_expand::{ }; use intern::Interned; use la_arena::{Arena, ArenaMap}; -use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; +use rustc_dependencies::abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 63138aa6ad7..a59bbf7e221 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -29,8 +29,8 @@ use std::{ ops::{Index, IndexMut}, }; -use anymap::Map; use rustc_hash::FxHashMap; +use stdx::anymap::Map; pub struct Key<K, V, P = (K, V)> { _phantom: PhantomData<(K, V, P)>, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index b9c5ff72792..1ebd1ba0e66 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -21,9 +21,10 @@ pub fn find_path( item: ItemInNs, from: ModuleId, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { let _p = profile::span("find_path"); - find_path_inner(db, item, from, None, prefer_no_std) + find_path_inner(db, item, from, None, prefer_no_std, prefer_prelude) } pub fn find_path_prefixed( @@ -32,9 +33,10 @@ pub fn find_path_prefixed( from: ModuleId, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { let _p = profile::span("find_path_prefixed"); - find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std) + find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std, prefer_prelude) } #[derive(Copy, Clone, Debug)] @@ -88,6 +90,7 @@ fn find_path_inner( from: ModuleId, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { // - if the item is a builtin, it's in scope if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { @@ -109,6 +112,7 @@ fn find_path_inner( MAX_PATH_LEN, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), + prefer_prelude, ) .map(|(item, _)| item); } @@ -134,6 +138,7 @@ fn find_path_inner( from, prefixed, prefer_no_std, + prefer_prelude, ) { let data = db.enum_data(variant.parent); path.push_segment(data.variants[variant.local_id].name.clone()); @@ -156,6 +161,7 @@ fn find_path_inner( from, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), + prefer_prelude, scope_name, ) .map(|(item, _)| item) @@ -171,6 +177,7 @@ fn find_path_for_module( max_len: usize, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<(ModPath, Stability)> { if max_len == 0 { return None; @@ -236,6 +243,7 @@ fn find_path_for_module( from, prefixed, prefer_no_std, + prefer_prelude, scope_name, ) } @@ -316,6 +324,7 @@ fn calculate_best_path( from: ModuleId, mut prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, scope_name: Option<Name>, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { @@ -351,11 +360,14 @@ fn calculate_best_path( best_path_len - 1, prefixed, prefer_no_std, + prefer_prelude, ) { path.0.push_segment(name); let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, prefer_no_std), + Some(best_path) => { + select_best_path(best_path, path, prefer_no_std, prefer_prelude) + } None => path, }; best_path_len = new_path.0.len(); @@ -367,18 +379,18 @@ fn calculate_best_path( // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - let crate_graph = db.crate_graph(); - let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { + for dep in &db.crate_graph()[from.krate].dependencies { let import_map = db.import_map(dep.crate_id); - import_map.import_info_for(item).and_then(|info| { + let Some(import_info_for) = import_map.import_info_for(item) else { continue }; + for info in import_info_for { if info.is_doc_hidden { // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate - return None; + continue; } // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let (mut path, path_stability) = find_path_for_module( + let Some((mut path, path_stability)) = find_path_for_module( db, def_map, visited_modules, @@ -388,22 +400,26 @@ fn calculate_best_path( max_len - 1, prefixed, prefer_no_std, - )?; + prefer_prelude, + ) else { + continue; + }; cov_mark::hit!(partially_imported); path.push_segment(info.name.clone()); - Some(( + + let path_with_stab = ( path, zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }), - )) - }) - }); + ); - for path in extern_paths { - let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, prefer_no_std), - None => path, - }; - update_best_path(&mut best_path, new_path); + let new_path_with_stab = match best_path.take() { + Some(best_path) => { + select_best_path(best_path, path_with_stab, prefer_no_std, prefer_prelude) + } + None => path_with_stab, + }; + update_best_path(&mut best_path, new_path_with_stab); + } } } if let Some(module) = item.module(db) { @@ -420,17 +436,39 @@ fn calculate_best_path( } } +/// Select the best (most relevant) path between two paths. +/// This accounts for stability, path length whether std should be chosen over alloc/core paths as +/// well as ignoring prelude like paths or not. fn select_best_path( - old_path: (ModPath, Stability), - new_path: (ModPath, Stability), + old_path @ (_, old_stability): (ModPath, Stability), + new_path @ (_, new_stability): (ModPath, Stability), prefer_no_std: bool, + prefer_prelude: bool, ) -> (ModPath, Stability) { - match (old_path.1, new_path.1) { + match (old_stability, new_stability) { (Stable, Unstable) => return old_path, (Unstable, Stable) => return new_path, _ => {} } const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc]; + + let choose = |new_path: (ModPath, _), old_path: (ModPath, _)| { + let new_has_prelude = new_path.0.segments().iter().any(|seg| seg == &known::prelude); + let old_has_prelude = old_path.0.segments().iter().any(|seg| seg == &known::prelude); + match (new_has_prelude, old_has_prelude, prefer_prelude) { + (true, false, true) | (false, true, false) => new_path, + (true, false, false) | (false, true, true) => old_path, + // no prelude difference in the paths, so pick the smaller one + (true, true, _) | (false, false, _) => { + if new_path.0.len() < old_path.0.len() { + new_path + } else { + old_path + } + } + } + }; + match (old_path.0.segments().first(), new_path.0.segments().first()) { (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { let rank = match prefer_no_std { @@ -451,23 +489,11 @@ fn select_best_path( let orank = rank(old); match nrank.cmp(&orank) { Ordering::Less => old_path, - Ordering::Equal => { - if new_path.0.len() < old_path.0.len() { - new_path - } else { - old_path - } - } + Ordering::Equal => choose(new_path, old_path), Ordering::Greater => new_path, } } - _ => { - if new_path.0.len() < old_path.0.len() { - new_path - } else { - old_path - } - } + _ => choose(new_path, old_path), } } @@ -570,7 +596,13 @@ mod tests { /// `code` needs to contain a cursor marker; checks that `find_path` for the /// item the `path` refers to returns that same path when called from the /// module the cursor is in. - fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) { + #[track_caller] + fn check_found_path_( + ra_fixture: &str, + path: &str, + prefix_kind: Option<PrefixKind>, + prefer_prelude: bool, + ) { let (db, pos) = TestDB::with_position(ra_fixture); let module = db.module_at_position(pos); let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};")); @@ -589,11 +621,17 @@ mod tests { ) .0 .take_types() - .unwrap(); - - let found_path = - find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false); - assert_eq!(found_path, Some(mod_path), "{prefix_kind:?}"); + .expect("path does not resolve to a type"); + + let found_path = find_path_inner( + &db, + ItemInNs::Types(resolved), + module, + prefix_kind, + false, + prefer_prelude, + ); + assert_eq!(found_path, Some(mod_path), "on kind: {prefix_kind:?}"); } fn check_found_path( @@ -603,10 +641,23 @@ mod tests { absolute: &str, self_prefixed: &str, ) { - check_found_path_(ra_fixture, unprefixed, None); - check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain)); - check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate)); - check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf)); + check_found_path_(ra_fixture, unprefixed, None, false); + check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), false); + check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), false); + check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), false); + } + + fn check_found_path_prelude( + ra_fixture: &str, + unprefixed: &str, + prefixed: &str, + absolute: &str, + self_prefixed: &str, + ) { + check_found_path_(ra_fixture, unprefixed, None, true); + check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), true); + check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), true); + check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), true); } #[test] @@ -1421,4 +1472,34 @@ pub mod error { "std::error::Error", ); } + + #[test] + fn respects_prelude_setting() { + let ra_fixture = r#" +//- /main.rs crate:main deps:krate +$0 +//- /krate.rs crate:krate +pub mod prelude { + pub use crate::foo::*; +} + +pub mod foo { + pub struct Foo; +} +"#; + check_found_path( + ra_fixture, + "krate::foo::Foo", + "krate::foo::Foo", + "krate::foo::Foo", + "krate::foo::Foo", + ); + check_found_path_prelude( + ra_fixture, + "krate::prelude::Foo", + "krate::prelude::Foo", + "krate::prelude::Foo", + "krate::prelude::Foo", + ); + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index 1e2535a8a99..fac90e66304 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -227,7 +227,7 @@ impl GenericParams { let mut expander = Lazy::new(|| { (module.def_map(db), Expander::new(db, loc.source(db).file_id, module)) }); - for param in &func_data.params { + for param in func_data.params.iter() { generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index 75025a984fc..46d24bd4a61 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -2,7 +2,7 @@ use std::mem; use hir_expand::name::Name; -use rustc_parse_format as parse; +use rustc_dependencies::parse_format as parse; use syntax::{ ast::{self, IsString}, AstToken, SmolStr, TextRange, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 44b7f1b4f63..d589fbe347a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -1,49 +1,48 @@ //! A map of all publicly exported items in a crate. -use std::collections::hash_map::Entry; use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; -use fst::{self, Streamer}; +use fst::{self, raw::IndexedValue, Streamer}; use hir_expand::name::Name; use indexmap::IndexMap; use itertools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use rustc_hash::{FxHashSet, FxHasher}; +use smallvec::SmallVec; use triomphe::Arc; -use crate::item_scope::ImportOrExternCrate; use crate::{ - db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId, - ModuleDefId, ModuleId, TraitId, + db::DefDatabase, + item_scope::{ImportOrExternCrate, ItemInNs}, + nameres::DefMap, + visibility::Visibility, + AssocItemId, ModuleDefId, ModuleId, TraitId, }; type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; -// FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should -// have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`. /// Item import details stored in the `ImportMap`. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct ImportInfo { /// A name that can be used to import the item, relative to the crate's root. pub name: Name, /// The module containing this item. pub container: ModuleId, - /// Whether the import is a trait associated item or not. - pub is_trait_assoc_item: bool, /// Whether this item is annotated with `#[doc(hidden)]`. pub is_doc_hidden: bool, /// Whether this item is annotated with `#[unstable(..)]`. pub is_unstable: bool, } +type ImportMapIndex = FxIndexMap<ItemInNs, (SmallVec<[ImportInfo; 1]>, IsTraitAssocItem)>; + /// A map from publicly exported items to its name. /// /// Reexports of items are taken into account, ie. if something is exported under multiple /// names, the one with the shortest import path will be used. #[derive(Default)] pub struct ImportMap { - map: FxIndexMap<ItemInNs, ImportInfo>, - + map: ImportMapIndex, /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. /// @@ -54,6 +53,12 @@ pub struct ImportMap { fst: fst::Map<Vec<u8>>, } +#[derive(Copy, Clone, PartialEq, Eq)] +enum IsTraitAssocItem { + Yes, + No, +} + impl ImportMap { pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { let _p = profile::span("import_map_query"); @@ -63,13 +68,18 @@ impl ImportMap { let mut importables: Vec<_> = map .iter() // We've only collected items, whose name cannot be tuple field. - .map(|(&item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + .flat_map(|(&item, (info, _))| { + info.iter() + .map(move |info| (item, info.name.as_str().unwrap().to_ascii_lowercase())) + }) .collect(); importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name)); + importables.dedup(); // Build the FST, taking care not to insert duplicate values. let mut builder = fst::MapBuilder::memory(); - let iter = importables.iter().enumerate().dedup_by(|lhs, rhs| lhs.1 .1 == rhs.1 .1); + let iter = + importables.iter().enumerate().dedup_by(|(_, (_, lhs)), (_, (_, rhs))| lhs == rhs); for (start_idx, (_, name)) in iter { let _ = builder.insert(name, start_idx as u64); } @@ -81,12 +91,12 @@ impl ImportMap { }) } - pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { - self.map.get(&item) + pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> { + self.map.get(&item).map(|(info, _)| &**info) } } -fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemInNs, ImportInfo> { +fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMapIndex { let _p = profile::span("collect_import_map"); let def_map = db.crate_def_map(krate); @@ -94,11 +104,13 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn // We look only into modules that are public(ly reexported), starting with the crate root. let root = def_map.module_id(DefMap::ROOT); - let mut worklist = vec![(root, 0)]; - // Records items' minimum module depth. - let mut depth_map = FxHashMap::default(); + let mut worklist = vec![root]; + let mut visited = FxHashSet::default(); - while let Some((module, depth)) = worklist.pop() { + while let Some(module) = worklist.pop() { + if !visited.insert(module) { + continue; + } let ext_def_map; let mod_data = if module.krate == krate { &def_map[module.local_id] @@ -126,62 +138,18 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn ItemInNs::Macros(id) => Some(id.into()), } }; - let status @ (is_doc_hidden, is_unstable) = - attr_id.map_or((false, false), |attr_id| { - let attrs = db.attrs(attr_id); - (attrs.has_doc_hidden(), attrs.is_unstable()) - }); + let (is_doc_hidden, is_unstable) = attr_id.map_or((false, false), |attr_id| { + let attrs = db.attrs(attr_id); + (attrs.has_doc_hidden(), attrs.is_unstable()) + }); let import_info = ImportInfo { name: name.clone(), container: module, - is_trait_assoc_item: false, is_doc_hidden, is_unstable, }; - match depth_map.entry(item) { - Entry::Vacant(entry) => _ = entry.insert((depth, status)), - Entry::Occupied(mut entry) => { - let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get(); - (depth, occ_depth); - let overwrite = match ( - is_doc_hidden, - occ_is_doc_hidden, - is_unstable, - occ_is_unstable, - ) { - // no change of hiddeness or unstableness - (true, true, true, true) - | (true, true, false, false) - | (false, false, true, true) - | (false, false, false, false) => depth < occ_depth, - - // either less hidden or less unstable, accept - (true, true, false, true) - | (false, true, true, true) - | (false, true, false, true) - | (false, true, false, false) - | (false, false, false, true) => true, - // more hidden or unstable, discard - (true, true, true, false) - | (true, false, true, true) - | (true, false, true, false) - | (true, false, false, false) - | (false, false, true, false) => false, - - // exchanges doc(hidden) for unstable (and vice-versa), - (true, false, false, true) | (false, true, true, false) => { - depth < occ_depth - } - }; - if !overwrite { - continue; - } - entry.insert((depth, status)); - } - } - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { collect_trait_assoc_items( db, @@ -192,13 +160,14 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn ); } - map.insert(item, import_info); + let (infos, _) = + map.entry(item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No)); + infos.reserve_exact(1); + infos.push(import_info); - // If we've just added a module, descend into it. We might traverse modules - // multiple times, but only if the module depth is smaller (else we `continue` - // above). + // If we've just added a module, descend into it. if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { - worklist.push((mod_id, depth + 1)); + worklist.push(mod_id); } } } @@ -209,7 +178,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn fn collect_trait_assoc_items( db: &dyn DefDatabase, - map: &mut FxIndexMap<ItemInNs, ImportInfo>, + map: &mut ImportMapIndex, tr: TraitId, is_type_in_ns: bool, trait_import_info: &ImportInfo, @@ -236,11 +205,14 @@ fn collect_trait_assoc_items( let assoc_item_info = ImportInfo { container: trait_import_info.container, name: assoc_item_name.clone(), - is_trait_assoc_item: true, is_doc_hidden: attrs.has_doc_hidden(), is_unstable: attrs.is_unstable(), }; - map.insert(assoc_item, assoc_item_info); + + let (infos, _) = + map.entry(assoc_item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::Yes)); + infos.reserve_exact(1); + infos.push(assoc_item_info); } } @@ -258,10 +230,13 @@ impl fmt::Debug for ImportMap { let mut importable_names: Vec<_> = self .map .iter() - .map(|(item, _)| match item { - ItemInNs::Types(it) => format!("- {it:?} (t)",), - ItemInNs::Values(it) => format!("- {it:?} (v)",), - ItemInNs::Macros(it) => format!("- {it:?} (m)",), + .map(|(item, (infos, _))| { + let l = infos.len(); + match item { + ItemInNs::Types(it) => format!("- {it:?} (t) [{l}]",), + ItemInNs::Values(it) => format!("- {it:?} (v) [{l}]",), + ItemInNs::Macros(it) => format!("- {it:?} (m) [{l}]",), + } }) .collect(); @@ -271,13 +246,15 @@ impl fmt::Debug for ImportMap { } /// A way to match import map contents against the search query. -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] enum SearchMode { /// Import map entry should strictly match the query string. Exact, /// Import map entry should contain all letters from the query string, /// in the same order, but not necessary adjacent. Fuzzy, + /// Import map entry should match the query string by prefix. + Prefix, } /// Three possible ways to search for the name in associated and/or other items. @@ -319,6 +296,14 @@ impl Query { Self { search_mode: SearchMode::Fuzzy, ..self } } + pub fn prefix(self) -> Self { + Self { search_mode: SearchMode::Prefix, ..self } + } + + pub fn exact(self) -> Self { + Self { search_mode: SearchMode::Exact, ..self } + } + /// Specifies whether we want to include associated items in the result. pub fn assoc_search_mode(self, assoc_mode: AssocSearchMode) -> Self { Self { assoc_mode, ..self } @@ -334,6 +319,15 @@ impl Query { Self { case_sensitive: true, ..self } } + fn matches_assoc_mode(&self, is_trait_assoc_item: IsTraitAssocItem) -> bool { + match (is_trait_assoc_item, self.assoc_mode) { + (IsTraitAssocItem::Yes, AssocSearchMode::Exclude) + | (IsTraitAssocItem::No, AssocSearchMode::AssocItemsOnly) => false, + _ => true, + } + } + + /// Checks whether the import map entry matches the query. fn import_matches( &self, db: &dyn DefDatabase, @@ -341,12 +335,8 @@ impl Query { enforce_lowercase: bool, ) -> bool { let _p = profile::span("import_map::Query::import_matches"); - match (import.is_trait_assoc_item, self.assoc_mode) { - (true, AssocSearchMode::Exclude) => return false, - (false, AssocSearchMode::AssocItemsOnly) => return false, - _ => {} - } + // FIXME: Can we get rid of the alloc here? let mut input = import.name.display(db.upcast()).to_string(); let case_insensitive = enforce_lowercase || !self.case_sensitive; if case_insensitive { @@ -356,7 +346,8 @@ impl Query { let query_string = if case_insensitive { &self.lowercased } else { &self.query }; match self.search_mode { - SearchMode::Exact => &input == query_string, + SearchMode::Exact => input == *query_string, + SearchMode::Prefix => input.starts_with(query_string), SearchMode::Fuzzy => { let mut input_chars = input.chars(); for query_char in query_string.chars() { @@ -376,7 +367,7 @@ impl Query { pub fn search_dependencies( db: &dyn DefDatabase, krate: CrateId, - query: Query, + ref query: Query, ) -> FxHashSet<ItemInNs> { let _p = profile::span("search_dependencies").detail(|| format!("{query:?}")); @@ -394,31 +385,58 @@ pub fn search_dependencies( let mut stream = op.union(); let mut res = FxHashSet::default(); + let mut common_importable_data_scratch = vec![]; while let Some((_, indexed_values)) = stream.next() { - for indexed_value in indexed_values { - let import_map = &import_maps[indexed_value.index]; - let importables = &import_map.importables[indexed_value.value as usize..]; + for &IndexedValue { index, value } in indexed_values { + let import_map = &import_maps[index]; + let importables @ [importable, ..] = &import_map.importables[value as usize..] else { + continue; + }; - let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(db, common_importable_data, true) { + let &(ref importable_data, is_trait_assoc_item) = &import_map.map[importable]; + if !query.matches_assoc_mode(is_trait_assoc_item) { continue; } - // Name shared by the importable items in this group. - let common_importable_name = - common_importable_data.name.to_smol_str().to_ascii_lowercase(); - // Add the items from this name group. Those are all subsequent items in - // `importables` whose name match `common_importable_name`. - let iter = importables - .iter() - .copied() - .take_while(|item| { - common_importable_name - == import_map.map[item].name.to_smol_str().to_ascii_lowercase() - }) - .filter(|item| { - !query.case_sensitive // we've already checked the common importables name case-insensitively - || query.import_matches(db, &import_map.map[item], false) + common_importable_data_scratch.extend( + importable_data + .iter() + .filter(|&info| query.import_matches(db, info, true)) + // Name shared by the importable items in this group. + .map(|info| info.name.to_smol_str()), + ); + if common_importable_data_scratch.is_empty() { + continue; + } + common_importable_data_scratch.sort(); + common_importable_data_scratch.dedup(); + + let iter = + common_importable_data_scratch.drain(..).flat_map(|common_importable_name| { + // Add the items from this name group. Those are all subsequent items in + // `importables` whose name match `common_importable_name`. + importables + .iter() + .copied() + .take_while(move |item| { + let &(ref import_infos, assoc_mode) = &import_map.map[item]; + query.matches_assoc_mode(assoc_mode) + && import_infos.iter().any(|info| { + info.name + .to_smol_str() + .eq_ignore_ascii_case(&common_importable_name) + }) + }) + .filter(move |item| { + !query.case_sensitive || { + // we've already checked the common importables name case-insensitively + let &(ref import_infos, assoc_mode) = &import_map.map[item]; + query.matches_assoc_mode(assoc_mode) + && import_infos + .iter() + .any(|info| query.import_matches(db, info, false)) + } + }) }); res.extend(iter); @@ -445,6 +463,7 @@ mod tests { let mut importable_paths: Vec<_> = self .map .iter() + .flat_map(|(item, (info, _))| info.iter().map(move |info| (item, info))) .map(|(item, info)| { let path = render_path(db, info); let ns = match item { @@ -483,7 +502,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - render_path(&db, dependency_imports.import_info_for(dependency)?), + render_path(&db, &dependency_imports.import_info_for(dependency)?[0]), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -531,7 +550,12 @@ mod tests { .items .iter() .find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?; - Some(format!("{}::{}", render_path(db, trait_info), assoc_item_name.display(db.upcast()))) + // FIXME: This should check all import infos, not just the first + Some(format!( + "{}::{}", + render_path(db, &trait_info[0]), + assoc_item_name.display(db.upcast()) + )) } fn check(ra_fixture: &str, expect: Expect) { @@ -607,6 +631,7 @@ mod tests { main: - publ1 (t) - real_pu2 (t) + - real_pu2::Pub (t) - real_pub (t) - real_pub::Pub (t) "#]], @@ -632,6 +657,7 @@ mod tests { - sub (t) - sub::Def (t) - sub::subsub (t) + - sub::subsub::Def (t) "#]], ); } @@ -731,7 +757,9 @@ mod tests { - module (t) - module::S (t) - module::S (v) + - module::module (t) - sub (t) + - sub::module (t) "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 4c812b62a46..70b96b25739 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -613,10 +613,17 @@ pub struct Function { pub(crate) flags: FnFlags, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum Param { - Normal(Interned<TypeRef>), - Varargs, +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Param { + /// This is [`None`] for varargs + pub type_ref: Option<Interned<TypeRef>>, + pub ast_id: ParamAstId, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ParamAstId { + Param(FileAstId<ast::Param>), + SelfParam(FileAstId<ast::SelfParam>), } bitflags::bitflags! { @@ -702,6 +709,7 @@ pub struct Impl { pub target_trait: Option<Interned<TraitRef>>, pub self_ty: Interned<TypeRef>, pub is_negative: bool, + pub is_unsafe: bool, pub items: Box<[AssocItem]>, pub ast_id: FileAstId<ast::Impl>, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index e4702c113b8..c898eb5f921 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -295,8 +295,12 @@ impl<'a> Ctx<'a> { } } }; - let ty = Interned::new(self_type); - let idx = self.data().params.alloc(Param::Normal(ty)); + let type_ref = Interned::new(self_type); + let ast_id = self.source_ast_id_map.ast_id(&self_param); + let idx = self.data().params.alloc(Param { + type_ref: Some(type_ref), + ast_id: ParamAstId::SelfParam(ast_id), + }); self.add_attrs( idx.into(), RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()), @@ -305,11 +309,19 @@ impl<'a> Ctx<'a> { } for param in param_list.params() { let idx = match param.dotdotdot_token() { - Some(_) => self.data().params.alloc(Param::Varargs), + Some(_) => { + let ast_id = self.source_ast_id_map.ast_id(¶m); + self.data() + .params + .alloc(Param { type_ref: None, ast_id: ParamAstId::Param(ast_id) }) + } None => { let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); let ty = Interned::new(type_ref); - self.data().params.alloc(Param::Normal(ty)) + let ast_id = self.source_ast_id_map.ast_id(¶m); + self.data() + .params + .alloc(Param { type_ref: Some(ty), ast_id: ParamAstId::Param(ast_id) }) } }; self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), ¶m, self.hygiene())); @@ -384,14 +396,7 @@ impl<'a> Ctx<'a> { let bounds = self.lower_type_bounds(type_alias); let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias); let ast_id = self.source_ast_id_map.ast_id(type_alias); - let res = TypeAlias { - name, - visibility, - bounds: bounds.into_boxed_slice(), - generic_params, - type_ref, - ast_id, - }; + let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; Some(id(self.data().type_aliases.alloc(res))) } @@ -487,6 +492,7 @@ impl<'a> Ctx<'a> { let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr)); let self_ty = self.lower_type_ref(&impl_def.self_ty()?); let is_negative = impl_def.excl_token().is_some(); + let is_unsafe = impl_def.unsafe_token().is_some(); // We cannot use `assoc_items()` here as that does not include macro calls. let items = impl_def @@ -501,7 +507,8 @@ impl<'a> Ctx<'a> { }) .collect(); let ast_id = self.source_ast_id_map.ast_id(impl_def); - let res = Impl { generic_params, target_trait, self_ty, is_negative, items, ast_id }; + let res = + Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id }; Some(id(self.data().impls.alloc(res))) } @@ -625,13 +632,13 @@ impl<'a> Ctx<'a> { Interned::new(generics) } - fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Vec<Interned<TypeBound>> { + fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Box<[Interned<TypeBound>]> { match node.type_bound_list() { Some(bound_list) => bound_list .bounds() .map(|it| Interned::new(TypeBound::from_ast(&self.body_ctx, it))) .collect(), - None => Vec::new(), + None => Box::default(), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 417bd37c8a9..ca3785bf28d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -261,15 +261,15 @@ impl Printer<'_> { self.indented(|this| { for param in params.clone() { this.print_attrs_of(param, "\n"); - match &this.tree[param] { - Param::Normal(ty) => { + match &this.tree[param].type_ref { + Some(ty) => { if flags.contains(FnFlags::HAS_SELF_PARAM) { w!(this, "self: "); } this.print_type_ref(ty); wln!(this, ","); } - Param::Varargs => { + None => { wln!(this, "..."); } }; @@ -388,8 +388,18 @@ impl Printer<'_> { wln!(self); } ModItem::Impl(it) => { - let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } = - &self.tree[it]; + let Impl { + target_trait, + self_ty, + is_negative, + is_unsafe, + items, + generic_params, + ast_id: _, + } = &self.tree[it]; + if *is_unsafe { + w!(self, "unsafe"); + } w!(self, "impl"); self.print_generic_params(generic_params); w!(self, " "); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 3f87fe62b83..fd8f64d6705 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -8,6 +8,7 @@ //! actually true. #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[allow(unused)] macro_rules! eprintln { @@ -48,7 +49,7 @@ pub mod visibility; pub mod find_path; pub mod import_map; -pub use rustc_abi as layout; +pub use rustc_dependencies::abi as layout; use triomphe::Arc; #[cfg(test)] @@ -72,6 +73,7 @@ use hir_expand::{ db::ExpandDatabase, eager::expand_eager_macro_input, hygiene::Hygiene, + name::Name, proc_macro::ProcMacroExpander, AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, UnresolvedMacro, @@ -150,7 +152,7 @@ impl TryFrom<ModuleId> for CrateRootModuleId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct ModuleId { krate: CrateId, /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the @@ -173,6 +175,18 @@ impl ModuleId { self.krate } + pub fn name(self, db: &dyn db::DefDatabase) -> Option<Name> { + let def_map = self.def_map(db); + let parent = def_map[self.local_id].parent?; + def_map[parent].children.iter().find_map(|(name, module_id)| { + if *module_id == self.local_id { + Some(name.clone()) + } else { + None + } + }) + } + pub fn containing_module(self, db: &dyn db::DefDatabase) -> Option<ModuleId> { self.def_map(db).containing_module(self.local_id) } @@ -498,10 +512,7 @@ impl_from!(Macro2Id, MacroRulesId, ProcMacroId for MacroId); impl MacroId { pub fn is_attribute(self, db: &dyn db::DefDatabase) -> bool { - match self { - MacroId::ProcMacroId(it) => it.lookup(db).kind == ProcMacroKind::Attr, - _ => false, - } + matches!(self, MacroId::ProcMacroId(it) if it.lookup(db).kind == ProcMacroKind::Attr) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 4aedb22c6bc..106ead83fad 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -17,7 +17,7 @@ fn main() { column!(); } #[rustc_builtin_macro] macro_rules! column {() => {}} -fn main() { 0 as u32; } +fn main() { 0u32; } "#]], ); } @@ -74,7 +74,7 @@ fn main() { line!() } #[rustc_builtin_macro] macro_rules! line {() => {}} -fn main() { 0 as u32 } +fn main() { 0u32 } "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index b416f45ff20..2886b2a366c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -970,3 +970,37 @@ builtin #format_args ("{}", &[0 2]); "##]], ); } + +#[test] +fn eager_concat_line() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! concat {} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! line {} + +fn main() { + concat!("event ", line!()); +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! concat {} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! line {} + +fn main() { + "event 0u32"; +} + +"##]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 1f27204c191..361bbec4318 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -13,11 +13,11 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -tracing = "0.1.35" -either = "1.7.0" +tracing.workspace = true +either.workspace = true rustc-hash = "1.1.0" la-arena.workspace = true -itertools = "0.10.5" +itertools.workspace = true hashbrown.workspace = true smallvec.workspace = true triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs index 1906ed15bae..40726505491 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs @@ -99,7 +99,7 @@ register_ast_id_node! { TraitAlias, TypeAlias, Use, - AssocItem, BlockExpr, Variant, RecordField, TupleField, ConstArg + AssocItem, BlockExpr, Variant, RecordField, TupleField, ConstArg, Param, SelfParam } /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 30b19b6e51b..a04de10b899 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -78,7 +78,7 @@ pub fn find_builtin_macro( register_builtin! { LAZY: - (column, Column) => column_expand, + (column, Column) => line_expand, (file, File) => file_expand, (line, Line) => line_expand, (module_path, ModulePath) => module_path_expand, @@ -127,11 +127,13 @@ fn line_expand( _tt: &tt::Subtree, ) -> ExpandResult<tt::Subtree> { // dummy implementation for type-checking purposes - let expanded = quote! { - 0 as u32 - }; - - ExpandResult::ok(expanded) + ExpandResult::ok(tt::Subtree { + delimiter: tt::Delimiter::unspecified(), + token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text: "0u32".into(), + span: tt::Span::UNSPECIFIED, + }))], + }) } fn log_syntax_expand( @@ -164,19 +166,6 @@ fn stringify_expand( ExpandResult::ok(expanded) } -fn column_expand( - _db: &dyn ExpandDatabase, - _id: MacroCallId, - _tt: &tt::Subtree, -) -> ExpandResult<tt::Subtree> { - // dummy implementation for type-checking purposes - let expanded = quote! { - 0 as u32 - }; - - ExpandResult::ok(expanded) -} - fn assert_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 5292a5fa1b1..ff0d279d8cc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -614,9 +614,12 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt err = error.clone().or(err); } - // Set a hard limit for the expanded tt - if let Err(value) = check_tt_count(&tt) { - return value; + // Skip checking token tree limit for include! macro call + if !loc.def.is_include() { + // Set a hard limit for the expanded tt + if let Err(value) = check_tt_count(&tt) { + return value; + } } ExpandResult { value: Arc::new(tt), err } diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index b95ae05ccd4..f2d2451511f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -13,27 +13,27 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true arrayvec = "0.7.2" -bitflags = "2.1.0" +bitflags.workspace = true smallvec.workspace = true ena = "0.14.0" -either = "1.7.0" +either.workspace = true oorandom = "11.1.3" -tracing = "0.1.35" +tracing.workspace = true rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.92.0", default-features = false } -chalk-ir = "0.92.0" -chalk-recursive = { version = "0.92.0", default-features = false } -chalk-derive = "0.92.0" +chalk-solve = { version = "0.94.0", default-features = false } +chalk-ir = "0.94.0" +chalk-recursive = { version = "0.94.0", default-features = false } +chalk-derive = "0.94.0" la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true nohash-hasher.workspace = true typed-arena = "2.0.1" -rustc_index.workspace = true +rustc-dependencies.workspace = true # local deps stdx.workspace = true @@ -47,12 +47,13 @@ limit.workspace = true [dev-dependencies] expect-test = "1.4.0" -tracing = "0.1.35" -tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "registry", -] } -tracing-tree = "0.2.1" +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-tree.workspace = true project-model = { path = "../project-model" } # local deps test-utils.workspace = true + +[features] +in-rust-tree = ["rustc-dependencies/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index c0b243ea248..c9ab356854b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -28,6 +28,7 @@ pub trait TyExt { fn is_unknown(&self) -> bool; fn contains_unknown(&self) -> bool; fn is_ty_var(&self) -> bool; + fn is_union(&self) -> bool; fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_builtin(&self) -> Option<BuiltinType>; @@ -96,6 +97,10 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::InferenceVar(_, _)) } + fn is_union(&self) -> bool { + matches!(self.adt_id(Interner), Some(AdtId(hir_def::AdtId::UnionId(_)))) + } + fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { match self.kind(Interner) { TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 7ad3659a4f6..b395e7f4a81 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -1159,6 +1159,20 @@ fn pattern_matching_slice() { "#, 33213, ); + check_number( + r#" + //- minicore: slice, index, coerce_unsized, copy + const fn f(mut slice: &[u32]) -> usize { + slice = match slice { + [0, rest @ ..] | rest => rest, + }; + slice.len() + } + const GOAL: usize = f(&[]) + f(&[10]) + f(&[0, 100]) + + f(&[1000, 1000, 1000]) + f(&[0, 57, 34, 46, 10000, 10000]); + "#, + 10, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9c96b5ab8db..410bcbf0356 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -20,8 +20,8 @@ use crate::{ method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, - TyDefId, ValueTyDefId, + Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, + TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -47,7 +47,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: DefWithBodyId, subst: Substitution, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Result<Arc<MirBody>, MirLowerError>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] @@ -55,7 +55,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: ClosureId, subst: Substitution, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Result<Arc<MirBody>, MirLowerError>; #[salsa::invoke(crate::mir::borrowck_query)] @@ -81,7 +81,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: GeneralConstId, subst: Substitution, - trait_env: Option<Arc<crate::TraitEnvironment>>, + trait_env: Option<Arc<TraitEnvironment>>, ) -> Result<Const, ConstEvalError>; #[salsa::invoke(crate::consteval::const_eval_static_query)] @@ -104,16 +104,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: AdtId, subst: Substitution, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] #[salsa::cycle(crate::layout::layout_of_ty_recover)] - fn layout_of_ty( - &self, - ty: Ty, - env: Arc<crate::TraitEnvironment>, - ) -> Result<Arc<Layout>, LayoutError>; + fn layout_of_ty(&self, ty: Ty, env: Arc<TraitEnvironment>) -> Result<Arc<Layout>, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>; @@ -121,7 +117,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] fn lookup_impl_method( &self, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution); @@ -149,10 +145,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] - fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>; + fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>; #[salsa::invoke(crate::lower::trait_environment_query)] - fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>; + fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>; #[salsa::invoke(crate::lower::generic_defaults_query)] #[salsa::cycle(crate::lower::generic_defaults_recover)] @@ -249,7 +245,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { fn normalize_projection( &self, projection: crate::ProjectionTy, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Ty; #[salsa::invoke(trait_solve_wait)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs index ef43ed5c463..c1b3619009b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs @@ -11,9 +11,3 @@ pub use crate::diagnostics::{ }, unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr}, }; - -#[derive(Debug, PartialEq, Eq)] -pub struct IncoherentImpl { - pub file_id: hir_expand::HirFileId, - pub impl_: syntax::AstPtr<syntax::ast::Impl>, -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 36d69edf9d5..f4c079b48c5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -9,6 +9,7 @@ //! - constants (e.g. `const FOO: u8 = 10;`) //! - static items (e.g. `static FOO: u8 = 10;`) //! - match arm bindings (e.g. `foo @ Some(_)`) +//! - modules (e.g. `mod foo { ... }` or `mod foo;`) mod case_conv; @@ -18,8 +19,8 @@ use hir_def::{ data::adt::VariantData, hir::{Pat, PatId}, src::HasSource, - AdtId, AttrDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, ItemContainerId, - Lookup, ModuleDefId, StaticId, StructId, + AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, + StaticId, StructId, }; use hir_expand::{ name::{AsName, Name}, @@ -83,6 +84,7 @@ pub enum IdentType { Structure, Variable, Variant, + Module, } impl fmt::Display for IdentType { @@ -97,6 +99,7 @@ impl fmt::Display for IdentType { IdentType::Structure => "Structure", IdentType::Variable => "Variable", IdentType::Variant => "Variant", + IdentType::Module => "Module", }; repr.fmt(f) @@ -132,6 +135,7 @@ impl<'a> DeclValidator<'a> { pub(super) fn validate_item(&mut self, item: ModuleDefId) { match item { + ModuleDefId::ModuleId(module_id) => self.validate_module(module_id), ModuleDefId::FunctionId(func) => self.validate_func(func), ModuleDefId::AdtId(adt) => self.validate_adt(adt), ModuleDefId::ConstId(const_id) => self.validate_const(const_id), @@ -230,6 +234,55 @@ impl<'a> DeclValidator<'a> { || parent() } + fn validate_module(&mut self, module_id: ModuleId) { + // Check whether non-snake case identifiers are allowed for this module. + if self.allowed(module_id.into(), allow::NON_SNAKE_CASE, false) { + return; + } + + // Check the module name. + let Some(module_name) = module_id.name(self.db.upcast()) else { return }; + let module_name_replacement = + module_name.as_str().and_then(to_lower_snake_case).map(|new_name| Replacement { + current_name: module_name, + suggested_text: new_name, + expected_case: CaseType::LowerSnakeCase, + }); + + if let Some(module_name_replacement) = module_name_replacement { + let module_data = &module_id.def_map(self.db.upcast())[module_id.local_id]; + let module_src = module_data.declaration_source(self.db.upcast()); + + if let Some(module_src) = module_src { + let ast_ptr = match module_src.value.name() { + Some(name) => name, + None => { + never!( + "Replacement ({:?}) was generated for a module without a name: {:?}", + module_name_replacement, + module_src + ); + return; + } + }; + + let diagnostic = IncorrectCase { + file: module_src.file_id, + ident_type: IdentType::Module, + ident: AstPtr::new(&ast_ptr), + expected_case: module_name_replacement.expected_case, + ident_text: module_name_replacement + .current_name + .display(self.db.upcast()) + .to_string(), + suggested_text: module_name_replacement.suggested_text, + }; + + self.sink.push(diagnostic); + } + } + } + fn validate_func(&mut self, func: FunctionId) { let data = self.db.function_data(func); if matches!(func.lookup(self.db.upcast()).container, ItemContainerId::ExternBlockId(_)) { @@ -237,8 +290,6 @@ impl<'a> DeclValidator<'a> { return; } - self.validate_body_inner_items(func.into()); - // Check whether non-snake case identifiers are allowed for this function. if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { return; @@ -336,48 +387,44 @@ impl<'a> DeclValidator<'a> { for (id, replacement) in pats_replacements { if let Ok(source_ptr) = source_map.pat_syntax(id) { - if let Some(expr) = source_ptr.value.as_ref().left() { + if let Some(ptr) = source_ptr.value.clone().cast::<ast::IdentPat>() { let root = source_ptr.file_syntax(self.db.upcast()); - if let ast::Pat::IdentPat(ident_pat) = expr.to_node(&root) { - let parent = match ident_pat.syntax().parent() { - Some(parent) => parent, - None => continue, - }; - let name_ast = match ident_pat.name() { - Some(name_ast) => name_ast, - None => continue, - }; + let ident_pat = ptr.to_node(&root); + let parent = match ident_pat.syntax().parent() { + Some(parent) => parent, + None => continue, + }; + let name_ast = match ident_pat.name() { + Some(name_ast) => name_ast, + None => continue, + }; + + let is_param = ast::Param::can_cast(parent.kind()); + + // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, + // because e.g. match arms are patterns as well. + // In other words, we check that it's a named variable binding. + let is_binding = ast::LetStmt::can_cast(parent.kind()) + || (ast::MatchArm::can_cast(parent.kind()) + && ident_pat.at_token().is_some()); + if !(is_param || is_binding) { + // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. + continue; + } - let is_param = ast::Param::can_cast(parent.kind()); - - // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, - // because e.g. match arms are patterns as well. - // In other words, we check that it's a named variable binding. - let is_binding = ast::LetStmt::can_cast(parent.kind()) - || (ast::MatchArm::can_cast(parent.kind()) - && ident_pat.at_token().is_some()); - if !(is_param || is_binding) { - // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. - continue; - } + let ident_type = + if is_param { IdentType::Parameter } else { IdentType::Variable }; - let ident_type = - if is_param { IdentType::Parameter } else { IdentType::Variable }; - - let diagnostic = IncorrectCase { - file: source_ptr.file_id, - ident_type, - ident: AstPtr::new(&name_ast), - expected_case: replacement.expected_case, - ident_text: replacement - .current_name - .display(self.db.upcast()) - .to_string(), - suggested_text: replacement.suggested_text, - }; + let diagnostic = IncorrectCase { + file: source_ptr.file_id, + ident_type, + ident: AstPtr::new(&name_ast), + expected_case: replacement.expected_case, + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), + suggested_text: replacement.suggested_text, + }; - self.sink.push(diagnostic); - } + self.sink.push(diagnostic); } } } @@ -519,11 +566,6 @@ impl<'a> DeclValidator<'a> { fn validate_enum(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); - for (local_id, _) in data.variants.iter() { - let variant_id = EnumVariantId { parent: enum_id, local_id }; - self.validate_body_inner_items(variant_id.into()); - } - // Check whether non-camel case names are allowed for this enum. if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { return; @@ -648,8 +690,6 @@ impl<'a> DeclValidator<'a> { fn validate_const(&mut self, const_id: ConstId) { let data = self.db.const_data(const_id); - self.validate_body_inner_items(const_id.into()); - if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -698,8 +738,6 @@ impl<'a> DeclValidator<'a> { return; } - self.validate_body_inner_items(static_id.into()); - if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -737,17 +775,4 @@ impl<'a> DeclValidator<'a> { self.sink.push(diagnostic); } - - // FIXME: We don't currently validate names within `DefWithBodyId::InTypeConstId`. - /// Recursively validates inner scope items, such as static variables and constants. - fn validate_body_inner_items(&mut self, body_id: DefWithBodyId) { - let body = self.db.body(body_id); - for (_, block_def_map) in body.blocks(self.db.upcast()) { - for (_, module) in block_def_map.modules() { - for def_id in module.scope.declarations() { - self.validate_item(def_id); - } - } - } - } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs index 2c136896209..cbe1af15703 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs @@ -11,50 +11,7 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> { return None; } - // Taken from rustc. - let ret = ident - .trim_matches('_') - .split('_') - .filter(|component| !component.is_empty()) - .map(|component| { - let mut camel_cased_component = String::with_capacity(component.len()); - - let mut new_word = true; - let mut prev_is_lower_case = true; - - for c in component.chars() { - // Preserve the case if an uppercase letter follows a lowercase letter, so that - // `camelCase` is converted to `CamelCase`. - if prev_is_lower_case && c.is_uppercase() { - new_word = true; - } - - if new_word { - camel_cased_component.extend(c.to_uppercase()); - } else { - camel_cased_component.extend(c.to_lowercase()); - } - - prev_is_lower_case = c.is_lowercase(); - new_word = false; - } - - camel_cased_component - }) - .fold((String::new(), None), |(acc, prev): (_, Option<String>), next| { - // separate two components with an underscore if their boundary cannot - // be distinguished using an uppercase/lowercase case distinction - let join = prev - .and_then(|prev| { - let f = next.chars().next()?; - let l = prev.chars().last()?; - Some(!char_has_case(l) && !char_has_case(f)) - }) - .unwrap_or(false); - (acc + if join { "_" } else { "" } + &next, Some(next)) - }) - .0; - Some(ret) + Some(stdx::to_camel_case(ident)) } /// Converts an identifier to a lower_snake_case form. @@ -97,7 +54,9 @@ fn is_camel_case(name: &str) -> bool { && !name.chars().any(|snd| { let ret = match fst { None => false, - Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_', + Some(fst) => { + stdx::char_has_case(fst) && snd == '_' || stdx::char_has_case(snd) && fst == '_' + } }; fst = Some(snd); @@ -135,11 +94,6 @@ fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool { }) } -// Taken from rustc. -fn char_has_case(c: char) -> bool { - c.is_lowercase() || c.is_uppercase() -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index f8cdeaa5e35..2e04bbfee83 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -147,7 +147,7 @@ impl<'a> PatCtxt<'a> { } hir_def::hir::Pat::Bind { id, subpat, .. } => { - let bm = self.infer.binding_modes[id]; + let bm = self.infer.binding_modes[pat]; ty = &self.infer[id]; let name = &self.body.bindings[id].name; match (bm, ty.kind(Interner)) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index f6d6b00d740..9ccf467358e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -945,6 +945,7 @@ impl HirDisplay for Ty { ItemInNs::Types((*def_id).into()), module_id, false, + true, ) { write!(f, "{}", path.display(f.db.upcast()))?; } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 78d3c667a1f..3d5ed1f93c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -420,7 +420,19 @@ pub struct InferenceResult { standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>, - pub binding_modes: ArenaMap<BindingId, BindingMode>, + /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. + /// + /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an + /// or pattern can have multiple binding modes. For example: + /// ``` + /// fn foo(mut slice: &[u32]) -> usize { + /// slice = match slice { + /// [0, rest @ ..] | rest => rest, + /// }; + /// } + /// ``` + /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. + pub binding_modes: ArenaMap<PatId, BindingMode>, pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>, pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>, // FIXME: remove this field diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 13d6b5643ac..af74df1032c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -679,7 +679,7 @@ impl InferenceContext<'_> { | Pat::Range { .. } => { update_result(CaptureKind::ByRef(BorrowKind::Shared)); } - Pat::Bind { id, .. } => match self.result.binding_modes[*id] { + Pat::Bind { id, .. } => match self.result.binding_modes[p] { crate::BindingMode::Move => { if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { update_result(CaptureKind::ByRef(BorrowKind::Shared)); @@ -735,6 +735,32 @@ impl InferenceContext<'_> { self.walk_expr(expr); } + fn restrict_precision_for_unsafe(&mut self) { + for capture in &mut self.current_captures { + let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + capture.place.projections.truncate(0); + continue; + } + for (i, p) in capture.place.projections.iter().enumerate() { + ty = p.projected_ty( + ty, + self.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db.upcast()).krate(), + ); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + capture.place.projections.truncate(i + 1); + break; + } + } + } + } + fn adjust_for_move_closure(&mut self) { for capture in &mut self.current_captures { if let Some(first_deref) = @@ -838,8 +864,8 @@ impl InferenceContext<'_> { | Pat::ConstBlock(_) | Pat::Path(_) | Pat::Lit(_) => self.consume_place(place, pat.into()), - Pat::Bind { id, subpat: _ } => { - let mode = self.result.binding_modes[*id]; + Pat::Bind { id: _, subpat: _ } => { + let mode = self.result.binding_modes[pat]; let capture_kind = match mode { BindingMode::Move => { self.consume_place(place, pat.into()); @@ -924,6 +950,7 @@ impl InferenceContext<'_> { self.result.mutated_bindings_in_closure.insert(item.place.local); } } + self.restrict_precision_for_unsafe(); // closure_kind should be done before adjust_for_move_closure let closure_kind = self.closure_kind(); match capture_by { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 4e28ec06023..7ff12e5b7f8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -421,7 +421,7 @@ impl InferenceContext<'_> { } else { BindingMode::convert(mode) }; - self.result.binding_modes.insert(binding, mode); + self.result.binding_modes.insert(pat, mode); let inner_ty = match subpat { Some(subpat) => self.infer_pat(subpat, &expected, default_bm), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 1a6106c0244..b2591f016d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -1,5 +1,7 @@ //! Compute the binary representation of a type +use std::fmt; + use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ @@ -9,6 +11,10 @@ use hir_def::{ LocalEnumVariantId, LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; +use rustc_dependencies::{ + abi::AddressSpace, + index::{IndexSlice, IndexVec}, +}; use stdx::never; use triomphe::Arc; @@ -22,19 +28,13 @@ pub use self::{ target::target_data_layout_query, }; -macro_rules! user_error { - ($it: expr) => { - return Err(LayoutError::UserError(format!($it).into())) - }; -} - mod adt; mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); -impl rustc_index::vec::Idx for RustcEnumVariantIdx { +impl rustc_dependencies::index::Idx for RustcEnumVariantIdx { fn new(idx: usize) -> Self { RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) } @@ -44,19 +44,63 @@ impl rustc_index::vec::Idx for RustcEnumVariantIdx { } } -pub type Layout = LayoutS<RustcEnumVariantIdx>; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct RustcFieldIdx(pub LocalFieldId); + +impl RustcFieldIdx { + pub fn new(idx: usize) -> Self { + RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32))) + } +} + +impl rustc_dependencies::index::Idx for RustcFieldIdx { + fn new(idx: usize) -> Self { + RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32))) + } + + fn index(self) -> usize { + u32::from(self.0.into_raw()) as usize + } +} + +pub type Layout = LayoutS<RustcFieldIdx, RustcEnumVariantIdx>; pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>; -pub type Variants = hir_def::layout::Variants<RustcEnumVariantIdx>; +pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum LayoutError { - UserError(Box<str>), - SizeOverflow, - TargetLayoutNotAvailable, - HasPlaceholder, + HasErrorConst, HasErrorType, + HasPlaceholder, + InvalidSimdType, NotImplemented, + RecursiveTypeWithoutIndirection, + SizeOverflow, + TargetLayoutNotAvailable, Unknown, + UserReprTooSmall, +} + +impl std::error::Error for LayoutError {} +impl fmt::Display for LayoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"), + LayoutError::HasErrorType => write!(f, "type contains an error"), + LayoutError::HasPlaceholder => write!(f, "type contains placeholders"), + LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"), + LayoutError::NotImplemented => write!(f, "not implemented"), + LayoutError::RecursiveTypeWithoutIndirection => { + write!(f, "recursive type without indirection") + } + LayoutError::SizeOverflow => write!(f, "size overflow"), + LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"), + LayoutError::Unknown => write!(f, "unknown"), + LayoutError::UserReprTooSmall => { + write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum") + } + } + } } struct LayoutCx<'a> { @@ -66,7 +110,7 @@ struct LayoutCx<'a> { impl<'a> LayoutCalculator for LayoutCx<'a> { type TargetDataLayoutRef = &'a TargetDataLayout; - fn delay_bug(&self, txt: &str) { + fn delay_bug(&self, txt: String) { never!("{}", txt); } @@ -95,9 +139,7 @@ fn layout_of_simd_ty( let f0_ty = match fields.iter().next() { Some(it) => it.1.clone().substitute(Interner, subst), - None => { - user_error!("simd type with zero fields"); - } + None => return Err(LayoutError::InvalidSimdType), }; // The element type and number of elements of the SIMD vector @@ -111,7 +153,7 @@ fn layout_of_simd_ty( // Extract the number of elements from the layout of the array field: let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else { - user_error!("Array with non array layout"); + return Err(LayoutError::Unknown); }; (e_ty.clone(), count, true) @@ -123,7 +165,7 @@ fn layout_of_simd_ty( // Compute the ABI of the element type: let e_ly = db.layout_of_ty(e_ty, env.clone())?; let Abi::Scalar(e_abi) = e_ly.abi else { - user_error!("simd type with inner non scalar type"); + return Err(LayoutError::Unknown); }; // Compute the size and alignment of the vector: @@ -145,6 +187,8 @@ fn layout_of_simd_ty( largest_niche: e_ly.largest_niche, size, align, + max_repr_align: None, + unadjusted_abi_align: align.abi, })) } @@ -230,13 +274,11 @@ pub fn layout_of_ty_query( .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone())) .collect::<Result<Vec<_>, _>>()?; let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); - let fields = fields.iter().collect::<Vec<_>>(); + let fields = fields.iter().collect::<IndexVec<_, _>>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from( - "unevaluated or mistyped const generic parameter", - )))? as u64; + let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; let element = db.layout_of_ty(element.clone(), trait_env.clone())?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; @@ -255,6 +297,8 @@ pub fn layout_of_ty_query( largest_niche, align: element.align, size, + max_repr_align: None, + unadjusted_abi_align: element.align.abi, } } TyKind::Slice(element) => { @@ -266,11 +310,23 @@ pub fn layout_of_ty_query( largest_niche: None, align: element.align, size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: element.align.abi, } } + TyKind::Str => Layout { + variants: Variants::Single { index: struct_variant_idx() }, + fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: dl.i8_align.abi, + }, // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { - let mut data_ptr = scalar_unit(dl, Primitive::Pointer); + let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA)); if matches!(ty.kind(Interner), TyKind::Ref(..)) { data_ptr.valid_range_mut().start = 1; } @@ -294,7 +350,7 @@ pub fn layout_of_ty_query( scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) } TyKind::Dyn(..) => { - let mut vtable = scalar_unit(dl, Primitive::Pointer); + let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA)); vtable.valid_range_mut().start = 1; vtable } @@ -308,32 +364,17 @@ pub fn layout_of_ty_query( cx.scalar_pair(data_ptr, metadata) } TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?, - TyKind::Str => Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, - abi: Abi::Aggregate { sized: false }, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }, - TyKind::Never => Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Primitive, - abi: Abi::Uninhabited, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }, + TyKind::Never => cx.layout_of_never_type(), TyKind::Dyn(_) | TyKind::Foreign(_) => { let mut unit = layout_of_unit(&cx, dl)?; match unit.abi { Abi::Aggregate { ref mut sized } => *sized = false, - _ => user_error!("bug"), + _ => return Err(LayoutError::Unknown), } unit } TyKind::Function(_) => { - let mut ptr = scalar_unit(dl, Primitive::Pointer); + let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space)); ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } @@ -363,7 +404,7 @@ pub fn layout_of_ty_query( }) .collect::<Result<Vec<_>, _>>()?; let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); - let fields = fields.iter().collect::<Vec<_>>(); + let fields = fields.iter().collect::<IndexVec<_, _>>(); cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) .ok_or(LayoutError::Unknown)? } @@ -394,13 +435,13 @@ pub fn layout_of_ty_recover( _: &Ty, _: &Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> { - cx.univariant::<RustcEnumVariantIdx, &&Layout>( + cx.univariant::<RustcFieldIdx, RustcEnumVariantIdx, &&Layout>( dl, - &[], + IndexSlice::empty(), &ReprOptions::default(), StructKind::AlwaysSized, ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 85ef649b895..58a06dc6435 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -8,6 +8,7 @@ use hir_def::{ AdtId, EnumVariantId, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; +use rustc_dependencies::index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; @@ -20,8 +21,8 @@ use crate::{ use super::LayoutCx; -pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { - RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0))) +pub(crate) const fn struct_variant_idx() -> RustcEnumVariantIdx { + RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from_u32(0))) } pub fn layout_of_adt_query( @@ -74,7 +75,7 @@ pub fn layout_of_adt_query( .iter() .map(|it| it.iter().map(|it| &**it).collect::<Vec<_>>()) .collect::<SmallVec<[_; 1]>>(); - let variants = variants.iter().map(|it| it.iter().collect()).collect(); + let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>(); let result = if matches!(def, AdtId::UnionId(..)) { cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? } else { @@ -105,7 +106,7 @@ pub fn layout_of_adt_query( && variants .iter() .next() - .and_then(|it| it.last().map(|it| !it.is_unsized())) + .and_then(|it| it.iter().last().map(|it| !it.is_unsized())) .unwrap_or(true), ) .ok_or(LayoutError::SizeOverflow)? @@ -119,7 +120,15 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, let attr = attrs.by_key(name).tt_values(); for tree in attr { if let Some(it) = tree.token_trees.first() { - if let Ok(it) = it.to_string().parse() { + let text = it.to_string().replace('_', ""); + let (text, base) = match text.as_bytes() { + [b'0', b'x', ..] => (&text[2..], 16), + [b'0', b'o', ..] => (&text[2..], 8), + [b'0', b'b', ..] => (&text[2..], 2), + _ => (&*text, 10), + }; + + if let Ok(it) = u128::from_str_radix(text, base) { return Bound::Included(it); } } @@ -136,7 +145,7 @@ pub fn layout_of_adt_recover( _: &Substitution, _: &Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } /// Finds the appropriate Integer type and signedness for the given @@ -160,11 +169,7 @@ fn repr_discr( let discr = Integer::from_attr(dl, ity); let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; if discr < fit { - return Err(LayoutError::UserError( - "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum " - .into(), - )); + return Err(LayoutError::UserReprTooSmall); } return Ok((discr, ity.is_signed())); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index ffdbb9de934..5e3a86c80e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -210,16 +210,13 @@ fn recursive() { struct BoxLike<T: ?Sized>(*mut T); struct Goal(BoxLike<Goal>); } - check_fail( - r#"struct Goal(Goal);"#, - LayoutError::UserError("infinite sized recursive type".into()), - ); + check_fail(r#"struct Goal(Goal);"#, LayoutError::RecursiveTypeWithoutIndirection); check_fail( r#" struct Foo<T>(Foo<T>); struct Goal(Foo<i32>); "#, - LayoutError::UserError("infinite sized recursive type".into()), + LayoutError::RecursiveTypeWithoutIndirection, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs index bbe855a14de..939025461f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs @@ -186,9 +186,9 @@ fn capture_specific_fields() { fn match_pattern() { size_and_align_expr! { struct X(i64, i32, (u8, i128)); - let y: X = X(2, 5, (7, 3)); + let _y: X = X(2, 5, (7, 3)); move |x: i64| { - match y { + match _y { _ => x, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 405bb001b5d..bcf7bfa0d27 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -1,6 +1,7 @@ //! The type system. We currently use this to infer types for completion, hover //! information and various assists. #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[allow(unused)] macro_rules! eprintln { @@ -80,6 +81,7 @@ pub use mapping::{ lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, to_placeholder_idx, }; +pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; pub use utils::{all_super_traits, is_fn_unsafe_to_call}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 9a61f153599..c8a85b4a9ff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1383,51 +1383,50 @@ pub(crate) fn generic_predicates_for_param_query( let ctx = TyLoweringContext::new(db, &resolver, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); let generics = generics(db.upcast(), def); - let mut predicates: Vec<_> = resolver - .where_predicates_in_scope() - // we have to filter out all other predicates *first*, before attempting to lower them - .filter(|pred| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound, .. } => { - match target { - WherePredicateTypeTarget::TypeRef(type_ref) => { - if ctx.lower_ty_only_param(type_ref) != Some(param_id) { - return false; - } - } - &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { - let target_id = TypeOrConstParamId { parent: def, local_id }; - if target_id != param_id { - return false; - } - } - }; - match &**bound { - TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => { - // Only lower the bound if the trait could possibly define the associated - // type we're looking for. + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &&_| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = match target { + WherePredicateTypeTarget::TypeRef(type_ref) => { + ctx.lower_ty_only_param(type_ref) != Some(param_id) + } + &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { + let target_id = TypeOrConstParamId { parent: def, local_id }; + target_id != param_id + } + }; + if invalid_target { + return false; + } + + match &**bound { + TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. - let assoc_name = match &assoc_name { - Some(it) => it, - None => return true, - }; - let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) { - Some(TypeNs::TraitId(tr)) => tr, - _ => return false, - }; + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db.upcast(), path) + else { + return false; + }; - all_super_traits(db.upcast(), tr).iter().any(|tr| { - db.trait_data(*tr).items.iter().any(|(name, item)| { - matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name - }) + all_super_traits(db.upcast(), tr).iter().any(|tr| { + db.trait_data(*tr).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name }) - } - TypeBound::Lifetime(_) | TypeBound::Error => false, + }) } + TypeBound::Lifetime(_) | TypeBound::Error => false, } - WherePredicate::Lifetime { .. } => false, - }) + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates: Vec<_> = resolver + .where_predicates_in_scope() + .filter(predicate) .flat_map(|pred| { ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p)) }) @@ -1519,7 +1518,12 @@ pub(crate) fn trait_environment_query( let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); - Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: traits_in_scope, env }) + Arc::new(TraitEnvironment { + krate, + block: None, + traits_from_clauses: traits_in_scope.into_boxed_slice(), + env, + }) } /// Resolve the where clause(s) of an item with generics. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index f3a5f69b2a6..87c93283361 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -862,6 +862,62 @@ fn is_inherent_impl_coherent( } } +/// Checks whether the impl satisfies the orphan rules. +/// +/// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an `impl`` is valid only if at least one of the following is true: +/// - Trait is a local trait +/// - All of +/// - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. +/// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) +pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { + let substs = TyBuilder::placeholder_subst(db, impl_); + let Some(impl_trait) = db.impl_trait(impl_) else { + // not a trait impl + return true; + }; + + let local_crate = impl_.lookup(db.upcast()).container.krate(); + let is_local = |tgt_crate| tgt_crate == local_crate; + + let trait_ref = impl_trait.substitute(Interner, &substs); + let trait_id = from_chalk_trait_id(trait_ref.trait_id); + if is_local(trait_id.module(db.upcast()).krate()) { + // trait to be implemented is local + return true; + } + + let unwrap_fundamental = |ty: Ty| match ty.kind(Interner) { + TyKind::Ref(_, _, referenced) => referenced.clone(), + &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref subs) => { + let struct_data = db.struct_data(s); + if struct_data.flags.contains(StructFlags::IS_FUNDAMENTAL) { + let next = subs.type_parameters(Interner).next(); + match next { + Some(ty) => ty, + None => ty, + } + } else { + ty + } + } + _ => ty, + }; + // - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. + let is_not_orphan = trait_ref.substitution.type_parameters(Interner).any(|ty| { + match unwrap_fundamental(ty).kind(Interner) { + &TyKind::Adt(AdtId(id), _) => is_local(id.module(db.upcast()).krate()), + TyKind::Error => true, + TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { + is_local(from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()) + }), + _ => false, + } + }); + // FIXME: param coverage + // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) + is_not_orphan +} + pub fn iterate_path_candidates( ty: &Canonical<Ty>, db: &dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index e953058ccca..747ca54858c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -243,16 +243,16 @@ impl Default for ProjectionStore { } impl ProjectionStore { - fn shrink_to_fit(&mut self) { + pub fn shrink_to_fit(&mut self) { self.id_to_proj.shrink_to_fit(); self.proj_to_id.shrink_to_fit(); } - fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> { + pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> { self.proj_to_id.get(projection).copied() } - fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { + pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { let new_id = ProjectionId(self.proj_to_id.len() as u32); match self.proj_to_id.entry(projection) { Entry::Occupied(id) => *id.get(), @@ -267,20 +267,20 @@ impl ProjectionStore { } impl ProjectionId { - const EMPTY: ProjectionId = ProjectionId(0); + pub const EMPTY: ProjectionId = ProjectionId(0); - fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { + pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { store.id_to_proj.get(&self).unwrap() } - fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { + pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { let mut current = self.lookup(store).to_vec(); current.push(projection); store.intern(current.into()) } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, pub projection: ProjectionId, @@ -1007,7 +1007,7 @@ pub enum Rvalue { #[derive(Debug, PartialEq, Eq, Clone)] pub enum StatementKind { Assign(Place, Rvalue), - //FakeRead(Box<(FakeReadCause, Place)>), + FakeRead(Place), //SetDiscriminant { // place: Box<Place>, // variant_index: VariantIdx, @@ -1109,7 +1109,9 @@ impl MirBody { } } } - StatementKind::Deinit(p) => f(p, &mut self.projection_store), + StatementKind::FakeRead(p) | StatementKind::Deinit(p) => { + f(p, &mut self.projection_store) + } StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 41fb129652a..74c5efd6c3f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -24,6 +24,7 @@ use super::{ pub enum MutabilityReason { Mut { spans: Vec<MirSpan> }, Not, + Unused, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -144,7 +145,8 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> } } }, - StatementKind::Deinit(_) + StatementKind::FakeRead(_) + | StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -264,7 +266,10 @@ fn ever_initialized_map( is_ever_initialized = false; } } - StatementKind::Deinit(_) | StatementKind::Nop | StatementKind::StorageLive(_) => (), + StatementKind::Deinit(_) + | StatementKind::FakeRead(_) + | StatementKind::Nop + | StatementKind::StorageLive(_) => (), } } let Some(terminator) = &block.terminator else { @@ -331,16 +336,37 @@ fn ever_initialized_map( result } +fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) { + match &mut result[local] { + MutabilityReason::Mut { spans } => spans.push(span), + it @ (MutabilityReason::Not | MutabilityReason::Unused) => { + *it = MutabilityReason::Mut { spans: vec![span] } + } + }; +} + +fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) { + match &mut result[local] { + it @ MutabilityReason::Unused => { + *it = MutabilityReason::Not; + } + _ => (), + }; +} + +fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) { + if let Operand::Copy(p) | Operand::Move(p) = arg { + record_usage(p.local, result); + } +} + fn mutability_of_locals( db: &dyn HirDatabase, body: &MirBody, ) -> ArenaMap<LocalId, MutabilityReason> { let mut result: ArenaMap<LocalId, MutabilityReason> = - body.locals.iter().map(|it| (it.0, MutabilityReason::Not)).collect(); - let mut push_mut_span = |local, span| match &mut result[local] { - MutabilityReason::Mut { spans } => spans.push(span), - it @ MutabilityReason::Not => *it = MutabilityReason::Mut { spans: vec![span] }, - }; + body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect(); + let ever_init_maps = ever_initialized_map(db, body); for (block_id, mut ever_init_map) in ever_init_maps.into_iter() { let block = &body.basic_blocks[block_id]; @@ -350,23 +376,51 @@ fn mutability_of_locals( match place_case(db, body, place) { ProjectionCase::Direct => { if ever_init_map.get(place.local).copied().unwrap_or_default() { - push_mut_span(place.local, statement.span); + push_mut_span(place.local, statement.span, &mut result); } else { ever_init_map.insert(place.local, true); } } ProjectionCase::DirectPart => { // Partial initialization is not supported, so it is definitely `mut` - push_mut_span(place.local, statement.span); + push_mut_span(place.local, statement.span, &mut result); + } + ProjectionCase::Indirect => { + record_usage(place.local, &mut result); } - ProjectionCase::Indirect => (), + } + match value { + Rvalue::CopyForDeref(p) + | Rvalue::Discriminant(p) + | Rvalue::Len(p) + | Rvalue::Ref(_, p) => { + record_usage(p.local, &mut result); + } + Rvalue::Use(o) + | Rvalue::Repeat(o, _) + | Rvalue::Cast(_, o, _) + | Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result), + Rvalue::CheckedBinaryOp(_, o1, o2) => { + for o in [o1, o2] { + record_usage_for_operand(o, &mut result); + } + } + Rvalue::Aggregate(_, args) => { + for arg in args.iter() { + record_usage_for_operand(arg, &mut result); + } + } + Rvalue::ShallowInitBox(_, _) | Rvalue::ShallowInitBoxWithAlloc(_) => (), } if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value { if place_case(db, body, p) != ProjectionCase::Indirect { - push_mut_span(p.local, statement.span); + push_mut_span(p.local, statement.span, &mut result); } } } + StatementKind::FakeRead(p) => { + record_usage(p.local, &mut result); + } StatementKind::StorageDead(p) => { ever_init_map.insert(*p, false); } @@ -386,15 +440,21 @@ fn mutability_of_locals( | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop - | TerminatorKind::SwitchInt { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Assert { .. } | TerminatorKind::Yield { .. } => (), - TerminatorKind::Call { destination, .. } => { + TerminatorKind::SwitchInt { discr, targets: _ } => { + record_usage_for_operand(discr, &mut result); + } + TerminatorKind::Call { destination, args, func, .. } => { + record_usage_for_operand(func, &mut result); + for arg in args.iter() { + record_usage_for_operand(arg, &mut result); + } if destination.projection.lookup(&body.projection_store).len() == 0 { if ever_init_map.get(destination.local).copied().unwrap_or_default() { - push_mut_span(destination.local, MirSpan::Unknown); + push_mut_span(destination.local, MirSpan::Unknown, &mut result); } else { ever_init_map.insert(destination.local, true); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 4364e0d323b..62efb858511 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -162,7 +162,7 @@ pub struct Evaluator<'a> { not_special_fn_cache: RefCell<FxHashSet<FunctionId>>, mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, Substitution), MirOrDynIndex>>, /// Constantly dropping and creating `Locals` is very costly. We store - /// old locals that we normaly want to drop here, to reuse their allocations + /// old locals that we normally want to drop here, to reuse their allocations /// later. unused_locals_store: RefCell<FxHashMap<DefWithBodyId, Vec<Locals>>>, cached_ptr_size: usize, @@ -375,10 +375,7 @@ impl MirEvalError { Err(_) => continue, }, MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|it| match it { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), + Ok(s) => s.map(|it| it.syntax_node_ptr()), Err(_) => continue, }, MirSpan::Unknown => continue, @@ -842,6 +839,7 @@ impl Evaluator<'_> { } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) + | StatementKind::FakeRead(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), } @@ -2301,7 +2299,7 @@ impl Evaluator<'_> { match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { MirOrDynIndex::Dyn(self_ty_idx) => { // In the layout of current possible receiver, which at the moment of writing this code is one of - // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers, + // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible receivers, // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on // the type. let first_arg = arg_bytes.clone().next().unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 803ef631f1e..2de99e41659 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1045,7 +1045,7 @@ impl Evaluator<'_> { } "transmute" => { let [arg] = args else { - return Err(MirEvalError::TypeError("trasmute arg is not provided")); + return Err(MirEvalError::TypeError("transmute arg is not provided")); }; destination.write_from_interval(self, arg.interval) } @@ -1065,7 +1065,7 @@ impl Evaluator<'_> { } "ctlz" | "ctlz_nonzero" => { let [arg] = args else { - return Err(MirEvalError::TypeError("cttz arg is not provided")); + return Err(MirEvalError::TypeError("ctlz arg is not provided")); }; let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).leading_zeros() as usize; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index dd2dba717f9..9905d522146 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -529,6 +529,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; + self.push_fake_read(current, cond_place, expr_id.into()); let (then_target, else_target) = self.pattern_match(current, None, cond_place, *pat)?; self.write_bytes_to_place( @@ -668,6 +669,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; + self.push_fake_read(current, cond_place, expr_id.into()); let mut end = None; for MatchArm { pat, guard, expr } in arms.iter() { let (then, mut otherwise) = @@ -1299,6 +1301,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); }; if matches!(&self.body.exprs[lhs], Expr::Underscore) { + self.push_fake_read_for_operand(current, rhs_op, span); return Ok(Some(current)); } if matches!( @@ -1575,6 +1578,16 @@ impl<'ctx> MirLowerCtx<'ctx> { self.result.basic_blocks[block].statements.push(statement); } + fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { + self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); + } + + fn push_fake_read_for_operand(&mut self, block: BasicBlockId, operand: Operand, span: MirSpan) { + if let Operand::Move(p) | Operand::Copy(p) = operand { + self.push_fake_read(block, p, span); + } + } + fn push_assignment( &mut self, block: BasicBlockId, @@ -1733,6 +1746,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); }; current = c; + self.push_fake_read(current, init_place, span); (current, else_block) = self.pattern_match(current, None, init_place, *pat)?; match (else_block, else_branch) { @@ -1760,13 +1774,14 @@ impl<'ctx> MirLowerCtx<'ctx> { } } } - hir_def::hir::Statement::Expr { expr, has_semi: _ } => { + &hir_def::hir::Statement::Expr { expr, has_semi: _ } => { let scope2 = self.push_drop_scope(); - let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((p, c)) = self.lower_expr_as_place(current, expr, true)? else { scope2.pop_assume_dropped(self); scope.pop_assume_dropped(self); return Ok(None); }; + self.push_fake_read(c, p, expr.into()); current = scope2.pop_and_drop(self, c); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 270f75ad967..1120bb1c112 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -284,6 +284,7 @@ impl MirLowerCtx<'_> { ); (current, current_else) = self.pattern_match_binding( id, + *slice, next_place, (*slice).into(), current, @@ -395,6 +396,7 @@ impl MirLowerCtx<'_> { if mode == MatchingMode::Bind { self.pattern_match_binding( *id, + pattern, cond_place, pattern.into(), current, @@ -431,13 +433,14 @@ impl MirLowerCtx<'_> { fn pattern_match_binding( &mut self, id: BindingId, + pat: PatId, cond_place: Place, span: MirSpan, current: BasicBlockId, current_else: Option<BasicBlockId>, ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { let target_place = self.binding_local(id)?; - let mode = self.infer.binding_modes[id]; + let mode = self.infer.binding_modes[pat]; self.push_storage_live(id, current)?; self.push_assignment( current, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index df16d0d8201..7d2bb95d931 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -248,6 +248,7 @@ impl Filler<'_> { | Rvalue::CopyForDeref(_) => (), }, StatementKind::Deinit(_) + | StatementKind::FakeRead(_) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 0108859ff32..6e42bee97f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -233,6 +233,11 @@ impl<'a> MirPrettyCtx<'a> { this.place(p); wln!(this, ");"); } + StatementKind::FakeRead(p) => { + w!(this, "FakeRead("); + this.place(p); + wln!(this, ");"); + } StatementKind::Nop => wln!(this, "Nop;"), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index d22d0d85c8e..1446e83fa88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -269,12 +269,7 @@ fn pat_node( Some(match body_source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); - sp.map(|ptr| { - ptr.either( - |it| it.to_node(&root).syntax().clone(), - |it| it.to_node(&root).syntax().clone(), - ) - }) + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => return None, }) @@ -303,12 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let syntax_ptr = match body_source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); - sp.map(|ptr| { - ptr.either( - |it| it.to_node(&root).syntax().clone(), - |it| it.to_node(&root).syntax().clone(), - ) - }) + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 1e6e946a13f..d16e0eb0137 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -684,8 +684,7 @@ fn infer_builtin_macros_line() { } "#, expect![[r#" - !0..1 '0': i32 - !0..6 '0asu32': u32 + !0..4 '0u32': u32 63..87 '{ ...!(); }': () 73..74 'x': u32 "#]], @@ -723,8 +722,7 @@ fn infer_builtin_macros_column() { } "#, expect![[r#" - !0..1 '0': i32 - !0..6 '0asu32': u32 + !0..4 '0u32': u32 65..91 '{ ...!(); }': () 75..76 'x': u32 "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index d36b885ec14..48dd9540329 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4439,42 +4439,42 @@ fn test(v: S<i32>) { fn associated_type_in_argument() { check( r#" - trait A { - fn m(&self) -> i32; - } +trait A { + fn m(&self) -> i32; +} - fn x<T: B>(k: &<T as B>::Ty) { - k.m(); - } +fn x<T: B>(k: &<T as B>::Ty) { + k.m(); +} - struct X; - struct Y; +struct X; +struct Y; - impl A for X { - fn m(&self) -> i32 { - 8 - } +impl A for X { + fn m(&self) -> i32 { + 8 } +} - impl A for Y { - fn m(&self) -> i32 { - 32 - } +impl A for Y { + fn m(&self) -> i32 { + 32 } +} - trait B { - type Ty: A; - } +trait B { + type Ty: A; +} - impl B for u16 { - type Ty = X; - } +impl B for u16 { + type Ty = X; +} - fn ttt() { - let inp = Y; - x::<u16>(&inp); - //^^^^ expected &X, got &Y - } - "#, +fn ttt() { + let inp = Y; + x::<u16>(&inp); + //^^^^ expected &X, got &Y +} +"#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 3c7cfbaed3a..467b94a2662 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -48,7 +48,7 @@ pub struct TraitEnvironment { pub krate: CrateId, pub block: Option<BlockId>, // FIXME make this a BTreeMap - pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, + pub(crate) traits_from_clauses: Box<[(Ty, TraitId)]>, pub env: chalk_ir::Environment<Interner>, } @@ -57,7 +57,7 @@ impl TraitEnvironment { TraitEnvironment { krate, block: None, - traits_from_clauses: Vec::new(), + traits_from_clauses: Box::default(), env: chalk_ir::Environment::new(Interner), } } diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index f860ee94845..4c1dfbc294e 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -13,9 +13,9 @@ doctest = false [dependencies] rustc-hash = "1.1.0" -either = "1.7.0" +either.workspace = true arrayvec = "0.7.2" -itertools = "0.10.5" +itertools.workspace = true smallvec.workspace = true triomphe.workspace = true once_cell = "1.17.1" @@ -30,3 +30,6 @@ profile.workspace = true stdx.workspace = true syntax.workspace = true tt.workspace = true + +[features] +in-rust-tree = [] diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 479138b67f8..cf9a2b73d9b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -3,7 +3,7 @@ //! //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. -pub use hir_ty::diagnostics::{CaseType, IncoherentImpl, IncorrectCase}; +pub use hir_ty::diagnostics::{CaseType, IncorrectCase}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -53,6 +53,9 @@ diagnostics![ PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, + TraitImplIncorrectSafety, + TraitImplMissingAssocItems, + TraitImplOrphan, TypedHole, TypeMismatch, UndeclaredLabel, @@ -66,6 +69,7 @@ diagnostics![ UnresolvedModule, UnresolvedProcMacro, UnusedMut, + UnusedVariable, ]; #[derive(Debug)] @@ -173,20 +177,19 @@ pub struct MalformedDerive { #[derive(Debug)] pub struct NoSuchField { - pub field: InFile<Either<AstPtr<ast::RecordExprField>, AstPtr<ast::RecordPatField>>>, + pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>, pub private: bool, } #[derive(Debug)] pub struct PrivateAssocItem { - pub expr_or_pat: - InFile<Either<AstPtr<ast::Expr>, Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>>>, + pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>, pub item: AssocItem, } #[derive(Debug)] pub struct MismatchedTupleStructPatArgCount { - pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>, + pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, pub expected: usize, pub found: usize, } @@ -227,7 +230,7 @@ pub struct MissingUnsafe { #[derive(Debug)] pub struct MissingFields { pub file: HirFileId, - pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>, + pub field_list_parent: AstPtr<Either<ast::RecordExpr, ast::RecordPat>>, pub field_list_parent_path: Option<AstPtr<ast::Path>>, pub missed_fields: Vec<Name>, } @@ -254,7 +257,7 @@ pub struct MissingMatchArms { #[derive(Debug)] pub struct TypeMismatch { - pub expr_or_pat: Either<InFile<AstPtr<ast::Expr>>, InFile<AstPtr<ast::Pat>>>, + pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, pub expected: Type, pub actual: Type, } @@ -271,7 +274,39 @@ pub struct UnusedMut { } #[derive(Debug)] +pub struct UnusedVariable { + pub local: Local, +} + +#[derive(Debug)] pub struct MovedOutOfRef { pub ty: Type, pub span: InFile<SyntaxNodePtr>, } + +#[derive(Debug, PartialEq, Eq)] +pub struct IncoherentImpl { + pub file_id: HirFileId, + pub impl_: AstPtr<ast::Impl>, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplOrphan { + pub file_id: HirFileId, + pub impl_: AstPtr<ast::Impl>, +} + +// FIXME: Split this off into the corresponding 4 rustc errors +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplIncorrectSafety { + pub file_id: HirFileId, + pub impl_: AstPtr<ast::Impl>, + pub should_be_safe: bool, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplMissingAssocItems { + pub file_id: HirFileId, + pub impl_: AstPtr<ast::Impl>, + pub missing: Vec<(Name, AssocItem)>, +} diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index ac171026d5d..5847c8a9fb5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -1,6 +1,6 @@ //! HirDisplay implementations for various hir types. use hir_def::{ - data::adt::VariantData, + data::adt::{StructKind, VariantData}, generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, @@ -163,7 +163,40 @@ impl HirDisplay for Struct { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::StructId(self.id)); write_generic_params(def_id, f)?; + + let variant_data = self.variant_data(f.db); + if let StructKind::Tuple = variant_data.kind() { + f.write_char('(')?; + let mut it = variant_data.fields().iter().peekable(); + + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; + field.ty(f.db).hir_fmt(f)?; + if it.peek().is_some() { + f.write_str(", ")?; + } + } + + f.write_str(");")?; + } + write_where_clause(def_id, f)?; + + if let StructKind::Record = variant_data.kind() { + let fields = self.fields(f.db); + if fields.is_empty() { + f.write_str(" {}")?; + } else { + f.write_str(" {\n")?; + for field in self.fields(f.db) { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; + } + } + Ok(()) } } @@ -176,6 +209,18 @@ impl HirDisplay for Enum { let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; + + let variants = self.variants(f.db); + if !variants.is_empty() { + f.write_str(" {\n")?; + for variant in variants { + f.write_str(" ")?; + variant.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; + } + Ok(()) } } @@ -188,6 +233,18 @@ impl HirDisplay for Union { let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; + + let fields = self.fields(f.db); + if !fields.is_empty() { + f.write_str(" {\n")?; + for field in self.fields(f.db) { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; + } + Ok(()) } } @@ -559,7 +616,7 @@ impl HirDisplay for TypeAlias { write_where_clause(def_id, f)?; if !data.bounds.is_empty() { f.write_str(": ")?; - f.write_joined(&data.bounds, " + ")?; + f.write_joined(data.bounds.iter(), " + ")?; } if let Some(ty) = &data.type_ref { f.write_str(" = ")?; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index b215ed38f28..bdc11aa3561 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -18,6 +18,7 @@ //! <https://www.tedinski.com/2018/02/06/system-boundaries.html>. #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "512"] mod semantics; @@ -33,7 +34,7 @@ pub mod symbols; mod display; -use std::{iter, ops::ControlFlow}; +use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; @@ -53,18 +54,18 @@ use hir_def::{ resolver::{HasResolver, Resolver}, src::HasSource as _, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, - EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, HasModule, ImplId, - InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, - MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, - TypeOrConstParamId, TypeParamId, UnionId, + EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, + ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, + Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, + TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{name::name, MacroCallKind}; use hir_ty::{ - all_super_traits, autoderef, + all_super_traits, autoderef, check_orphan_rules, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, known_const_to_ast, - layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, + layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, @@ -89,17 +90,7 @@ use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ attrs::{resolve_doc_path_on, HasAttrs}, - diagnostics::{ - AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, - IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, - MacroExpansionParseError, MalformedDerive, MismatchedArgCount, - MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, - MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, - ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, - UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, - UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, - UnresolvedProcMacro, UnusedMut, - }, + diagnostics::*, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, }; @@ -452,15 +443,7 @@ impl HasVisibility for ModuleDef { impl Module { /// Name of this module. pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { - let def_map = self.id.def_map(db.upcast()); - let parent = def_map[self.id.local_id].parent?; - def_map[parent].children.iter().find_map(|(name, module_id)| { - if *module_id == self.id.local_id { - Some(name.clone()) - } else { - None - } - }) + self.id.name(db.upcast()) } /// Returns the crate this module is part of. @@ -571,6 +554,7 @@ impl Module { if def_map[m.id.local_id].origin.is_inline() { m.diagnostics(db, acc) } + acc.extend(def.diagnostics(db)) } ModuleDef::Trait(t) => { for diag in db.trait_data_with_diagnostics(t.id).1.iter() { @@ -610,6 +594,7 @@ impl Module { let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); + let mut impl_assoc_items_scratch = vec![]; for impl_def in self.impl_defs(db) { let loc = impl_def.id.lookup(db.upcast()); let tree = loc.id.item_tree(db.upcast()); @@ -620,19 +605,109 @@ impl Module { // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; } + let ast_id_map = db.ast_id_map(file_id); for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { emit_def_diagnostic(db, acc, diag); } if inherent_impls.invalid_impls().contains(&impl_def.id) { - let ast_id_map = db.ast_id_map(file_id); - acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into()) } - for item in impl_def.items(db) { - let def: DefWithBody = match item { + if !impl_def.check_orphan_rules(db) { + acc.push(TraitImplOrphan { impl_: ast_id_map.get(node.ast_id()), file_id }.into()) + } + + let trait_ = impl_def.trait_(db); + let trait_is_unsafe = trait_.map_or(false, |t| t.is_unsafe(db)); + let impl_is_negative = impl_def.is_negative(db); + let impl_is_unsafe = impl_def.is_unsafe(db); + + let drop_maybe_dangle = (|| { + // FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper + let trait_ = trait_?; + let drop_trait = db.lang_item(self.krate().into(), LangItem::Drop)?.as_trait()?; + if drop_trait != trait_.into() { + return None; + } + let parent = impl_def.id.into(); + let generic_params = db.generic_params(parent); + let lifetime_params = generic_params.lifetimes.iter().map(|(local_id, _)| { + GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }) + }); + let type_params = generic_params + .iter() + .filter(|(_, it)| it.type_param().is_some()) + .map(|(local_id, _)| { + GenericParamId::TypeParamId(TypeParamId::from_unchecked( + TypeOrConstParamId { parent, local_id }, + )) + }); + let res = type_params + .chain(lifetime_params) + .any(|p| db.attrs(AttrDefId::GenericParamId(p)).by_key("may_dangle").exists()); + Some(res) + })() + .unwrap_or(false); + + match (impl_is_unsafe, trait_is_unsafe, impl_is_negative, drop_maybe_dangle) { + // unsafe negative impl + (true, _, true, _) | + // unsafe impl for safe trait + (true, false, _, false) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: true }.into()), + // safe impl for unsafe trait + (false, true, false, _) | + // safe impl of dangling drop + (false, false, _, true) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: false }.into()), + _ => (), + }; + + if let Some(trait_) = trait_ { + let items = &db.trait_data(trait_.into()).items; + let required_items = items.iter().filter(|&(_, assoc)| match *assoc { + AssocItemId::FunctionId(it) => !db.function_data(it).has_body(), + AssocItemId::ConstId(_) => true, + AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), + }); + impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().map( + |&item| { + ( + item, + match item { + AssocItemId::FunctionId(it) => db.function_data(it).name.clone(), + AssocItemId::ConstId(it) => { + db.const_data(it).name.as_ref().unwrap().clone() + } + AssocItemId::TypeAliasId(it) => db.type_alias_data(it).name.clone(), + }, + ) + }, + )); + + let missing: Vec<_> = required_items + .filter(|(name, id)| { + !impl_assoc_items_scratch.iter().any(|(impl_item, impl_name)| { + discriminant(impl_item) == discriminant(id) && impl_name == name + }) + }) + .map(|(name, item)| (name.clone(), AssocItem::from(*item))) + .collect(); + if !missing.is_empty() { + acc.push( + TraitImplMissingAssocItems { + impl_: ast_id_map.get(node.ast_id()), + file_id, + missing, + } + .into(), + ) + } + impl_assoc_items_scratch.clear(); + } + + for &item in &db.impl_data(impl_def.id).items { + let def: DefWithBody = match AssocItem::from(item) { AssocItem::Function(it) => it.into(), AssocItem::Const(it) => it.into(), AssocItem::TypeAlias(_) => continue, @@ -671,8 +746,15 @@ impl Module { db: &dyn DefDatabase, item: impl Into<ItemInNs>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { - hir_def::find_path::find_path(db, item.into().into(), self.into(), prefer_no_std) + hir_def::find_path::find_path( + db, + item.into().into(), + self.into(), + prefer_no_std, + prefer_prelude, + ) } /// Finds a path that can be used to refer to the given item from within @@ -683,6 +765,7 @@ impl Module { item: impl Into<ItemInNs>, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { hir_def::find_path::find_path_prefixed( db, @@ -690,6 +773,7 @@ impl Module { self.into(), prefix_kind, prefer_no_std, + prefer_prelude, ) } } @@ -1453,9 +1537,7 @@ impl DefWithBody { let (body, source_map) = db.body_with_source_map(self.into()); for (_, def_map) in body.blocks(db.upcast()) { - for diag in def_map.diagnostics() { - emit_def_diagnostic(db, acc, diag); - } + Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc); } for diag in source_map.diagnostics() { @@ -1509,10 +1591,10 @@ impl DefWithBody { &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { - source_map.field_syntax(expr).map(Either::Left) + source_map.field_syntax(expr).map(AstPtr::wrap_left) } ExprOrPatId::PatId(pat) => { - source_map.pat_field_syntax(pat).map(Either::Right) + source_map.pat_field_syntax(pat).map(AstPtr::wrap_right) } }; acc.push(NoSuchField { field: expr_or_pat, private }.into()) @@ -1530,8 +1612,8 @@ impl DefWithBody { } &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => { let expr_or_pat = match id { - ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), - ExprOrPatId::PatId(pat) => pat_syntax(pat).map(Either::Right), + ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left), + ExprOrPatId::PatId(pat) => pat_syntax(pat).map(AstPtr::wrap_right), }; let item = item.into(); acc.push(PrivateAssocItem { expr_or_pat, item }.into()) @@ -1609,12 +1691,17 @@ impl DefWithBody { found, } => { let expr_or_pat = match pat { - ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), - ExprOrPatId::PatId(pat) => source_map - .pat_syntax(pat) - .expect("unexpected synthetic") - .map(|it| it.unwrap_left()) - .map(Either::Right), + ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(AstPtr::wrap_left), + ExprOrPatId::PatId(pat) => { + let InFile { file_id, value } = + source_map.pat_syntax(pat).expect("unexpected synthetic"); + + // cast from Either<Pat, SelfParam> -> Either<_, Pat> + let Some(ptr) = AstPtr::try_from_raw(value.syntax_node_ptr()) else { + continue; + }; + InFile { file_id, value: ptr } + } }; acc.push( MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(), @@ -1628,11 +1715,15 @@ impl DefWithBody { ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right), }; let expr_or_pat = match expr_or_pat { - Ok(Either::Left(expr)) => Either::Left(expr), - Ok(Either::Right(InFile { file_id, value: Either::Left(pat) })) => { - Either::Right(InFile { file_id, value: pat }) + Ok(Either::Left(expr)) => expr.map(AstPtr::wrap_left), + Ok(Either::Right(InFile { file_id, value: pat })) => { + // cast from Either<Pat, SelfParam> -> Either<_, Pat> + let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else { + continue; + }; + InFile { file_id, value: ptr } } - Ok(Either::Right(_)) | Err(SyntheticSyntax) => continue, + Err(SyntheticSyntax) => continue, }; acc.push( @@ -1667,10 +1758,7 @@ impl DefWithBody { Err(_) => continue, }, mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) { - Ok(s) => s.map(|it| match it { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, mir::MirSpan::Unknown => continue, @@ -1697,9 +1785,20 @@ impl DefWithBody { // Skip synthetic bindings continue; } - let need_mut = &mol[local]; + let mut need_mut = &mol[local]; + if body[binding_id].name.as_str() == Some("self") + && need_mut == &mir::MutabilityReason::Unused + { + need_mut = &mir::MutabilityReason::Not; + } let local = Local { parent: self.into(), binding_id }; match (need_mut, local.is_mut(db)) { + (mir::MutabilityReason::Unused, _) => { + let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with("_")); + if !should_ignore { + acc.push(UnusedVariable { local }.into()) + } + } (mir::MutabilityReason::Mut { .. }, true) | (mir::MutabilityReason::Not, false) => (), (mir::MutabilityReason::Mut { spans }, false) => { @@ -1710,10 +1809,7 @@ impl DefWithBody { Err(_) => continue, }, mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|it| match it { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), + Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, mir::MirSpan::Unknown => continue, @@ -1752,18 +1848,18 @@ impl DefWithBody { Ok(source_ptr) => { let root = source_ptr.file_syntax(db.upcast()); if let ast::Expr::RecordExpr(record_expr) = - &source_ptr.value.to_node(&root) + source_ptr.value.to_node(&root) { if record_expr.record_expr_field_list().is_some() { + let field_list_parent_path = + record_expr.path().map(|path| AstPtr::new(&path)); acc.push( MissingFields { file: source_ptr.file_id, - field_list_parent: Either::Left(AstPtr::new( + field_list_parent: AstPtr::new(&Either::Left( record_expr, )), - field_list_parent_path: record_expr - .path() - .map(|path| AstPtr::new(&path)), + field_list_parent_path, missed_fields, } .into(), @@ -1775,24 +1871,24 @@ impl DefWithBody { }, Either::Right(record_pat) => match source_map.pat_syntax(record_pat) { Ok(source_ptr) => { - if let Some(expr) = source_ptr.value.as_ref().left() { + if let Some(ptr) = source_ptr.value.clone().cast::<ast::RecordPat>() + { let root = source_ptr.file_syntax(db.upcast()); - if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { - if record_pat.record_pat_field_list().is_some() { - acc.push( - MissingFields { - file: source_ptr.file_id, - field_list_parent: Either::Right(AstPtr::new( - &record_pat, - )), - field_list_parent_path: record_pat - .path() - .map(|path| AstPtr::new(&path)), - missed_fields, - } - .into(), - ) - } + let record_pat = ptr.to_node(&root); + if record_pat.record_pat_field_list().is_some() { + let field_list_parent_path = + record_pat.path().map(|path| AstPtr::new(&path)); + acc.push( + MissingFields { + file: source_ptr.file_id, + field_list_parent: AstPtr::new(&Either::Right( + record_pat, + )), + field_list_parent_path, + missed_fields, + } + .into(), + ) } } } @@ -1960,6 +2056,17 @@ impl Function { db.function_data(self.id).attrs.is_test() } + /// is this a `fn main` or a function with an `export_name` of `main`? + pub fn is_main(self, db: &dyn HirDatabase) -> bool { + if !self.module(db).is_crate_root() { + return false; + } + let data = db.function_data(self.id); + + data.name.to_smol_str() == "main" + || data.attrs.export_name().map(core::ops::Deref::deref) == Some("main") + } + /// Does this function have the ignore attribute? pub fn is_ignore(self, db: &dyn HirDatabase) -> bool { db.function_data(self.id).attrs.is_ignore() @@ -2926,10 +3033,10 @@ impl Local { .map(|&definition| { let src = source_map.pat_syntax(definition).unwrap(); // Hmm... let root = src.file_syntax(db.upcast()); - src.map(|ast| match ast { - // Suspicious unwrap - Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)), - Either::Right(it) => Either::Right(it.to_node(&root)), + src.map(|ast| match ast.to_node(&root) { + Either::Left(ast::Pat::IdentPat(it)) => Either::Left(it), + Either::Left(_) => unreachable!("local with non ident-pattern"), + Either::Right(it) => Either::Right(it), }) }) .map(move |source| LocalSource { local: self, source }) @@ -3371,6 +3478,10 @@ impl Impl { db.impl_data(self.id).is_negative } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + db.impl_data(self.id).is_unsafe + } + pub fn module(self, db: &dyn HirDatabase) -> Module { self.id.lookup(db.upcast()).container.into() } @@ -3379,6 +3490,10 @@ impl Impl { let src = self.source(db)?; src.file_id.as_builtin_derive_attr_node(db.upcast()) } + + pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool { + check_orphan_rules(db, self.id) + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -4515,15 +4630,31 @@ impl Layout { Some(self.0.largest_niche?.available(&*self.1)) } - pub fn field_offset(&self, idx: usize) -> Option<u64> { + pub fn field_offset(&self, field: Field) -> Option<u64> { match self.0.fields { layout::FieldsShape::Primitive => None, layout::FieldsShape::Union(_) => Some(0), layout::FieldsShape::Array { stride, count } => { - let i = u64::try_from(idx).ok()?; + let i = u64::try_from(field.index()).ok()?; (i < count).then_some((stride * i).bytes()) } - layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()), + layout::FieldsShape::Arbitrary { ref offsets, .. } => { + Some(offsets.get(RustcFieldIdx(field.id))?.bytes()) + } + } + } + + pub fn tuple_field_offset(&self, field: usize) -> Option<u64> { + match self.0.fields { + layout::FieldsShape::Primitive => None, + layout::FieldsShape::Union(_) => Some(0), + layout::FieldsShape::Array { stride, count } => { + let i = u64::try_from(field).ok()?; + (i < count).then_some((stride * i).bytes()) + } + layout::FieldsShape::Arbitrary { ref offsets, .. } => { + Some(offsets.get(RustcFieldIdx::new(field))?.bytes()) + } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index f29fb1edf00..55c2f8324c6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -236,9 +236,9 @@ impl SourceAnalyzer { _db: &dyn HirDatabase, pat: &ast::IdentPat, ) -> Option<BindingMode> { - let binding_id = self.binding_id_of_pat(pat)?; + let id = self.pat_id(&pat.clone().into())?; let infer = self.infer.as_ref()?; - infer.binding_modes.get(binding_id).map(|bm| match bm { + infer.binding_modes.get(id).map(|bm| match bm { hir_ty::BindingMode::Move => BindingMode::Move, hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut), hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => { @@ -888,7 +888,7 @@ fn scope_for_offset( .scope_by_expr() .iter() .filter_map(|(id, scope)| { - let InFile { file_id, value } = source_map.expr_syntax(*id).ok()?; + let InFile { file_id, value } = source_map.expr_syntax(id).ok()?; if from_file == file_id { return Some((value.text_range(), scope)); } @@ -923,7 +923,7 @@ fn adjust( .scope_by_expr() .iter() .filter_map(|(id, scope)| { - let source = source_map.expr_syntax(*id).ok()?; + let source = source_map.expr_syntax(id).ok()?; // FIXME: correctly handle macro expansion if source.file_id != from_file { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index 447e38f91f4..a622ec1a953 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -14,8 +14,8 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" -either = "1.7.0" +itertools.workspace = true +either.workspace = true smallvec.workspace = true # local deps diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index b273ebc85a5..fbe17dbfd77 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -14,5 +14,6 @@ pub struct AssistConfig { pub allowed: Option<Vec<AssistKind>>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub assist_emit_must_use: bool, } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index c0e5429a22c..410c623109e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2249,4 +2249,35 @@ impl b::LocalTrait for B { "#, ) } + + #[test] + fn doc_hidden_nondefault_member() { + check_assist( + add_missing_impl_members, + r#" +//- /lib.rs crate:b new_source_root:local +trait LocalTrait { + #[doc(hidden)] + fn no_skip_non_default() -> Option<()>; + + #[doc(hidden)] + fn skip_default() -> Option<()> { + todo!() + } +} + +//- /main.rs crate:a deps:b +struct B; +impl b::Loc$0alTrait for B {} + "#, + r#" +struct B; +impl b::LocalTrait for B { + fn no_skip_non_default() -> Option<()> { + ${0:todo!()} + } +} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 3b162d7c4d8..2374da9a343 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -88,7 +88,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)?, + build_pat( + ctx.db(), + module, + variant, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -140,7 +146,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); let patterns = variants.into_iter().filter_map(|variant| { - build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std) + build_pat( + ctx.db(), + module, + variant, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) }); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) @@ -173,7 +185,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); let patterns = variants.into_iter().filter_map(|variant| { - build_pat(ctx.db(), module, variant.clone(), ctx.config.prefer_no_std) + build_pat( + ctx.db(), + module, + variant.clone(), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) }); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) @@ -273,9 +291,10 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) syntax::SyntaxElement::Token(it) => { // Don't have a way to make tokens mut, so instead make the parent mut // and find the token again - let parent = edit.make_syntax_mut(it.parent().unwrap()); + let parent = + edit.make_syntax_mut(it.parent().expect("Token must have a parent.")); let mut_token = - parent.covering_element(it.text_range()).into_token().unwrap(); + parent.covering_element(it.text_range()).into_token().expect("Covering element cannot be found. Range may be beyond the current node's range"); syntax::SyntaxElement::from(mut_token) } @@ -439,28 +458,35 @@ fn build_pat( module: hir::Module, var: ExtendedVariant, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ast::Pat> { match var { ExtendedVariant::Variant(var) => { - let path = - mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var), prefer_no_std)?); + let path = mod_path_to_ast(&module.find_use_path( + db, + ModuleDef::from(var), + prefer_no_std, + prefer_prelude, + )?); // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though - let pat: ast::Pat = match var.source(db)?.value.kind() { + Some(match var.source(db)?.value.kind() { ast::StructKind::Tuple(field_list) => { let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); make::tuple_struct_pat(path, pats).into() } ast::StructKind::Record(field_list) => { - let pats = field_list - .fields() - .map(|f| make::ext::simple_ident_pat(f.name().unwrap()).into()); + let pats = field_list.fields().map(|f| { + make::ext::simple_ident_pat( + f.name().expect("Record field must have a name"), + ) + .into() + }); make::record_pat(path, pats).into() } ast::StructKind::Unit => make::path_pat(path), - }; - Some(pat) + }) } ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), @@ -1941,4 +1967,35 @@ fn main() { "#, ); } + + /// See [`discussion`](https://github.com/rust-lang/rust-analyzer/pull/15594#discussion_r1322960614) + #[test] + fn missing_field_name() { + check_assist( + add_missing_match_arms, + r#" +enum A { + A, + Missing { a: u32, : u32, c: u32 } +} + +fn a() { + let b = A::A; + match b$0 {} +}"#, + r#" +enum A { + A, + Missing { a: u32, : u32, c: u32 } +} + +fn a() { + let b = A::A; + match b { + $0A::A => todo!(), + A::Missing { a, u32, c } => todo!(), + } +}"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index 36f68d17677..88fd0b1b733 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -1,6 +1,9 @@ +use either::Either; use ide_db::defs::{Definition, NameRefClass}; -use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxKind, T}; +use syntax::{ + ast::{self, make, HasArgList}, + ted, AstNode, +}; use crate::{ assist_context::{AssistContext, Assists}, @@ -25,21 +28,45 @@ use crate::{ // } // ``` pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| { - let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?; - if arg_list.args().next().is_some() { - return None; - } - cov_mark::hit!(add_turbo_fish_after_call); - cov_mark::hit!(add_type_ascription_after_call); - arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) - })?; - let next_token = ident.next_token()?; - if next_token.kind() == T![::] { + let turbofish_target = + ctx.find_node_at_offset::<ast::PathSegment>().map(Either::Left).or_else(|| { + let callable_expr = ctx.find_node_at_offset::<ast::CallableExpr>()?; + + if callable_expr.arg_list()?.args().next().is_some() { + return None; + } + + cov_mark::hit!(add_turbo_fish_after_call); + cov_mark::hit!(add_type_ascription_after_call); + + match callable_expr { + ast::CallableExpr::Call(it) => { + let ast::Expr::PathExpr(path) = it.expr()? else { + return None; + }; + + Some(Either::Left(path.path()?.segment()?)) + } + ast::CallableExpr::MethodCall(it) => Some(Either::Right(it)), + } + })?; + + let already_has_turbofish = match &turbofish_target { + Either::Left(path_segment) => path_segment.generic_arg_list().is_some(), + Either::Right(method_call) => method_call.generic_arg_list().is_some(), + }; + + if already_has_turbofish { cov_mark::hit!(add_turbo_fish_one_fish_is_enough); return None; } - let name_ref = ast::NameRef::cast(ident.parent()?)?; + + let name_ref = match &turbofish_target { + Either::Left(path_segment) => path_segment.name_ref()?, + Either::Right(method_call) => method_call.name_ref()?, + }; + let ident = name_ref.ident_token()?; + let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { NameRefClass::Definition(def) => def, NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => { @@ -58,20 +85,27 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() { if let_stmt.colon_token().is_none() { - let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); - let semi_pos = let_stmt.syntax().last_token()?.text_range().end(); + if let_stmt.pat().is_none() { + return None; + } acc.add( AssistId("add_type_ascription", AssistKind::RefactorRewrite), "Add `: _` before assignment operator", ident.text_range(), - |builder| { + |edit| { + let let_stmt = edit.make_mut(let_stmt); + if let_stmt.semicolon_token().is_none() { - builder.insert(semi_pos, ";"); + ted::append_child(let_stmt.syntax(), make::tokens::semicolon()); } - match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), - None => builder.insert(type_pos, ": _"), + + let placeholder_ty = make::ty_placeholder().clone_for_update(); + + let_stmt.set_ty(Some(placeholder_ty.clone())); + + if let Some(cap) = ctx.config.snippet_cap { + edit.add_placeholder_snippet(cap, placeholder_ty); } }, )? @@ -91,38 +125,46 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti AssistId("add_turbo_fish", AssistKind::RefactorRewrite), "Add `::<>`", ident.text_range(), - |builder| { - builder.trigger_signature_help(); - match ctx.config.snippet_cap { - Some(cap) => { - let fish_head = get_snippet_fish_head(number_of_arguments); - let snip = format!("::<{fish_head}>"); - builder.insert_snippet(cap, ident.text_range().end(), snip) + |edit| { + edit.trigger_signature_help(); + + let new_arg_list = match turbofish_target { + Either::Left(path_segment) => { + edit.make_mut(path_segment).get_or_create_generic_arg_list() + } + Either::Right(method_call) => { + edit.make_mut(method_call).get_or_create_generic_arg_list() } - None => { - let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", "); - let snip = format!("::<{fish_head}>"); - builder.insert(ident.text_range().end(), snip); + }; + + let fish_head = get_fish_head(number_of_arguments).clone_for_update(); + + // Note: we need to replace the `new_arg_list` instead of being able to use something like + // `GenericArgList::add_generic_arg` as `PathSegment::get_or_create_generic_arg_list` + // always creates a non-turbofish form generic arg list. + ted::replace(new_arg_list.syntax(), fish_head.syntax()); + + if let Some(cap) = ctx.config.snippet_cap { + for arg in fish_head.generic_args() { + edit.add_placeholder_snippet(cap, arg) } } }, ) } -/// This will create a snippet string with tabstops marked -fn get_snippet_fish_head(number_of_arguments: usize) -> String { - let mut fish_head = (1..number_of_arguments) - .format_with("", |i, f| f(&format_args!("${{{i}:_}}, "))) - .to_string(); - - // tabstop 0 is a special case and always the last one - fish_head.push_str("${0:_}"); - fish_head +/// This will create a turbofish generic arg list corresponding to the number of arguments +fn get_fish_head(number_of_arguments: usize) -> ast::GenericArgList { + let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into()); + make::turbofish_generic_arg_list(args) } #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; + use crate::tests::{ + check_assist, check_assist_by_label, check_assist_not_applicable, + check_assist_not_applicable_by_label, + }; use super::*; @@ -364,6 +406,20 @@ fn main() { } #[test] + fn add_type_ascription_missing_pattern() { + check_assist_not_applicable_by_label( + add_turbo_fish, + r#" +fn make<T>() -> T {} +fn main() { + let = make$0() +} +"#, + "Add `: _` before assignment operator", + ); + } + + #[test] fn add_turbo_fish_function_lifetime_parameter() { check_assist( add_turbo_fish, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 66bc2f6dadc..2d41243c20e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -1,7 +1,13 @@ use std::collections::VecDeque; +use ide_db::{ + assists::GroupLabel, + famous_defs::FamousDefs, + source_change::SourceChangeBuilder, + syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, +}; use syntax::{ - ast::{self, AstNode, Expr::BinExpr}, + ast::{self, make, AstNode, Expr::BinExpr, HasArgList}, ted::{self, Position}, SyntaxKind, }; @@ -89,7 +95,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let dm_lhs = demorganed.lhs()?; - acc.add( + acc.add_group( + &GroupLabel("Apply De Morgan's law".to_string()), AssistId("apply_demorgan", AssistKind::RefactorRewrite), "Apply De Morgan's law", op_range, @@ -143,6 +150,127 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti ) } +// Assist: apply_demorgan_iterator +// +// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law] to +// `Iterator::all` and `Iterator::any`. +// +// This transforms expressions of the form `!iter.any(|x| predicate(x))` into +// `iter.all(|x| !predicate(x))` and vice versa. This also works the other way for +// `Iterator::all` into `Iterator::any`. +// +// ``` +// # //- minicore: iterator +// fn main() { +// let arr = [1, 2, 3]; +// if !arr.into_iter().$0any(|num| num == 4) { +// println!("foo"); +// } +// } +// ``` +// -> +// ``` +// fn main() { +// let arr = [1, 2, 3]; +// if arr.into_iter().all(|num| num != 4) { +// println!("foo"); +// } +// } +// ``` +pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?; + + let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None }; + let closure_body = closure_expr.body()?; + + let op_range = method_call.syntax().text_range(); + let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); + acc.add_group( + &GroupLabel("Apply De Morgan's law".to_string()), + AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite), + label, + op_range, + |edit| { + // replace the method name + let new_name = match name.text().as_str() { + "all" => make::name_ref("any"), + "any" => make::name_ref("all"), + _ => unreachable!(), + } + .clone_for_update(); + edit.replace_ast(name, new_name); + + // negate all tail expressions in the closure body + let tail_cb = &mut |e: &_| tail_cb_impl(edit, e); + walk_expr(&closure_body, &mut |expr| { + if let ast::Expr::ReturnExpr(ret_expr) = expr { + if let Some(ret_expr_arg) = &ret_expr.expr() { + for_each_tail_expr(ret_expr_arg, tail_cb); + } + } + }); + for_each_tail_expr(&closure_body, tail_cb); + + // negate the whole method call + if let Some(prefix_expr) = method_call + .syntax() + .parent() + .and_then(ast::PrefixExpr::cast) + .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) + { + edit.delete( + prefix_expr + .op_token() + .expect("prefix expression always has an operator") + .text_range(), + ); + } else { + edit.insert(method_call.syntax().text_range().start(), "!"); + } + }, + ) +} + +/// Ensures that the method call is to `Iterator::all` or `Iterator::any`. +fn validate_method_call_expr( + ctx: &AssistContext<'_>, + method_call: &ast::MethodCallExpr, +) -> Option<(ast::NameRef, ast::Expr)> { + let name_ref = method_call.name_ref()?; + if name_ref.text() != "all" && name_ref.text() != "any" { + return None; + } + let arg_expr = method_call.arg_list()?.args().next()?; + + let sema = &ctx.sema; + + let receiver = method_call.receiver()?; + let it_type = sema.type_of_expr(&receiver)?.adjusted(); + let module = sema.scope(receiver.syntax())?.module(); + let krate = module.krate(); + + let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?; + it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr)) +} + +fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) { + match e { + ast::Expr::BreakExpr(break_expr) => { + if let Some(break_expr_arg) = break_expr.expr() { + for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(edit, e)) + } + } + ast::Expr::ReturnExpr(_) => { + // all return expressions have already been handled by the walk loop + } + e => { + let inverted_body = invert_boolean_expression(e.clone()); + edit.replace(e.syntax().text_range(), inverted_body.syntax().text()); + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -255,4 +383,206 @@ fn f() { !(S <= S || S < S) } "fn() { let x = a && b && c; }", ) } + + #[test] + fn demorgan_iterator_any_all_reverse() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if arr.into_iter().all(|num| num $0!= 4) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().any(|num| num == 4) { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_all_any() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().$0all(|num| num > 3) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + if arr.into_iter().any(|num| num <= 3) { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_multiple_terms() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().$0any(|num| num > 3 && num == 23 && num <= 30) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + if arr.into_iter().all(|num| !(num > 3 && num == 23 && num <= 30)) { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_double_negation() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().$0all(|num| !(num > 3)) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + if arr.into_iter().any(|num| num > 3) { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_double_parens() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().$0any(|num| (num > 3 && (num == 1 || num == 2))) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + if arr.into_iter().all(|num| !(num > 3 && (num == 1 || num == 2))) { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_multiline() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if arr + .into_iter() + .all$0(|num| !num.is_negative()) + { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + if !arr + .into_iter() + .any(|num| num.is_negative()) + { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_block_closure() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [-1, 1, 2, 3]; + if arr.into_iter().all(|num: i32| { + $0if num.is_positive() { + num <= 3 + } else { + num >= -1 + } + }) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let arr = [-1, 1, 2, 3]; + if !arr.into_iter().any(|num: i32| { + if num.is_positive() { + num > 3 + } else { + num < -1 + } + }) { + println!("foo"); + } +} +"#, + ); + } + + #[test] + fn demorgan_iterator_wrong_method() { + check_assist_not_applicable( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().$0map(|num| num > 3) { + println!("foo"); + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index 7acf2ea0a0d..f508c42c53e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -5,7 +5,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::{ import_assets::{ImportAssets, ImportCandidate, LocatedImport}, - insert_use::{insert_use, ImportScope}, + insert_use::{insert_use, insert_use_as_alias, ImportScope}, }, }; use syntax::{ast, AstNode, NodeOrToken, SyntaxElement}; @@ -93,6 +93,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< &ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_no_std, ); if proposed_imports.is_empty() { return None; @@ -129,10 +130,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< for import in proposed_imports { let import_path = import.import_path; + let (assist_id, import_name) = + (AssistId("auto_import", AssistKind::QuickFix), import_path.display(ctx.db())); acc.add_group( &group_label, - AssistId("auto_import", AssistKind::QuickFix), - format!("Import `{}`", import_path.display(ctx.db())), + assist_id, + format!("Import `{}`", import_name), range, |builder| { let scope = match scope.clone() { @@ -143,6 +146,38 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use); }, ); + + match import_assets.import_candidate() { + ImportCandidate::TraitAssocItem(name) | ImportCandidate::TraitMethod(name) => { + let is_method = + matches!(import_assets.import_candidate(), ImportCandidate::TraitMethod(_)); + let type_ = if is_method { "method" } else { "item" }; + let group_label = GroupLabel(format!( + "Import a trait for {} {} by alias", + type_, + name.assoc_item_name.text() + )); + acc.add_group( + &group_label, + assist_id, + format!("Import `{} as _`", import_name), + range, + |builder| { + let scope = match scope.clone() { + ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), + ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), + ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)), + }; + insert_use_as_alias( + &scope, + mod_path_to_ast(&import_path), + &ctx.config.insert_use, + ); + }, + ); + } + _ => {} + } } Some(()) } @@ -253,7 +288,8 @@ mod tests { }; use crate::tests::{ - check_assist, check_assist_not_applicable, check_assist_target, TEST_CONFIG, + check_assist, check_assist_by_label, check_assist_not_applicable, check_assist_target, + TEST_CONFIG, }; fn check_auto_import_order(before: &str, order: &[&str]) { @@ -705,7 +741,7 @@ fn main() { #[test] fn associated_trait_function() { - check_assist( + check_assist_by_label( auto_import, r" mod test_mod { @@ -739,6 +775,44 @@ fn main() { test_mod::TestStruct::test_function } ", + "Import `test_mod::TestTrait`", + ); + + check_assist_by_label( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } + } + + fn main() { + test_mod::TestStruct::test_function$0 + } + ", + r" + use test_mod::TestTrait as _; + + mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } + } + + fn main() { + test_mod::TestStruct::test_function + } + ", + "Import `test_mod::TestTrait as _`", ); } @@ -776,7 +850,44 @@ fn main() { #[test] fn associated_trait_const() { - check_assist( + check_assist_by_label( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const TEST_CONST: u8 = 42; + } + } + + fn main() { + test_mod::TestStruct::TEST_CONST$0 + } + ", + r" + use test_mod::TestTrait as _; + + mod test_mod { + pub trait TestTrait { + const TEST_CONST: u8; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const TEST_CONST: u8 = 42; + } + } + + fn main() { + test_mod::TestStruct::TEST_CONST + } + ", + "Import `test_mod::TestTrait as _`", + ); + + check_assist_by_label( auto_import, r" mod test_mod { @@ -810,6 +921,7 @@ fn main() { test_mod::TestStruct::TEST_CONST } ", + "Import `test_mod::TestTrait`", ); } @@ -847,7 +959,46 @@ fn main() { #[test] fn trait_method() { - check_assist( + check_assist_by_label( + auto_import, + r" + mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } + } + + fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_meth$0od() + } + ", + r" + use test_mod::TestTrait as _; + + mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } + } + + fn main() { + let test_struct = test_mod::TestStruct {}; + test_struct.test_method() + } + ", + "Import `test_mod::TestTrait as _`", + ); + + check_assist_by_label( auto_import, r" mod test_mod { @@ -883,12 +1034,43 @@ fn main() { test_struct.test_method() } ", + "Import `test_mod::TestTrait`", ); } #[test] fn trait_method_cross_crate() { - check_assist( + check_assist_by_label( + auto_import, + r" + //- /main.rs crate:main deps:dep + fn main() { + let test_struct = dep::test_mod::TestStruct {}; + test_struct.test_meth$0od() + } + //- /dep.rs crate:dep + pub mod test_mod { + pub trait TestTrait { + fn test_method(&self); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_method(&self) {} + } + } + ", + r" + use dep::test_mod::TestTrait as _; + + fn main() { + let test_struct = dep::test_mod::TestStruct {}; + test_struct.test_method() + } + ", + "Import `dep::test_mod::TestTrait as _`", + ); + + check_assist_by_label( auto_import, r" //- /main.rs crate:main deps:dep @@ -915,12 +1097,41 @@ fn main() { test_struct.test_method() } ", + "Import `dep::test_mod::TestTrait`", ); } #[test] fn assoc_fn_cross_crate() { - check_assist( + check_assist_by_label( + auto_import, + r" + //- /main.rs crate:main deps:dep + fn main() { + dep::test_mod::TestStruct::test_func$0tion + } + //- /dep.rs crate:dep + pub mod test_mod { + pub trait TestTrait { + fn test_function(); + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + fn test_function() {} + } + } + ", + r" + use dep::test_mod::TestTrait as _; + + fn main() { + dep::test_mod::TestStruct::test_function + } + ", + "Import `dep::test_mod::TestTrait as _`", + ); + + check_assist_by_label( auto_import, r" //- /main.rs crate:main deps:dep @@ -945,12 +1156,41 @@ fn main() { dep::test_mod::TestStruct::test_function } ", + "Import `dep::test_mod::TestTrait`", ); } #[test] fn assoc_const_cross_crate() { - check_assist( + check_assist_by_label( + auto_import, + r" + //- /main.rs crate:main deps:dep + fn main() { + dep::test_mod::TestStruct::CONST$0 + } + //- /dep.rs crate:dep + pub mod test_mod { + pub trait TestTrait { + const CONST: bool; + } + pub struct TestStruct {} + impl TestTrait for TestStruct { + const CONST: bool = true; + } + } + ", + r" + use dep::test_mod::TestTrait as _; + + fn main() { + dep::test_mod::TestStruct::CONST + } + ", + "Import `dep::test_mod::TestTrait as _`", + ); + + check_assist_by_label( auto_import, r" //- /main.rs crate:main deps:dep @@ -975,6 +1215,7 @@ fn main() { dep::test_mod::TestStruct::CONST } ", + "Import `dep::test_mod::TestTrait`", ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs new file mode 100644 index 00000000000..11facc5bee2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -0,0 +1,1673 @@ +use hir::ModuleDef; +use ide_db::{ + assists::{AssistId, AssistKind}, + defs::Definition, + helpers::mod_path_to_ast, + imports::insert_use::{insert_use, ImportScope}, + search::{FileReference, UsageSearchResult}, + source_change::SourceChangeBuilder, + FxHashSet, +}; +use itertools::Itertools; +use syntax::{ + ast::{ + self, + edit::IndentLevel, + edit_in_place::{AttrsOwnerEdit, Indent}, + make, HasName, + }, + ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, +}; +use text_edit::TextRange; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: bool_to_enum +// +// This converts boolean local variables, fields, constants, and statics into a new +// enum with two variants `Bool::True` and `Bool::False`, as well as replacing +// all assignments with the variants and replacing all usages with `== Bool::True` or +// `== Bool::False`. +// +// ``` +// fn main() { +// let $0bool = true; +// +// if bool { +// println!("foo"); +// } +// } +// ``` +// -> +// ``` +// #[derive(PartialEq, Eq)] +// enum Bool { True, False } +// +// fn main() { +// let bool = Bool::True; +// +// if bool == Bool::True { +// println!("foo"); +// } +// } +// ``` +pub(crate) fn bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let BoolNodeData { target_node, name, ty_annotation, initializer, definition } = + find_bool_node(ctx)?; + let target_module = ctx.sema.scope(&target_node)?.module().nearest_non_block_module(ctx.db()); + + let target = name.syntax().text_range(); + acc.add( + AssistId("bool_to_enum", AssistKind::RefactorRewrite), + "Convert boolean to enum", + target, + |edit| { + if let Some(ty) = &ty_annotation { + cov_mark::hit!(replaces_ty_annotation); + edit.replace(ty.syntax().text_range(), "Bool"); + } + + if let Some(initializer) = initializer { + replace_bool_expr(edit, initializer); + } + + let usages = definition.usages(&ctx.sema).all(); + add_enum_def(edit, ctx, &usages, target_node, &target_module); + replace_usages(edit, ctx, &usages, definition, &target_module); + }, + ) +} + +struct BoolNodeData { + target_node: SyntaxNode, + name: ast::Name, + ty_annotation: Option<ast::Type>, + initializer: Option<ast::Expr>, + definition: Definition, +} + +/// Attempts to find an appropriate node to apply the action to. +fn find_bool_node(ctx: &AssistContext<'_>) -> Option<BoolNodeData> { + let name: ast::Name = ctx.find_node_at_offset()?; + + if let Some(let_stmt) = name.syntax().ancestors().find_map(ast::LetStmt::cast) { + let bind_pat = match let_stmt.pat()? { + ast::Pat::IdentPat(pat) => pat, + _ => { + cov_mark::hit!(not_applicable_in_non_ident_pat); + return None; + } + }; + let def = ctx.sema.to_def(&bind_pat)?; + if !def.ty(ctx.db()).is_bool() { + cov_mark::hit!(not_applicable_non_bool_local); + return None; + } + + Some(BoolNodeData { + target_node: let_stmt.syntax().clone(), + name, + ty_annotation: let_stmt.ty(), + initializer: let_stmt.initializer(), + definition: Definition::Local(def), + }) + } else if let Some(const_) = name.syntax().parent().and_then(ast::Const::cast) { + let def = ctx.sema.to_def(&const_)?; + if !def.ty(ctx.db()).is_bool() { + cov_mark::hit!(not_applicable_non_bool_const); + return None; + } + + Some(BoolNodeData { + target_node: const_.syntax().clone(), + name, + ty_annotation: const_.ty(), + initializer: const_.body(), + definition: Definition::Const(def), + }) + } else if let Some(static_) = name.syntax().parent().and_then(ast::Static::cast) { + let def = ctx.sema.to_def(&static_)?; + if !def.ty(ctx.db()).is_bool() { + cov_mark::hit!(not_applicable_non_bool_static); + return None; + } + + Some(BoolNodeData { + target_node: static_.syntax().clone(), + name, + ty_annotation: static_.ty(), + initializer: static_.body(), + definition: Definition::Static(def), + }) + } else { + let field = name.syntax().parent().and_then(ast::RecordField::cast)?; + if field.name()? != name { + return None; + } + + let adt = field.syntax().ancestors().find_map(ast::Adt::cast)?; + let def = ctx.sema.to_def(&field)?; + if !def.ty(ctx.db()).is_bool() { + cov_mark::hit!(not_applicable_non_bool_field); + return None; + } + Some(BoolNodeData { + target_node: adt.syntax().clone(), + name, + ty_annotation: field.ty(), + initializer: None, + definition: Definition::Field(def), + }) + } +} + +fn replace_bool_expr(edit: &mut SourceChangeBuilder, expr: ast::Expr) { + let expr_range = expr.syntax().text_range(); + let enum_expr = bool_expr_to_enum_expr(expr); + edit.replace(expr_range, enum_expr.syntax().text()) +} + +/// Converts an expression of type `bool` to one of the new enum type. +fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { + let true_expr = make::expr_path(make::path_from_text("Bool::True")).clone_for_update(); + let false_expr = make::expr_path(make::path_from_text("Bool::False")).clone_for_update(); + + if let ast::Expr::Literal(literal) = &expr { + match literal.kind() { + ast::LiteralKind::Bool(true) => true_expr, + ast::LiteralKind::Bool(false) => false_expr, + _ => expr, + } + } else { + make::expr_if( + expr, + make::tail_only_block_expr(true_expr), + Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))), + ) + .clone_for_update() + } +} + +/// Replaces all usages of the target identifier, both when read and written to. +fn replace_usages( + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + usages: &UsageSearchResult, + target_definition: Definition, + target_module: &hir::Module, +) { + for (file_id, references) in usages.iter() { + edit.edit_file(*file_id); + + let refs_with_imports = + augment_references_with_imports(edit, ctx, references, target_module); + + refs_with_imports.into_iter().rev().for_each( + |FileReferenceWithImport { range, old_name, new_name, import_data }| { + // replace the usages in patterns and expressions + if let Some(ident_pat) = old_name.syntax().ancestors().find_map(ast::IdentPat::cast) + { + cov_mark::hit!(replaces_record_pat_shorthand); + + let definition = ctx.sema.to_def(&ident_pat).map(Definition::Local); + if let Some(def) = definition { + replace_usages( + edit, + ctx, + &def.usages(&ctx.sema).all(), + target_definition, + target_module, + ) + } + } else if let Some(initializer) = find_assignment_usage(&new_name) { + cov_mark::hit!(replaces_assignment); + + replace_bool_expr(edit, initializer); + } else if let Some((prefix_expr, inner_expr)) = find_negated_usage(&new_name) { + cov_mark::hit!(replaces_negation); + + edit.replace( + prefix_expr.syntax().text_range(), + format!("{} == Bool::False", inner_expr), + ); + } else if let Some((record_field, initializer)) = old_name + .as_name_ref() + .and_then(ast::RecordExprField::for_field_name) + .and_then(|record_field| ctx.sema.resolve_record_field(&record_field)) + .and_then(|(got_field, _, _)| { + find_record_expr_usage(&new_name, got_field, target_definition) + }) + { + cov_mark::hit!(replaces_record_expr); + + let record_field = edit.make_mut(record_field); + let enum_expr = bool_expr_to_enum_expr(initializer); + record_field.replace_expr(enum_expr); + } else if let Some(pat) = find_record_pat_field_usage(&old_name) { + match pat { + ast::Pat::IdentPat(ident_pat) => { + cov_mark::hit!(replaces_record_pat); + + let definition = ctx.sema.to_def(&ident_pat).map(Definition::Local); + if let Some(def) = definition { + replace_usages( + edit, + ctx, + &def.usages(&ctx.sema).all(), + target_definition, + target_module, + ) + } + } + ast::Pat::LiteralPat(literal_pat) => { + cov_mark::hit!(replaces_literal_pat); + + if let Some(expr) = literal_pat.literal().and_then(|literal| { + literal.syntax().ancestors().find_map(ast::Expr::cast) + }) { + replace_bool_expr(edit, expr); + } + } + _ => (), + } + } else if let Some((ty_annotation, initializer)) = find_assoc_const_usage(&new_name) + { + edit.replace(ty_annotation.syntax().text_range(), "Bool"); + replace_bool_expr(edit, initializer); + } else if let Some(receiver) = find_method_call_expr_usage(&new_name) { + edit.replace( + receiver.syntax().text_range(), + format!("({} == Bool::True)", receiver), + ); + } else if new_name.syntax().ancestors().find_map(ast::UseTree::cast).is_none() { + // for any other usage in an expression, replace it with a check that it is the true variant + if let Some((record_field, expr)) = new_name + .as_name_ref() + .and_then(ast::RecordExprField::for_field_name) + .and_then(|record_field| { + record_field.expr().map(|expr| (record_field, expr)) + }) + { + record_field.replace_expr( + make::expr_bin_op( + expr, + ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: false }), + make::expr_path(make::path_from_text("Bool::True")), + ) + .clone_for_update(), + ); + } else { + edit.replace(range, format!("{} == Bool::True", new_name.text())); + } + } + + // add imports across modules where needed + if let Some((import_scope, path)) = import_data { + insert_use(&import_scope, path, &ctx.config.insert_use); + } + }, + ) + } +} + +struct FileReferenceWithImport { + range: TextRange, + old_name: ast::NameLike, + new_name: ast::NameLike, + import_data: Option<(ImportScope, ast::Path)>, +} + +fn augment_references_with_imports( + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + references: &[FileReference], + target_module: &hir::Module, +) -> Vec<FileReferenceWithImport> { + let mut visited_modules = FxHashSet::default(); + + references + .iter() + .filter_map(|FileReference { range, name, .. }| { + ctx.sema.scope(name.syntax()).map(|scope| (*range, name, scope.module())) + }) + .map(|(range, name, ref_module)| { + let old_name = name.clone(); + let new_name = edit.make_mut(name.clone()); + + // if the referenced module is not the same as the target one and has not been seen before, add an import + let import_data = if ref_module.nearest_non_block_module(ctx.db()) != *target_module + && !visited_modules.contains(&ref_module) + { + visited_modules.insert(ref_module); + + let import_scope = + ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); + let path = ref_module + .find_use_path_prefixed( + ctx.sema.db, + ModuleDef::Module(*target_module), + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) + .map(|mod_path| { + make::path_concat(mod_path_to_ast(&mod_path), make::path_from_text("Bool")) + }); + + import_scope.zip(path) + } else { + None + }; + + FileReferenceWithImport { range, old_name, new_name, import_data } + }) + .collect() +} + +fn find_assignment_usage(name: &ast::NameLike) -> Option<ast::Expr> { + let bin_expr = name.syntax().ancestors().find_map(ast::BinExpr::cast)?; + + if !bin_expr.lhs()?.syntax().descendants().contains(name.syntax()) { + cov_mark::hit!(dont_assign_incorrect_ref); + return None; + } + + if let Some(ast::BinaryOp::Assignment { op: None }) = bin_expr.op_kind() { + bin_expr.rhs() + } else { + None + } +} + +fn find_negated_usage(name: &ast::NameLike) -> Option<(ast::PrefixExpr, ast::Expr)> { + let prefix_expr = name.syntax().ancestors().find_map(ast::PrefixExpr::cast)?; + + if !matches!(prefix_expr.expr()?, ast::Expr::PathExpr(_) | ast::Expr::FieldExpr(_)) { + cov_mark::hit!(dont_overwrite_expression_inside_negation); + return None; + } + + if let Some(ast::UnaryOp::Not) = prefix_expr.op_kind() { + let inner_expr = prefix_expr.expr()?; + Some((prefix_expr, inner_expr)) + } else { + None + } +} + +fn find_record_expr_usage( + name: &ast::NameLike, + got_field: hir::Field, + target_definition: Definition, +) -> Option<(ast::RecordExprField, ast::Expr)> { + let name_ref = name.as_name_ref()?; + let record_field = ast::RecordExprField::for_field_name(name_ref)?; + let initializer = record_field.expr()?; + + if let Definition::Field(expected_field) = target_definition { + if got_field != expected_field { + return None; + } + } + + Some((record_field, initializer)) +} + +fn find_record_pat_field_usage(name: &ast::NameLike) -> Option<ast::Pat> { + let record_pat_field = name.syntax().parent().and_then(ast::RecordPatField::cast)?; + let pat = record_pat_field.pat()?; + + match pat { + ast::Pat::IdentPat(_) | ast::Pat::LiteralPat(_) | ast::Pat::WildcardPat(_) => Some(pat), + _ => None, + } +} + +fn find_assoc_const_usage(name: &ast::NameLike) -> Option<(ast::Type, ast::Expr)> { + let const_ = name.syntax().parent().and_then(ast::Const::cast)?; + if const_.syntax().parent().and_then(ast::AssocItemList::cast).is_none() { + return None; + } + + Some((const_.ty()?, const_.body()?)) +} + +fn find_method_call_expr_usage(name: &ast::NameLike) -> Option<ast::Expr> { + let method_call = name.syntax().ancestors().find_map(ast::MethodCallExpr::cast)?; + let receiver = method_call.receiver()?; + + if !receiver.syntax().descendants().contains(name.syntax()) { + return None; + } + + Some(receiver) +} + +/// Adds the definition of the new enum before the target node. +fn add_enum_def( + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + usages: &UsageSearchResult, + target_node: SyntaxNode, + target_module: &hir::Module, +) { + let make_enum_pub = usages + .iter() + .flat_map(|(_, refs)| refs) + .filter_map(|FileReference { name, .. }| { + ctx.sema.scope(name.syntax()).map(|scope| scope.module()) + }) + .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); + let enum_def = make_bool_enum(make_enum_pub); + + let insert_before = node_to_insert_before(target_node); + let indent = IndentLevel::from_node(&insert_before); + enum_def.reindent_to(indent); + + ted::insert_all( + ted::Position::before(&edit.make_syntax_mut(insert_before)), + vec![ + enum_def.syntax().clone().into(), + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + ], + ); +} + +/// Finds where to put the new enum definition. +/// Tries to find the ast node at the nearest module or at top-level, otherwise just +/// returns the input node. +fn node_to_insert_before(target_node: SyntaxNode) -> SyntaxNode { + target_node + .ancestors() + .take_while(|it| !matches!(it.kind(), SyntaxKind::MODULE | SyntaxKind::SOURCE_FILE)) + .filter(|it| ast::Item::can_cast(it.kind())) + .last() + .unwrap_or(target_node) +} + +fn make_bool_enum(make_pub: bool) -> ast::Enum { + let enum_def = make::enum_( + if make_pub { Some(make::visibility_pub()) } else { None }, + make::name("Bool"), + make::variant_list(vec![ + make::variant(make::name("True"), None), + make::variant(make::name("False"), None), + ]), + ) + .clone_for_update(); + + let derive_eq = make::attr_outer(make::meta_token_tree( + make::ext::ident_path("derive"), + make::token_tree( + T!['('], + vec![ + NodeOrToken::Token(make::tokens::ident("PartialEq")), + NodeOrToken::Token(make::token(T![,])), + NodeOrToken::Token(make::tokens::single_space()), + NodeOrToken::Token(make::tokens::ident("Eq")), + ], + ), + )) + .clone_for_update(); + enum_def.add_attr(derive_eq); + + enum_def +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn local_variable_with_usage() { + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo = true; + + if foo { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = Bool::True; + + if foo == Bool::True { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn local_variable_with_usage_negated() { + cov_mark::check!(replaces_negation); + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo = true; + + if !foo { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = Bool::True; + + if foo == Bool::False { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn local_variable_with_type_annotation() { + cov_mark::check!(replaces_ty_annotation); + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo: bool = false; +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo: Bool = Bool::False; +} +"#, + ) + } + + #[test] + fn local_variable_with_non_literal_initializer() { + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo = 1 == 2; +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = if 1 == 2 { Bool::True } else { Bool::False }; +} +"#, + ) + } + + #[test] + fn local_variable_binexpr_usage() { + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo = false; + let bar = true; + + if !foo && bar { + println!("foobar"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = Bool::False; + let bar = true; + + if foo == Bool::False && bar { + println!("foobar"); + } +} +"#, + ) + } + + #[test] + fn local_variable_unop_usage() { + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo = true; + + if *&foo { + println!("foobar"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = Bool::True; + + if *&foo == Bool::True { + println!("foobar"); + } +} +"#, + ) + } + + #[test] + fn local_variable_assigned_later() { + cov_mark::check!(replaces_assignment); + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo: bool; + foo = true; +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo: Bool; + foo = Bool::True; +} +"#, + ) + } + + #[test] + fn local_variable_does_not_apply_recursively() { + check_assist( + bool_to_enum, + r#" +fn main() { + let $0foo = true; + let bar = !foo; + + if bar { + println!("bar"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let foo = Bool::True; + let bar = foo == Bool::False; + + if bar { + println!("bar"); + } +} +"#, + ) + } + + #[test] + fn local_variable_nested_in_negation() { + cov_mark::check!(dont_overwrite_expression_inside_negation); + check_assist( + bool_to_enum, + r#" +fn main() { + if !"foo".chars().any(|c| { + let $0foo = true; + foo + }) { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + if !"foo".chars().any(|c| { + let foo = Bool::True; + foo == Bool::True + }) { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn local_variable_non_bool() { + cov_mark::check!(not_applicable_non_bool_local); + check_assist_not_applicable( + bool_to_enum, + r#" +fn main() { + let $0foo = 1; +} +"#, + ) + } + + #[test] + fn local_variable_cursor_not_on_ident() { + check_assist_not_applicable( + bool_to_enum, + r#" +fn main() { + let foo = $0true; +} +"#, + ) + } + + #[test] + fn local_variable_non_ident_pat() { + cov_mark::check!(not_applicable_in_non_ident_pat); + check_assist_not_applicable( + bool_to_enum, + r#" +fn main() { + let ($0foo, bar) = (true, false); +} +"#, + ) + } + + #[test] + fn field_struct_basic() { + cov_mark::check!(replaces_record_expr); + check_assist( + bool_to_enum, + r#" +struct Foo { + $0bar: bool, + baz: bool, +} + +fn main() { + let foo = Foo { bar: true, baz: false }; + + if foo.bar { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Foo { + bar: Bool, + baz: bool, +} + +fn main() { + let foo = Foo { bar: Bool::True, baz: false }; + + if foo.bar == Bool::True { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn field_enum_basic() { + cov_mark::check!(replaces_record_pat); + check_assist( + bool_to_enum, + r#" +enum Foo { + Foo, + Bar { $0bar: bool }, +} + +fn main() { + let foo = Foo::Bar { bar: true }; + + if let Foo::Bar { bar: baz } = foo { + if baz { + println!("foo"); + } + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +enum Foo { + Foo, + Bar { bar: Bool }, +} + +fn main() { + let foo = Foo::Bar { bar: Bool::True }; + + if let Foo::Bar { bar: baz } = foo { + if baz == Bool::True { + println!("foo"); + } + } +} +"#, + ) + } + + #[test] + fn field_enum_cross_file() { + check_assist( + bool_to_enum, + r#" +//- /foo.rs +pub enum Foo { + Foo, + Bar { $0bar: bool }, +} + +fn foo() { + let foo = Foo::Bar { bar: true }; +} + +//- /main.rs +use foo::Foo; + +mod foo; + +fn main() { + let foo = Foo::Bar { bar: false }; +} +"#, + r#" +//- /foo.rs +#[derive(PartialEq, Eq)] +pub enum Bool { True, False } + +pub enum Foo { + Foo, + Bar { bar: Bool }, +} + +fn foo() { + let foo = Foo::Bar { bar: Bool::True }; +} + +//- /main.rs +use foo::{Foo, Bool}; + +mod foo; + +fn main() { + let foo = Foo::Bar { bar: Bool::False }; +} +"#, + ) + } + + #[test] + fn field_enum_shorthand() { + cov_mark::check!(replaces_record_pat_shorthand); + check_assist( + bool_to_enum, + r#" +enum Foo { + Foo, + Bar { $0bar: bool }, +} + +fn main() { + let foo = Foo::Bar { bar: true }; + + match foo { + Foo::Bar { bar } => { + if bar { + println!("foo"); + } + } + _ => (), + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +enum Foo { + Foo, + Bar { bar: Bool }, +} + +fn main() { + let foo = Foo::Bar { bar: Bool::True }; + + match foo { + Foo::Bar { bar } => { + if bar == Bool::True { + println!("foo"); + } + } + _ => (), + } +} +"#, + ) + } + + #[test] + fn field_enum_replaces_literal_patterns() { + cov_mark::check!(replaces_literal_pat); + check_assist( + bool_to_enum, + r#" +enum Foo { + Foo, + Bar { $0bar: bool }, +} + +fn main() { + let foo = Foo::Bar { bar: true }; + + if let Foo::Bar { bar: true } = foo { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +enum Foo { + Foo, + Bar { bar: Bool }, +} + +fn main() { + let foo = Foo::Bar { bar: Bool::True }; + + if let Foo::Bar { bar: Bool::True } = foo { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn field_enum_keeps_wildcard_patterns() { + check_assist( + bool_to_enum, + r#" +enum Foo { + Foo, + Bar { $0bar: bool }, +} + +fn main() { + let foo = Foo::Bar { bar: true }; + + if let Foo::Bar { bar: _ } = foo { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +enum Foo { + Foo, + Bar { bar: Bool }, +} + +fn main() { + let foo = Foo::Bar { bar: Bool::True }; + + if let Foo::Bar { bar: _ } = foo { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn field_union_basic() { + check_assist( + bool_to_enum, + r#" +union Foo { + $0foo: bool, + bar: usize, +} + +fn main() { + let foo = Foo { foo: true }; + + if unsafe { foo.foo } { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +union Foo { + foo: Bool, + bar: usize, +} + +fn main() { + let foo = Foo { foo: Bool::True }; + + if unsafe { foo.foo == Bool::True } { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn field_negated() { + check_assist( + bool_to_enum, + r#" +struct Foo { + $0bar: bool, +} + +fn main() { + let foo = Foo { bar: false }; + + if !foo.bar { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Foo { + bar: Bool, +} + +fn main() { + let foo = Foo { bar: Bool::False }; + + if foo.bar == Bool::False { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn field_in_mod_properly_indented() { + check_assist( + bool_to_enum, + r#" +mod foo { + struct Bar { + $0baz: bool, + } + + impl Bar { + fn new(baz: bool) -> Self { + Self { baz } + } + } +} +"#, + r#" +mod foo { + #[derive(PartialEq, Eq)] + enum Bool { True, False } + + struct Bar { + baz: Bool, + } + + impl Bar { + fn new(baz: bool) -> Self { + Self { baz: if baz { Bool::True } else { Bool::False } } + } + } +} +"#, + ) + } + + #[test] + fn field_multiple_initializations() { + check_assist( + bool_to_enum, + r#" +struct Foo { + $0bar: bool, + baz: bool, +} + +fn main() { + let foo1 = Foo { bar: true, baz: false }; + let foo2 = Foo { bar: false, baz: false }; + + if foo1.bar && foo2.bar { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Foo { + bar: Bool, + baz: bool, +} + +fn main() { + let foo1 = Foo { bar: Bool::True, baz: false }; + let foo2 = Foo { bar: Bool::False, baz: false }; + + if foo1.bar == Bool::True && foo2.bar == Bool::True { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn field_assigned_to_another() { + cov_mark::check!(dont_assign_incorrect_ref); + check_assist( + bool_to_enum, + r#" +struct Foo { + $0foo: bool, +} + +struct Bar { + bar: bool, +} + +fn main() { + let foo = Foo { foo: true }; + let mut bar = Bar { bar: true }; + + bar.bar = foo.foo; +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Foo { + foo: Bool, +} + +struct Bar { + bar: bool, +} + +fn main() { + let foo = Foo { foo: Bool::True }; + let mut bar = Bar { bar: true }; + + bar.bar = foo.foo == Bool::True; +} +"#, + ) + } + + #[test] + fn field_initialized_with_other() { + check_assist( + bool_to_enum, + r#" +struct Foo { + $0foo: bool, +} + +struct Bar { + bar: bool, +} + +fn main() { + let foo = Foo { foo: true }; + let bar = Bar { bar: foo.foo }; +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Foo { + foo: Bool, +} + +struct Bar { + bar: bool, +} + +fn main() { + let foo = Foo { foo: Bool::True }; + let bar = Bar { bar: foo.foo == Bool::True }; +} +"#, + ) + } + + #[test] + fn field_method_chain_usage() { + check_assist( + bool_to_enum, + r#" +struct Foo { + $0bool: bool, +} + +fn main() { + let foo = Foo { bool: true }; + + foo.bool.then(|| 2); +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +struct Foo { + bool: Bool, +} + +fn main() { + let foo = Foo { bool: Bool::True }; + + (foo.bool == Bool::True).then(|| 2); +} +"#, + ) + } + + #[test] + fn field_non_bool() { + cov_mark::check!(not_applicable_non_bool_field); + check_assist_not_applicable( + bool_to_enum, + r#" +struct Foo { + $0bar: usize, +} + +fn main() { + let foo = Foo { bar: 1 }; +} +"#, + ) + } + + #[test] + fn const_basic() { + check_assist( + bool_to_enum, + r#" +const $0FOO: bool = false; + +fn main() { + if FOO { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +const FOO: Bool = Bool::False; + +fn main() { + if FOO == Bool::True { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn const_in_module() { + check_assist( + bool_to_enum, + r#" +fn main() { + if foo::FOO { + println!("foo"); + } +} + +mod foo { + pub const $0FOO: bool = true; +} +"#, + r#" +use foo::Bool; + +fn main() { + if foo::FOO == Bool::True { + println!("foo"); + } +} + +mod foo { + #[derive(PartialEq, Eq)] + pub enum Bool { True, False } + + pub const FOO: Bool = Bool::True; +} +"#, + ) + } + + #[test] + fn const_in_module_with_import() { + check_assist( + bool_to_enum, + r#" +fn main() { + use foo::FOO; + + if FOO { + println!("foo"); + } +} + +mod foo { + pub const $0FOO: bool = true; +} +"#, + r#" +use crate::foo::Bool; + +fn main() { + use foo::FOO; + + if FOO == Bool::True { + println!("foo"); + } +} + +mod foo { + #[derive(PartialEq, Eq)] + pub enum Bool { True, False } + + pub const FOO: Bool = Bool::True; +} +"#, + ) + } + + #[test] + fn const_cross_file() { + check_assist( + bool_to_enum, + r#" +//- /main.rs +mod foo; + +fn main() { + if foo::FOO { + println!("foo"); + } +} + +//- /foo.rs +pub const $0FOO: bool = true; +"#, + r#" +//- /main.rs +use foo::Bool; + +mod foo; + +fn main() { + if foo::FOO == Bool::True { + println!("foo"); + } +} + +//- /foo.rs +#[derive(PartialEq, Eq)] +pub enum Bool { True, False } + +pub const FOO: Bool = Bool::True; +"#, + ) + } + + #[test] + fn const_cross_file_and_module() { + check_assist( + bool_to_enum, + r#" +//- /main.rs +mod foo; + +fn main() { + use foo::bar; + + if bar::BAR { + println!("foo"); + } +} + +//- /foo.rs +pub mod bar { + pub const $0BAR: bool = false; +} +"#, + r#" +//- /main.rs +use crate::foo::bar::Bool; + +mod foo; + +fn main() { + use foo::bar; + + if bar::BAR == Bool::True { + println!("foo"); + } +} + +//- /foo.rs +pub mod bar { + #[derive(PartialEq, Eq)] + pub enum Bool { True, False } + + pub const BAR: Bool = Bool::False; +} +"#, + ) + } + + #[test] + fn const_in_impl_cross_file() { + check_assist( + bool_to_enum, + r#" +//- /main.rs +mod foo; + +struct Foo; + +impl Foo { + pub const $0BOOL: bool = true; +} + +//- /foo.rs +use crate::Foo; + +fn foo() -> bool { + Foo::BOOL +} +"#, + r#" +//- /main.rs +mod foo; + +struct Foo; + +#[derive(PartialEq, Eq)] +pub enum Bool { True, False } + +impl Foo { + pub const BOOL: Bool = Bool::True; +} + +//- /foo.rs +use crate::{Foo, Bool}; + +fn foo() -> bool { + Foo::BOOL == Bool::True +} +"#, + ) + } + + #[test] + fn const_in_trait() { + check_assist( + bool_to_enum, + r#" +trait Foo { + const $0BOOL: bool; +} + +impl Foo for usize { + const BOOL: bool = true; +} + +fn main() { + if <usize as Foo>::BOOL { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +trait Foo { + const BOOL: Bool; +} + +impl Foo for usize { + const BOOL: Bool = Bool::True; +} + +fn main() { + if <usize as Foo>::BOOL == Bool::True { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn const_non_bool() { + cov_mark::check!(not_applicable_non_bool_const); + check_assist_not_applicable( + bool_to_enum, + r#" +const $0FOO: &str = "foo"; + +fn main() { + println!("{FOO}"); +} +"#, + ) + } + + #[test] + fn static_basic() { + check_assist( + bool_to_enum, + r#" +static mut $0BOOL: bool = true; + +fn main() { + unsafe { BOOL = false }; + if unsafe { BOOL } { + println!("foo"); + } +} +"#, + r#" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +static mut BOOL: Bool = Bool::True; + +fn main() { + unsafe { BOOL = Bool::False }; + if unsafe { BOOL == Bool::True } { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn static_non_bool() { + cov_mark::check!(not_applicable_non_bool_static); + check_assist_not_applicable( + bool_to_enum, + r#" +static mut $0FOO: usize = 0; + +fn main() { + if unsafe { FOO } == 0 { + println!("foo"); + } +} +"#, + ) + } + + #[test] + fn not_applicable_to_other_names() { + check_assist_not_applicable(bool_to_enum, "fn $0main() {}") + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs index 1acd5ee9728..3f478ee7d39 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -25,9 +25,7 @@ pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_>) let comment = ctx.find_token_at_offset::<ast::Comment>()?; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { - if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() { - return None; - } + Whitespace::cast(prev).filter(|w| w.text().contains('\n'))?; } match comment.kind().shape { @@ -78,7 +76,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { // Establish the target of our edit based on the comments we found let target = TextRange::new( comments[0].syntax().text_range().start(), - comments.last().unwrap().syntax().text_range().end(), + comments.last()?.syntax().text_range().end(), ); acc.add( @@ -91,8 +89,12 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { // contents of each line comment when they're put into the block comment. let indentation = IndentLevel::from_token(comment.syntax()); - let block_comment_body = - comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); + let block_comment_body = comments + .into_iter() + .map(|c| line_comment_text(indentation, c)) + .collect::<Vec<String>>() + .into_iter() + .join("\n"); let block_prefix = CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix(); @@ -160,7 +162,8 @@ pub(crate) fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> { // // But since such comments aren't idiomatic we're okay with this. pub(crate) fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String { - let contents_without_prefix = comm.text().strip_prefix(comm.prefix()).unwrap(); + let text = comm.text(); + let contents_without_prefix = text.strip_prefix(comm.prefix()).unwrap_or(text); let contents = contents_without_prefix.strip_prefix(' ').unwrap_or(contents_without_prefix); // Don't add the indentation if the line is empty diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index 872b52c98ff..d649f13d6ee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -50,7 +50,12 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - _ => return None, }; - mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def, ctx.config.prefer_no_std)?) + mod_path_to_ast(&module.find_use_path( + ctx.db(), + src_type_def, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?) }; let dest_type = match &ast_trait { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 7d0e424769e..73ba3f5c4cd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -51,22 +51,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' // Check if there is an IfLet that we can handle. let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) { let let_ = single_let(cond)?; - match let_.pat() { - Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => { - let path = pat.path()?; - if path.qualifier().is_some() { - return None; - } - - let bound_ident = pat.fields().next()?; - if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) { - return None; - } - - (Some((path, bound_ident)), let_.expr()?) - } - _ => return None, // Unsupported IfLet. - } + (Some(let_.pat()?), let_.expr()?) } else { (None, cond) }; @@ -136,11 +121,10 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' }; new_expr.syntax().clone_for_update() } - Some((path, bound_ident)) => { + Some(pat) => { // If-let. - let pat = make::tuple_struct_pat(path, once(bound_ident)); let let_else_stmt = make::let_else_stmt( - pat.into(), + pat, None, cond_expr, ast::make::tail_only_block_expr(early_expression), @@ -443,6 +427,60 @@ fn main() { } #[test] + fn convert_arbitrary_if_let_patterns() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + $0if let None = Some(92) { + foo(); + } +} +"#, + r#" +fn main() { + let None = Some(92) else { return }; + foo(); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + $0if let [1, x] = [1, 92] { + foo(x); + } +} +"#, + r#" +fn main() { + let [1, x] = [1, 92] else { return }; + foo(x); +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +fn main() { + $0if let (Some(x), None) = (Some(92), None) { + foo(x); + } +} +"#, + r#" +fn main() { + let (Some(x), None) = (Some(92), None) else { return }; + foo(x); +} +"#, + ); + } + + #[test] fn ignore_already_converted_if() { check_assist_not_applicable( convert_to_guarded_return, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs new file mode 100644 index 00000000000..1f3caa7db33 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -0,0 +1,887 @@ +use either::Either; +use hir::ModuleDef; +use ide_db::{ + assists::{AssistId, AssistKind}, + defs::Definition, + helpers::mod_path_to_ast, + imports::insert_use::{insert_use, ImportScope}, + search::{FileReference, UsageSearchResult}, + source_change::SourceChangeBuilder, + syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, + FxHashSet, +}; +use syntax::{ + ast::{self, edit::IndentLevel, edit_in_place::Indent, make, HasName}, + match_ast, ted, AstNode, SyntaxNode, +}; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: convert_tuple_return_type_to_struct +// +// This converts the return type of a function from a tuple type +// into a tuple struct and updates the body accordingly. +// +// ``` +// fn bar() { +// let (a, b, c) = foo(); +// } +// +// fn foo() -> ($0u32, u32, u32) { +// (1, 2, 3) +// } +// ``` +// -> +// ``` +// fn bar() { +// let FooResult(a, b, c) = foo(); +// } +// +// struct FooResult(u32, u32, u32); +// +// fn foo() -> FooResult { +// FooResult(1, 2, 3) +// } +// ``` +pub(crate) fn convert_tuple_return_type_to_struct( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; + let type_ref = ret_type.ty()?; + + let ast::Type::TupleType(tuple_ty) = &type_ref else { return None }; + if tuple_ty.fields().any(|field| matches!(field, ast::Type::ImplTraitType(_))) { + return None; + } + + let fn_ = ret_type.syntax().parent().and_then(ast::Fn::cast)?; + let fn_def = ctx.sema.to_def(&fn_)?; + let fn_name = fn_.name()?; + let target_module = ctx.sema.scope(fn_.syntax())?.module().nearest_non_block_module(ctx.db()); + + let target = type_ref.syntax().text_range(); + acc.add( + AssistId("convert_tuple_return_type_to_struct", AssistKind::RefactorRewrite), + "Convert tuple return type to tuple struct", + target, + move |edit| { + let ret_type = edit.make_mut(ret_type); + let fn_ = edit.make_mut(fn_); + + let usages = Definition::Function(fn_def).usages(&ctx.sema).all(); + let struct_name = format!("{}Result", stdx::to_camel_case(&fn_name.to_string())); + let parent = fn_.syntax().ancestors().find_map(<Either<ast::Impl, ast::Trait>>::cast); + add_tuple_struct_def( + edit, + ctx, + &usages, + parent.as_ref().map(|it| it.syntax()).unwrap_or(fn_.syntax()), + tuple_ty, + &struct_name, + &target_module, + ); + + ted::replace( + ret_type.syntax(), + make::ret_type(make::ty(&struct_name)).syntax().clone_for_update(), + ); + + if let Some(fn_body) = fn_.body() { + replace_body_return_values(ast::Expr::BlockExpr(fn_body), &struct_name); + } + + replace_usages(edit, ctx, &usages, &struct_name, &target_module); + }, + ) +} + +/// Replaces tuple usages with the corresponding tuple struct pattern. +fn replace_usages( + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + usages: &UsageSearchResult, + struct_name: &str, + target_module: &hir::Module, +) { + for (file_id, references) in usages.iter() { + edit.edit_file(*file_id); + + let refs_with_imports = + augment_references_with_imports(edit, ctx, references, struct_name, target_module); + + refs_with_imports.into_iter().rev().for_each(|(name, import_data)| { + if let Some(fn_) = name.syntax().parent().and_then(ast::Fn::cast) { + cov_mark::hit!(replace_trait_impl_fns); + + if let Some(ret_type) = fn_.ret_type() { + ted::replace( + ret_type.syntax(), + make::ret_type(make::ty(struct_name)).syntax().clone_for_update(), + ); + } + + if let Some(fn_body) = fn_.body() { + replace_body_return_values(ast::Expr::BlockExpr(fn_body), struct_name); + } + } else { + // replace tuple patterns + let pats = name + .syntax() + .ancestors() + .find(|node| { + ast::CallExpr::can_cast(node.kind()) + || ast::MethodCallExpr::can_cast(node.kind()) + }) + .and_then(|node| node.parent()) + .and_then(node_to_pats) + .unwrap_or(Vec::new()); + + let tuple_pats = pats.iter().filter_map(|pat| match pat { + ast::Pat::TuplePat(tuple_pat) => Some(tuple_pat), + _ => None, + }); + for tuple_pat in tuple_pats { + ted::replace( + tuple_pat.syntax(), + make::tuple_struct_pat( + make::path_from_text(struct_name), + tuple_pat.fields(), + ) + .clone_for_update() + .syntax(), + ); + } + } + // add imports across modules where needed + if let Some((import_scope, path)) = import_data { + insert_use(&import_scope, path, &ctx.config.insert_use); + } + }) + } +} + +fn node_to_pats(node: SyntaxNode) -> Option<Vec<ast::Pat>> { + match_ast! { + match node { + ast::LetStmt(it) => it.pat().map(|pat| vec![pat]), + ast::LetExpr(it) => it.pat().map(|pat| vec![pat]), + ast::MatchExpr(it) => it.match_arm_list().map(|arm_list| { + arm_list.arms().filter_map(|arm| arm.pat()).collect() + }), + _ => None, + } + } +} + +fn augment_references_with_imports( + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + references: &[FileReference], + struct_name: &str, + target_module: &hir::Module, +) -> Vec<(ast::NameLike, Option<(ImportScope, ast::Path)>)> { + let mut visited_modules = FxHashSet::default(); + + references + .iter() + .filter_map(|FileReference { name, .. }| { + ctx.sema.scope(name.syntax()).map(|scope| (name, scope.module())) + }) + .map(|(name, ref_module)| { + let new_name = edit.make_mut(name.clone()); + + // if the referenced module is not the same as the target one and has not been seen before, add an import + let import_data = if ref_module.nearest_non_block_module(ctx.db()) != *target_module + && !visited_modules.contains(&ref_module) + { + visited_modules.insert(ref_module); + + let import_scope = + ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); + let path = ref_module + .find_use_path_prefixed( + ctx.sema.db, + ModuleDef::Module(*target_module), + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) + .map(|mod_path| { + make::path_concat( + mod_path_to_ast(&mod_path), + make::path_from_text(struct_name), + ) + }); + + import_scope.zip(path) + } else { + None + }; + + (new_name, import_data) + }) + .collect() +} + +// Adds the definition of the tuple struct before the parent function. +fn add_tuple_struct_def( + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_>, + usages: &UsageSearchResult, + parent: &SyntaxNode, + tuple_ty: &ast::TupleType, + struct_name: &str, + target_module: &hir::Module, +) { + let make_struct_pub = usages + .iter() + .flat_map(|(_, refs)| refs) + .filter_map(|FileReference { name, .. }| { + ctx.sema.scope(name.syntax()).map(|scope| scope.module()) + }) + .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); + let visibility = if make_struct_pub { Some(make::visibility_pub()) } else { None }; + + let field_list = ast::FieldList::TupleFieldList(make::tuple_field_list( + tuple_ty.fields().map(|ty| make::tuple_field(visibility.clone(), ty)), + )); + let struct_name = make::name(struct_name); + let struct_def = make::struct_(visibility, struct_name, None, field_list).clone_for_update(); + + let indent = IndentLevel::from_node(parent); + struct_def.reindent_to(indent); + + edit.insert(parent.text_range().start(), format!("{struct_def}\n\n{indent}")); +} + +/// Replaces each returned tuple in `body` with the constructor of the tuple struct named `struct_name`. +fn replace_body_return_values(body: ast::Expr, struct_name: &str) { + let mut exprs_to_wrap = Vec::new(); + + let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); + walk_expr(&body, &mut |expr| { + if let ast::Expr::ReturnExpr(ret_expr) = expr { + if let Some(ret_expr_arg) = &ret_expr.expr() { + for_each_tail_expr(ret_expr_arg, tail_cb); + } + } + }); + for_each_tail_expr(&body, tail_cb); + + for ret_expr in exprs_to_wrap { + if let ast::Expr::TupleExpr(tuple_expr) = &ret_expr { + let struct_constructor = make::expr_call( + make::expr_path(make::ext::ident_path(struct_name)), + make::arg_list(tuple_expr.fields()), + ) + .clone_for_update(); + ted::replace(ret_expr.syntax(), struct_constructor.syntax()); + } + } +} + +fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) { + match e { + ast::Expr::BreakExpr(break_expr) => { + if let Some(break_expr_arg) = break_expr.expr() { + for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) + } + } + ast::Expr::ReturnExpr(_) => { + // all return expressions have already been handled by the walk loop + } + e => acc.push(e.clone()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn function_basic() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(&'static str, bool) { + ("bar", true) +} +"#, + r#" +struct BarResult(&'static str, bool); + +fn bar() -> BarResult { + BarResult("bar", true) +} +"#, + ) + } + + #[test] + fn struct_and_usages_indented() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +mod foo { + pub(crate) fn foo() { + let (bar, baz) = bar(); + println!("{bar} {baz}"); + } + + pub(crate) fn bar() -> $0(usize, bool) { + (42, true) + } +} +"#, + r#" +mod foo { + pub(crate) fn foo() { + let BarResult(bar, baz) = bar(); + println!("{bar} {baz}"); + } + + struct BarResult(usize, bool); + + pub(crate) fn bar() -> BarResult { + BarResult(42, true) + } +} +"#, + ) + } + + #[test] + fn field_usage() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(usize, bool) { + (42, true) +} + +fn main() { + let bar_result = bar(); + println!("{} {}", bar_result.1, bar().0); +} +"#, + r#" +struct BarResult(usize, bool); + +fn bar() -> BarResult { + BarResult(42, true) +} + +fn main() { + let bar_result = bar(); + println!("{} {}", bar_result.1, bar().0); +} +"#, + ) + } + + #[test] + fn method_usage() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +struct Foo; + +impl Foo { + fn foo(&self, x: usize) -> $0(usize, usize) { + (x, x) + } +} + +fn main() { + let foo = Foo {}; + let (x, y) = foo.foo(2); +} +"#, + r#" +struct Foo; + +struct FooResult(usize, usize); + +impl Foo { + fn foo(&self, x: usize) -> FooResult { + FooResult(x, x) + } +} + +fn main() { + let foo = Foo {}; + let FooResult(x, y) = foo.foo(2); +} +"#, + ) + } + + #[test] + fn method_usage_within_same_impl() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +struct Foo; + +impl Foo { + fn new() -> $0(usize, usize) { + (0, 0) + } + + fn foo() { + let (mut foo1, mut foo2) = Self::new(); + } +} +"#, + r#" +struct Foo; + +struct NewResult(usize, usize); + +impl Foo { + fn new() -> NewResult { + NewResult(0, 0) + } + + fn foo() { + let NewResult(mut foo1, mut foo2) = Self::new(); + } +} +"#, + ) + } + + #[test] + fn multiple_usages() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(usize, usize) { + (42, 24) +} + +fn main() { + let bar_result = bar(); + let (foo, b) = bar(); + let (b, baz) = bar(); + + if foo == b && b == baz { + println!("{} {}", bar_result.1, bar().0); + } +} +"#, + r#" +struct BarResult(usize, usize); + +fn bar() -> BarResult { + BarResult(42, 24) +} + +fn main() { + let bar_result = bar(); + let BarResult(foo, b) = bar(); + let BarResult(b, baz) = bar(); + + if foo == b && b == baz { + println!("{} {}", bar_result.1, bar().0); + } +} +"#, + ) + } + + #[test] + fn usage_match_tuple_pat() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(usize, bool) { + (42, true) +} + +fn main() { + match bar() { + x if x.0 == 0 => println!("0"), + (x, false) => println!("{x}"), + (42, true) => println!("bar"), + _ => println!("foo"), + } +} +"#, + r#" +struct BarResult(usize, bool); + +fn bar() -> BarResult { + BarResult(42, true) +} + +fn main() { + match bar() { + x if x.0 == 0 => println!("0"), + BarResult(x, false) => println!("{x}"), + BarResult(42, true) => println!("bar"), + _ => println!("foo"), + } +} +"#, + ) + } + + #[test] + fn usage_if_let_tuple_pat() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(usize, bool) { + (42, true) +} + +fn main() { + if let (42, true) = bar() { + println!("bar") + } +} +"#, + r#" +struct BarResult(usize, bool); + +fn bar() -> BarResult { + BarResult(42, true) +} + +fn main() { + if let BarResult(42, true) = bar() { + println!("bar") + } +} +"#, + ) + } + + #[test] + fn function_nested_outer() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(usize, bool) { + fn foo() -> (usize, bool) { + (42, true) + } + + foo() +} +"#, + r#" +struct BarResult(usize, bool); + +fn bar() -> BarResult { + fn foo() -> (usize, bool) { + (42, true) + } + + foo() +} +"#, + ) + } + + #[test] + fn function_nested_inner() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> (usize, bool) { + fn foo() -> $0(usize, bool) { + (42, true) + } + + foo() +} +"#, + r#" +fn bar() -> (usize, bool) { + struct FooResult(usize, bool); + + fn foo() -> FooResult { + FooResult(42, true) + } + + foo() +} +"#, + ) + } + + #[test] + fn trait_impl_and_usage() { + cov_mark::check!(replace_trait_impl_fns); + check_assist( + convert_tuple_return_type_to_struct, + r#" +struct Struct; + +trait Foo { + fn foo(&self) -> $0(usize, bool); +} + +impl Foo for Struct { + fn foo(&self) -> (usize, bool) { + (0, true) + } +} + +fn main() { + let s = Struct {}; + let (foo, bar) = s.foo(); + let (foo, bar) = Struct::foo(&s); + println!("{foo} {bar}"); +} +"#, + r#" +struct Struct; + +struct FooResult(usize, bool); + +trait Foo { + fn foo(&self) -> FooResult; +} + +impl Foo for Struct { + fn foo(&self) -> FooResult { + FooResult(0, true) + } +} + +fn main() { + let s = Struct {}; + let FooResult(foo, bar) = s.foo(); + let FooResult(foo, bar) = Struct::foo(&s); + println!("{foo} {bar}"); +} +"#, + ) + } + + #[test] + fn body_wraps_nested() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn foo() -> $0(u8, usize, u32) { + if true { + match 3 { + 0 => (1, 2, 3), + _ => return (4, 5, 6), + } + } else { + (2, 1, 3) + } +} +"#, + r#" +struct FooResult(u8, usize, u32); + +fn foo() -> FooResult { + if true { + match 3 { + 0 => FooResult(1, 2, 3), + _ => return FooResult(4, 5, 6), + } + } else { + FooResult(2, 1, 3) + } +} +"#, + ) + } + + #[test] + fn body_wraps_break_and_return() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn foo(mut i: isize) -> (usize, $0u32, u8) { + if i < 0 { + return (0, 0, 0); + } + + loop { + if i == 2 { + println!("foo"); + break (1, 2, 3); + } + i += 1; + } +} +"#, + r#" +struct FooResult(usize, u32, u8); + +fn foo(mut i: isize) -> FooResult { + if i < 0 { + return FooResult(0, 0, 0); + } + + loop { + if i == 2 { + println!("foo"); + break FooResult(1, 2, 3); + } + i += 1; + } +} +"#, + ) + } + + #[test] + fn body_doesnt_wrap_identifier() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn foo() -> $0(u8, usize, u32) { + let tuple = (1, 2, 3); + tuple +} +"#, + r#" +struct FooResult(u8, usize, u32); + +fn foo() -> FooResult { + let tuple = (1, 2, 3); + tuple +} +"#, + ) + } + + #[test] + fn body_doesnt_wrap_other_exprs() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar(num: usize) -> (u8, usize, u32) { + (1, num, 3) +} + +fn foo() -> $0(u8, usize, u32) { + bar(2) +} +"#, + r#" +fn bar(num: usize) -> (u8, usize, u32) { + (1, num, 3) +} + +struct FooResult(u8, usize, u32); + +fn foo() -> FooResult { + bar(2) +} +"#, + ) + } + + #[test] + fn cross_file_and_module() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +//- /main.rs +mod foo; + +fn main() { + use foo::bar; + + let (bar, baz) = bar::bar(); + println!("{}", bar == baz); +} + +//- /foo.rs +pub mod bar { + pub fn bar() -> $0(usize, usize) { + (1, 3) + } +} +"#, + r#" +//- /main.rs +use crate::foo::bar::BarResult; + +mod foo; + +fn main() { + use foo::bar; + + let BarResult(bar, baz) = bar::bar(); + println!("{}", bar == baz); +} + +//- /foo.rs +pub mod bar { + pub struct BarResult(pub usize, pub usize); + + pub fn bar() -> BarResult { + BarResult(1, 3) + } +} +"#, + ) + } + + #[test] + fn does_not_replace_nested_usage() { + check_assist( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(usize, bool) { + (42, true) +} + +fn main() { + let ((bar1, bar2), foo) = (bar(), 3); + println!("{bar1} {bar2} {foo}"); +} +"#, + r#" +struct BarResult(usize, bool); + +fn bar() -> BarResult { + BarResult(42, true) +} + +fn main() { + let ((bar1, bar2), foo) = (bar(), 3); + println!("{bar1} {bar2} {foo}"); +} +"#, + ) + } + + #[test] + fn function_with_non_tuple_return_type() { + check_assist_not_applicable( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0usize { + 0 +} +"#, + ) + } + + #[test] + fn function_with_impl_type() { + check_assist_not_applicable( + convert_tuple_return_type_to_struct, + r#" +fn bar() -> $0(impl Clone, usize) { + ("bar", 0) +} +"#, + ) + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index f30ca2552d3..65b497e83aa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -3,10 +3,12 @@ use ide_db::{ defs::Definition, search::{FileReference, SearchScope, UsageSearchResult}, }; +use itertools::Itertools; use syntax::{ - ast::{self, AstNode, FieldExpr, HasName, IdentPat, MethodCallExpr}, - TextRange, + ast::{self, make, AstNode, FieldExpr, HasName, IdentPat, MethodCallExpr}, + ted, T, }; +use text_edit::TextRange; use crate::assist_context::{AssistContext, Assists, SourceChangeBuilder}; @@ -61,27 +63,36 @@ pub(crate) fn destructure_tuple_binding_impl( acc.add( AssistId("destructure_tuple_binding_in_sub_pattern", AssistKind::RefactorRewrite), "Destructure tuple in sub-pattern", - data.range, - |builder| { - edit_tuple_assignment(ctx, builder, &data, true); - edit_tuple_usages(&data, builder, ctx, true); - }, + data.ident_pat.syntax().text_range(), + |edit| destructure_tuple_edit_impl(ctx, edit, &data, true), ); } acc.add( AssistId("destructure_tuple_binding", AssistKind::RefactorRewrite), if with_sub_pattern { "Destructure tuple in place" } else { "Destructure tuple" }, - data.range, - |builder| { - edit_tuple_assignment(ctx, builder, &data, false); - edit_tuple_usages(&data, builder, ctx, false); - }, + data.ident_pat.syntax().text_range(), + |edit| destructure_tuple_edit_impl(ctx, edit, &data, false), ); Some(()) } +fn destructure_tuple_edit_impl( + ctx: &AssistContext<'_>, + edit: &mut SourceChangeBuilder, + data: &TupleData, + in_sub_pattern: bool, +) { + let assignment_edit = edit_tuple_assignment(ctx, edit, &data, in_sub_pattern); + let current_file_usages_edit = edit_tuple_usages(&data, edit, ctx, in_sub_pattern); + + assignment_edit.apply(); + if let Some(usages_edit) = current_file_usages_edit { + usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit)) + } +} + fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> { if ident_pat.at_token().is_some() { // Cannot destructure pattern with sub-pattern: @@ -109,7 +120,6 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat } let name = ident_pat.name()?.to_string(); - let range = ident_pat.syntax().text_range(); let usages = ctx.sema.to_def(&ident_pat).map(|def| { Definition::Local(def) @@ -122,7 +132,7 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat .map(|i| generate_name(ctx, i, &name, &ident_pat, &usages)) .collect::<Vec<_>>(); - Some(TupleData { ident_pat, range, ref_type, field_names, usages }) + Some(TupleData { ident_pat, ref_type, field_names, usages }) } fn generate_name( @@ -142,72 +152,100 @@ enum RefType { } struct TupleData { ident_pat: IdentPat, - // name: String, - range: TextRange, ref_type: Option<RefType>, field_names: Vec<String>, - // field_types: Vec<Type>, usages: Option<UsageSearchResult>, } fn edit_tuple_assignment( ctx: &AssistContext<'_>, - builder: &mut SourceChangeBuilder, + edit: &mut SourceChangeBuilder, data: &TupleData, in_sub_pattern: bool, -) { +) -> AssignmentEdit { + let ident_pat = edit.make_mut(data.ident_pat.clone()); + let tuple_pat = { let original = &data.ident_pat; let is_ref = original.ref_token().is_some(); let is_mut = original.mut_token().is_some(); - let fields = data.field_names.iter().map(|name| { - ast::Pat::from(ast::make::ident_pat(is_ref, is_mut, ast::make::name(name))) - }); - ast::make::tuple_pat(fields) + let fields = data + .field_names + .iter() + .map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name)))); + make::tuple_pat(fields).clone_for_update() }; - let add_cursor = |text: &str| { - // place cursor on first tuple item - let first_tuple = &data.field_names[0]; - text.replacen(first_tuple, &format!("$0{first_tuple}"), 1) - }; + if let Some(cap) = ctx.config.snippet_cap { + // place cursor on first tuple name + if let Some(ast::Pat::IdentPat(first_pat)) = tuple_pat.fields().next() { + edit.add_tabstop_before( + cap, + first_pat.name().expect("first ident pattern should have a name"), + ) + } + } - // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` - if in_sub_pattern { - let text = format!(" @ {tuple_pat}"); - match ctx.config.snippet_cap { - Some(cap) => { - let snip = add_cursor(&text); - builder.insert_snippet(cap, data.range.end(), snip); - } - None => builder.insert(data.range.end(), text), - }; - } else { - let text = tuple_pat.to_string(); - match ctx.config.snippet_cap { - Some(cap) => { - let snip = add_cursor(&text); - builder.replace_snippet(cap, data.range, snip); - } - None => builder.replace(data.range, text), - }; + AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern } +} +struct AssignmentEdit { + ident_pat: ast::IdentPat, + tuple_pat: ast::TuplePat, + in_sub_pattern: bool, +} + +impl AssignmentEdit { + fn apply(self) { + // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` + if self.in_sub_pattern { + self.ident_pat.set_pat(Some(self.tuple_pat.into())) + } else { + ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) + } } } fn edit_tuple_usages( data: &TupleData, - builder: &mut SourceChangeBuilder, + edit: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, in_sub_pattern: bool, -) { +) -> Option<Vec<EditTupleUsage>> { + let mut current_file_usages = None; + if let Some(usages) = data.usages.as_ref() { - for (file_id, refs) in usages.iter() { - builder.edit_file(*file_id); + // We need to collect edits first before actually applying them + // as mapping nodes to their mutable node versions requires an + // unmodified syntax tree. + // + // We also defer editing usages in the current file first since + // tree mutation in the same file breaks when `builder.edit_file` + // is called + + if let Some((_, refs)) = usages.iter().find(|(file_id, _)| **file_id == ctx.file_id()) { + current_file_usages = Some( + refs.iter() + .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern)) + .collect_vec(), + ); + } - for r in refs { - edit_tuple_usage(ctx, builder, r, data, in_sub_pattern); + for (file_id, refs) in usages.iter() { + if *file_id == ctx.file_id() { + continue; } + + edit.edit_file(*file_id); + + let tuple_edits = refs + .iter() + .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern)) + .collect_vec(); + + tuple_edits.into_iter().for_each(|tuple_edit| tuple_edit.apply(edit)) } } + + current_file_usages } fn edit_tuple_usage( ctx: &AssistContext<'_>, @@ -215,25 +253,14 @@ fn edit_tuple_usage( usage: &FileReference, data: &TupleData, in_sub_pattern: bool, -) { +) -> Option<EditTupleUsage> { match detect_tuple_index(usage, data) { - Some(index) => edit_tuple_field_usage(ctx, builder, data, index), - None => { - if in_sub_pattern { - cov_mark::hit!(destructure_tuple_call_with_subpattern); - return; - } - - // no index access -> make invalid -> requires handling by user - // -> put usage in block comment - // - // Note: For macro invocations this might result in still valid code: - // When a macro accepts the tuple as argument, as well as no arguments at all, - // uncommenting the tuple still leaves the macro call working (see `tests::in_macro_call::empty_macro`). - // But this is an unlikely case. Usually the resulting macro call will become erroneous. - builder.insert(usage.range.start(), "/*"); - builder.insert(usage.range.end(), "*/"); + Some(index) => Some(edit_tuple_field_usage(ctx, builder, data, index)), + None if in_sub_pattern => { + cov_mark::hit!(destructure_tuple_call_with_subpattern); + return None; } + None => Some(EditTupleUsage::NoIndex(usage.range)), } } @@ -242,19 +269,47 @@ fn edit_tuple_field_usage( builder: &mut SourceChangeBuilder, data: &TupleData, index: TupleIndex, -) { +) -> EditTupleUsage { let field_name = &data.field_names[index.index]; + let field_name = make::expr_path(make::ext::ident_path(field_name)); if data.ref_type.is_some() { - let ref_data = handle_ref_field_usage(ctx, &index.field_expr); - builder.replace(ref_data.range, ref_data.format(field_name)); + let (replace_expr, ref_data) = handle_ref_field_usage(ctx, &index.field_expr); + let replace_expr = builder.make_mut(replace_expr); + EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr(field_name)) } else { - builder.replace(index.range, field_name); + let field_expr = builder.make_mut(index.field_expr); + EditTupleUsage::ReplaceExpr(field_expr.into(), field_name) + } +} +enum EditTupleUsage { + /// no index access -> make invalid -> requires handling by user + /// -> put usage in block comment + /// + /// Note: For macro invocations this might result in still valid code: + /// When a macro accepts the tuple as argument, as well as no arguments at all, + /// uncommenting the tuple still leaves the macro call working (see `tests::in_macro_call::empty_macro`). + /// But this is an unlikely case. Usually the resulting macro call will become erroneous. + NoIndex(TextRange), + ReplaceExpr(ast::Expr, ast::Expr), +} + +impl EditTupleUsage { + fn apply(self, edit: &mut SourceChangeBuilder) { + match self { + EditTupleUsage::NoIndex(range) => { + edit.insert(range.start(), "/*"); + edit.insert(range.end(), "*/"); + } + EditTupleUsage::ReplaceExpr(target_expr, replace_with) => { + ted::replace(target_expr.syntax(), replace_with.clone_for_update().syntax()) + } + } } } + struct TupleIndex { index: usize, - range: TextRange, field_expr: FieldExpr, } fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option<TupleIndex> { @@ -296,7 +351,7 @@ fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option<TupleIn return None; } - Some(TupleIndex { index: idx, range: field_expr.syntax().text_range(), field_expr }) + Some(TupleIndex { index: idx, field_expr }) } else { // tuple index out of range None @@ -307,32 +362,34 @@ fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option<TupleIn } struct RefData { - range: TextRange, needs_deref: bool, needs_parentheses: bool, } impl RefData { - fn format(&self, field_name: &str) -> String { - match (self.needs_deref, self.needs_parentheses) { - (true, true) => format!("(*{field_name})"), - (true, false) => format!("*{field_name}"), - (false, true) => format!("({field_name})"), - (false, false) => field_name.to_string(), + fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr { + if self.needs_deref { + expr = make::expr_prefix(T![*], expr); } + + if self.needs_parentheses { + expr = make::expr_paren(expr); + } + + return expr; } } -fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> RefData { +fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> (ast::Expr, RefData) { let s = field_expr.syntax(); - let mut ref_data = - RefData { range: s.text_range(), needs_deref: true, needs_parentheses: true }; + let mut ref_data = RefData { needs_deref: true, needs_parentheses: true }; + let mut target_node = field_expr.clone().into(); let parent = match s.parent().map(ast::Expr::cast) { Some(Some(parent)) => parent, Some(None) => { ref_data.needs_parentheses = false; - return ref_data; + return (target_node, ref_data); } - None => return ref_data, + None => return (target_node, ref_data), }; match parent { @@ -342,7 +399,7 @@ fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> Re // there might be a ref outside: `&(t.0)` -> can be removed if let Some(it) = it.syntax().parent().and_then(ast::RefExpr::cast) { ref_data.needs_deref = false; - ref_data.range = it.syntax().text_range(); + target_node = it.into(); } } ast::Expr::RefExpr(it) => { @@ -351,8 +408,8 @@ fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> Re ref_data.needs_parentheses = false; // might be surrounded by parens -> can be removed too match it.syntax().parent().and_then(ast::ParenExpr::cast) { - Some(parent) => ref_data.range = parent.syntax().text_range(), - None => ref_data.range = it.syntax().text_range(), + Some(parent) => target_node = parent.into(), + None => target_node = it.into(), }; } // higher precedence than deref `*` @@ -414,7 +471,7 @@ fn handle_ref_field_usage(ctx: &AssistContext<'_>, field_expr: &FieldExpr) -> Re } }; - ref_data + (target_node, ref_data) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs index ddc8a50ed40..c859e98524e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs @@ -33,9 +33,7 @@ pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { - if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() { - return None; - } + Whitespace::cast(prev).filter(|w| w.text().contains('\n'))?; } let indentation = IndentLevel::from_token(comment.syntax()).to_string(); @@ -50,7 +48,7 @@ pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> ( TextRange::new( comments[0].syntax().text_range().start(), - comments.last().unwrap().syntax().text_range().end(), + comments.last()?.syntax().text_range().end(), ), Either::Right(comments), ) @@ -71,9 +69,11 @@ pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> .map(|l| l.strip_prefix(&indentation).unwrap_or(l)) .join("\n") } - Either::Right(comments) => { - comments.into_iter().map(|c| line_comment_text(IndentLevel(0), c)).join("\n") - } + Either::Right(comments) => comments + .into_iter() + .map(|cm| line_comment_text(IndentLevel(0), cm)) + .collect::<Vec<_>>() + .join("\n"), }; let hashes = "#".repeat(required_hashes(&text)); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index de591cfde94..6b48d158815 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -163,6 +163,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ModuleDef::from(control_flow_enum), ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ); if let Some(mod_path) = mod_path { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index e4f64ccc754..37db27a8fc0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -384,6 +384,7 @@ fn process_references( *enum_module_def, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ); if let Some(mut mod_path) = mod_path { mod_path.pop_segment(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 014c23197fa..e7c884dcb70 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -29,22 +29,31 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; // } // ``` pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - if ctx.has_empty_selection() { - return None; - } - - let node = match ctx.covering_element() { - NodeOrToken::Node(it) => it, - NodeOrToken::Token(it) if it.kind() == COMMENT => { - cov_mark::hit!(extract_var_in_comment_is_not_applicable); + let node = if ctx.has_empty_selection() { + if let Some(expr_stmt) = ctx.find_node_at_offset::<ast::ExprStmt>() { + expr_stmt.syntax().clone() + } else if let Some(expr) = ctx.find_node_at_offset::<ast::Expr>() { + expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone() + } else { return None; } - NodeOrToken::Token(it) => it.parent()?, + } else { + match ctx.covering_element() { + NodeOrToken::Node(it) => it, + NodeOrToken::Token(it) if it.kind() == COMMENT => { + cov_mark::hit!(extract_var_in_comment_is_not_applicable); + return None; + } + NodeOrToken::Token(it) => it.parent()?, + } }; + let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?; + let range = node.text_range(); + let to_extract = node .descendants() - .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range())) + .take_while(|it| range.contains_range(it.text_range())) .find_map(valid_target_expr)?; let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted); @@ -236,6 +245,138 @@ mod tests { use super::*; #[test] + fn test_extract_var_simple_without_select() { + check_assist( + extract_variable, + r#" +fn main() -> i32 { + if true { + 1 + } else { + 2 + }$0 +} +"#, + r#" +fn main() -> i32 { + let $0var_name = if true { + 1 + } else { + 2 + }; + var_name +} +"#, + ); + + check_assist( + extract_variable, + r#" +fn foo() -> i32 { 1 } +fn main() { + foo();$0 +} +"#, + r#" +fn foo() -> i32 { 1 } +fn main() { + let $0foo = foo(); +} +"#, + ); + + check_assist( + extract_variable, + r#" +fn main() { + let a = Some(2); + a.is_some();$0 +} +"#, + r#" +fn main() { + let a = Some(2); + let $0is_some = a.is_some(); +} +"#, + ); + + check_assist( + extract_variable, + r#" +fn main() { + "hello"$0; +} +"#, + r#" +fn main() { + let $0var_name = "hello"; +} +"#, + ); + + check_assist( + extract_variable, + r#" +fn main() { + 1 + 2$0; +} +"#, + r#" +fn main() { + let $0var_name = 1 + 2; +} +"#, + ); + + check_assist( + extract_variable, + r#" +fn main() { + match () { + () if true => 1, + _ => 2, + };$0 +} +"#, + r#" +fn main() { + let $0var_name = match () { + () if true => 1, + _ => 2, + }; +} +"#, + ); + } + + #[test] + fn test_extract_var_unit_expr_without_select_not_applicable() { + check_assist_not_applicable( + extract_variable, + r#" +fn foo() {} +fn main() { + foo()$0; +} +"#, + ); + + check_assist_not_applicable( + extract_variable, + r#" +fn foo() { + let mut i = 3; + if i >= 0 { + i += 1; + } else { + i -= 1; + }$0 +}"#, + ); + } + + #[test] fn test_extract_var_simple() { check_assist( extract_variable, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 81545396175..473c699b595 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -58,8 +58,12 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?; + let trait_path = module.find_use_path( + ctx.db(), + ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?; let field_type = field.ty()?; let field_name = field.name()?; @@ -99,8 +103,12 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?; + let trait_path = module.find_use_path( + ctx.db(), + ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + )?; let field_type = field.ty()?; let target = field.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 5b13e01b133..f74fc5df4bd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -404,7 +404,11 @@ impl FunctionBuilder { leading_ws, ret_type: fn_def.ret_type(), // PANIC: we guarantee we always create a function body with a tail expr - tail_expr: fn_def.body().unwrap().tail_expr().unwrap(), + tail_expr: fn_def + .body() + .expect("generated function should have a body") + .tail_expr() + .expect("function body should have a tail expression"), should_focus_return_type: self.should_focus_return_type, fn_def, trailing_ws, @@ -683,7 +687,7 @@ where { // This function should be only called with `Impl`, `Trait`, or `Function`, for which it's // infallible to get source ast. - let node = ctx.sema.source(def).unwrap().value; + let node = ctx.sema.source(def).expect("definition's source couldn't be found").value; let generic_params = node.generic_param_list().into_iter().flat_map(|it| it.generic_params()); let where_clauses = node.where_clause().into_iter().flat_map(|it| it.predicates()); (generic_params, where_clauses) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs new file mode 100644 index 00000000000..cb8ef395650 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -0,0 +1,202 @@ +use ide_db::famous_defs::FamousDefs; +use syntax::{ + ast::{self, make}, + ted, AstNode, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// FIXME: Generate proper `index_mut` method body refer to `index` method body may impossible due to the unpredicable case [#15581]. +// Here just leave the `index_mut` method body be same as `index` method body, user can modify it manually to meet their need. + +// Assist: generate_mut_trait_impl +// +// Adds a IndexMut impl from the `Index` trait. +// +// ``` +// # //- minicore: index +// pub enum Axis { X = 0, Y = 1, Z = 2 } +// +// impl<T> core::ops::Index$0<Axis> for [T; 3] { +// type Output = T; +// +// fn index(&self, index: Axis) -> &Self::Output { +// &self[index as usize] +// } +// } +// ``` +// -> +// ``` +// pub enum Axis { X = 0, Y = 1, Z = 2 } +// +// $0impl<T> core::ops::IndexMut<Axis> for [T; 3] { +// fn index_mut(&mut self, index: Axis) -> &mut Self::Output { +// &self[index as usize] +// } +// } +// +// impl<T> core::ops::Index<Axis> for [T; 3] { +// type Output = T; +// +// fn index(&self, index: Axis) -> &Self::Output { +// &self[index as usize] +// } +// } +// ``` +pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update(); + + let trait_ = impl_def.trait_()?; + if let ast::Type::PathType(trait_path) = trait_.clone() { + let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?; + let scope = ctx.sema.scope(trait_path.syntax())?; + if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? { + return None; + } + } + + // Index -> IndexMut + let index_trait = impl_def + .syntax() + .descendants() + .filter_map(ast::NameRef::cast) + .find(|it| it.text() == "Index")?; + ted::replace( + index_trait.syntax(), + make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(), + ); + + // index -> index_mut + let trait_method_name = impl_def + .syntax() + .descendants() + .filter_map(ast::Name::cast) + .find(|it| it.text() == "index")?; + ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax()); + + let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?; + ted::remove(type_alias.syntax()); + + // &self -> &mut self + let mut_self_param = make::mut_self_param(); + let self_param: ast::SelfParam = + impl_def.syntax().descendants().find_map(ast::SelfParam::cast)?; + ted::replace(self_param.syntax(), mut_self_param.clone_for_update().syntax()); + + // &Self::Output -> &mut Self::Output + let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?; + ted::replace( + ret_type.syntax(), + make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(), + ); + + let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it { + ast::AssocItem::Fn(f) => Some(f), + _ => None, + })?; + + let assoc_list = make::assoc_item_list().clone_for_update(); + assoc_list.add_item(syntax::ast::AssocItem::Fn(fn_)); + ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); + + let target = impl_def.syntax().text_range(); + acc.add( + AssistId("generate_mut_trait_impl", AssistKind::Generate), + "Generate `IndexMut` impl from this `Index` trait", + target, + |edit| { + edit.insert(target.start(), format!("$0{}\n\n", impl_def.to_string())); + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn test_generate_mut_trait_impl() { + check_assist( + generate_mut_trait_impl, + r#" +//- minicore: index +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl<T> core::ops::Index$0<Axis> for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +$0impl<T> core::ops::IndexMut<Axis> for [T; 3] { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + &self[index as usize] + } +} + +impl<T> core::ops::Index<Axis> for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#, + ); + + check_assist( + generate_mut_trait_impl, + r#" +//- minicore: index +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } +} +"#, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +$0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + let var_name = &self[index as usize]; + var_name + } +} + +impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } +} +"#, + ); + } + + #[test] + fn test_generate_mut_trait_impl_not_applicable() { + check_assist_not_applicable( + generate_mut_trait_impl, + r#" +pub trait Index<Idx: ?Sized> {} + +impl<T> Index$0<i32> for [T; 3] {} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 824255e4f8e..7bfd599660d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -67,6 +67,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; let expr = use_trivial_constructor( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index ffab58509b1..f8c75bdb0de 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -224,7 +224,6 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< syntax.text_range(), |builder| { let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info); - builder.replace_ast( match call_info.node { ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it), @@ -363,16 +362,22 @@ fn inline( .collect(); if function.self_param(sema.db).is_some() { - let this = || make::name_ref("this").syntax().clone_for_update().first_token().unwrap(); + let this = || { + make::name_ref("this") + .syntax() + .clone_for_update() + .first_token() + .expect("NameRef should have had a token.") + }; if let Some(self_local) = params[0].2.as_local(sema.db) { usages_for_locals(self_local) .filter_map(|FileReference { name, range, .. }| match name { ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), _ => None, }) - .for_each(|it| { - ted::replace(it, &this()); - }) + .for_each(|usage| { + ted::replace(usage, &this()); + }); } } @@ -470,7 +475,9 @@ fn inline( } } else if let Some(stmt_list) = body.stmt_list() { ted::insert_all( - ted::Position::after(stmt_list.l_curly_token().unwrap()), + ted::Position::after( + stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing."), + ), let_stmts.into_iter().map(|stmt| stmt.syntax().clone().into()).collect(), ); } @@ -481,8 +488,12 @@ fn inline( }; body.reindent_to(original_indentation); + let no_stmts = body.statements().next().is_none(); match body.tail_expr() { - Some(expr) if !is_async_fn && body.statements().next().is_none() => expr, + Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { + make::expr_paren(expr).clone_for_update() + } + Some(expr) if !is_async_fn && no_stmts => expr, _ => match node .syntax() .parent() @@ -1474,4 +1485,29 @@ fn main() { "#, ); } + + #[test] + fn inline_call_closure_body() { + check_assist( + inline_call, + r#" +fn f() -> impl Fn() -> i32 { + || 2 +} + +fn main() { + let _ = $0f()(); +} +"#, + r#" +fn f() -> impl Fn() -> i32 { + || 2 +} + +fn main() { + let _ = (|| 2)(); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index e69d1a29677..49dcde75d2b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -96,8 +96,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) ); let parent = matches!( usage_parent, - ast::Expr::CallExpr(_) - | ast::Expr::TupleExpr(_) + ast::Expr::TupleExpr(_) | ast::Expr::ArrayExpr(_) | ast::Expr::ParenExpr(_) | ast::Expr::ForExpr(_) @@ -952,4 +951,22 @@ fn f() { "#, ); } + + #[test] + fn test_inline_closure() { + check_assist( + inline_local_variable, + r#" +fn main() { + let $0f = || 2; + let _ = f(); +} +"#, + r#" +fn main() { + let _ = (|| 2)(); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs index 663df266b6f..965e4aa786e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs @@ -52,9 +52,13 @@ pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) == FamousDefs(sema, scope.krate()).core_convert_Into()? { let type_call = sema.type_of_expr(&method_call.clone().into())?; - let type_call_disp = - type_call.adjusted().display_source_code(db, scope.module().into(), true).ok()?; + let adjusted_tc = type_call.adjusted(); + if adjusted_tc.contains_unknown() { + return None; + } + + let sc = adjusted_tc.display_source_code(db, scope.module().into(), true).ok()?; acc.add( AssistId("into_to_qualified_from", AssistKind::Generate), "Convert `into` to fully qualified `from`", @@ -62,7 +66,11 @@ pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) |edit| { edit.replace( method_call.syntax().text_range(), - format!("{}::from({})", type_call_disp, receiver), + if sc.chars().all(|c| c.is_alphanumeric() || c == ':') { + format!("{}::from({})", sc, receiver) + } else { + format!("<{}>::from({})", sc, receiver) + }, ); }, ); @@ -202,4 +210,64 @@ fn main() -> () { }"#, ) } + + #[test] + fn preceding_type_qualifier() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +impl From<(i32,i32)> for [i32;2] { + fn from(value: (i32,i32)) -> Self { + [value.0, value.1] + } +} + +fn tuple_to_array() -> [i32; 2] { + (0,1).in$0to() +}"#, + r#" +impl From<(i32,i32)> for [i32;2] { + fn from(value: (i32,i32)) -> Self { + [value.0, value.1] + } +} + +fn tuple_to_array() -> [i32; 2] { + <[i32; 2]>::from((0,1)) +}"#, + ) + } + + #[test] + fn type_with_gens() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +struct StructA<Gen>(Gen); + +impl From<i32> for StructA<i32> { + fn from(value: i32) -> Self { + StructA(value + 1) + } +} + +fn main() -> () { + let a: StructA<i32> = 3.in$0to(); +}"#, + r#" +struct StructA<Gen>(Gen); + +impl From<i32> for StructA<i32> { + fn from(value: i32) -> Self { + StructA(value + 1) + } +} + +fn main() -> () { + let a: StructA<i32> = <StructA<i32>>::from(3); +}"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 4bf974a5655..ff65aac82e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -48,6 +48,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 239149dc411..fde46db3058 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -37,8 +37,11 @@ use crate::{ // ``` pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; - let mut proposed_imports = - import_assets.search_for_relative_paths(&ctx.sema, ctx.config.prefer_no_std); + let mut proposed_imports = import_assets.search_for_relative_paths( + &ctx.sema, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ); if proposed_imports.is_empty() { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index ac45581b7b4..69a4e748b7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -82,7 +82,12 @@ pub(crate) fn replace_derive_with_manual_impl( }) .flat_map(|trait_| { current_module - .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.prefer_no_std) + .find_use_path( + ctx.sema.db, + hir::ModuleDef::Trait(trait_), + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .as_ref() .map(mod_path_to_ast) .zip(Some(trait_)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs new file mode 100644 index 00000000000..b1daaea1ed1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -0,0 +1,172 @@ +use syntax::ast::{self, AstNode}; + +use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; + +// Assist: replace_is_some_with_if_let_some +// +// Replace `if x.is_some()` with `if let Some(_tmp) = x` or `if x.is_ok()` with `if let Ok(_tmp) = x`. +// +// ``` +// fn main() { +// let x = Some(1); +// if x.is_som$0e() {} +// } +// ``` +// -> +// ``` +// fn main() { +// let x = Some(1); +// if let Some(${0:x}) = x {} +// } +// ``` +pub(crate) fn replace_is_method_with_if_let_method( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + let if_expr = ctx.find_node_at_offset::<ast::IfExpr>()?; + + let cond = if_expr.condition()?; + let call_expr = match cond { + ast::Expr::MethodCallExpr(call) => call, + _ => return None, + }; + + let name_ref = call_expr.name_ref()?; + match name_ref.text().as_str() { + "is_some" | "is_ok" => { + let receiver = call_expr.receiver()?; + + let var_name = if let ast::Expr::PathExpr(path_expr) = receiver.clone() { + path_expr.path()?.to_string() + } else { + suggest_name::for_variable(&receiver, &ctx.sema) + }; + + let target = call_expr.syntax().text_range(); + + let (assist_id, message, text) = if name_ref.text() == "is_some" { + ("replace_is_some_with_if_let_some", "Replace `is_some` with `if let Some`", "Some") + } else { + ("replace_is_ok_with_if_let_ok", "Replace `is_ok` with `if let Ok`", "Ok") + }; + + acc.add(AssistId(assist_id, AssistKind::RefactorRewrite), message, target, |edit| { + let var_name = format!("${{0:{}}}", var_name); + let replacement = format!("let {}({}) = {}", text, var_name, receiver); + edit.replace(target, replacement); + }) + } + _ => return None, + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::replace_is_method_with_if_let_method; + + #[test] + fn replace_is_some_with_if_let_some_works() { + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_som$0e() {} +} +"#, + r#" +fn main() { + let x = Some(1); + if let Some(${0:x}) = x {} +} +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn test() -> Option<i32> { + Some(1) +} +fn main() { + if test().is_som$0e() {} +} +"#, + r#" +fn test() -> Option<i32> { + Some(1) +} +fn main() { + if let Some(${0:test}) = test() {} +} +"#, + ); + } + + #[test] + fn replace_is_some_with_if_let_some_not_applicable() { + check_assist_not_applicable( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Some(1); + if x.is_non$0e() {} +} +"#, + ); + } + + #[test] + fn replace_is_ok_with_if_let_ok_works() { + check_assist( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Ok(1); + if x.is_o$0k() {} +} +"#, + r#" +fn main() { + let x = Ok(1); + if let Ok(${0:x}) = x {} +} +"#, + ); + + check_assist( + replace_is_method_with_if_let_method, + r#" +fn test() -> Result<i32> { + Ok(1) +} +fn main() { + if test().is_o$0k() {} +} +"#, + r#" +fn test() -> Result<i32> { + Ok(1) +} +fn main() { + if let Ok(${0:test}) = test() {} +} +"#, + ); + } + + #[test] + fn replace_is_ok_with_if_let_ok_not_applicable() { + check_assist_not_applicable( + replace_is_method_with_if_let_method, + r#" +fn main() { + let x = Ok(1); + if x.is_e$0rr() {} +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index dbbc56958f1..f03eb6118a5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -68,6 +68,7 @@ pub(crate) fn replace_qualified_name_with_use( module, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) }) .flatten(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs index dac216b69b7..52df30d9623 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs @@ -36,29 +36,25 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let old_parent_range = use_.syntax().parent()?.text_range(); let new_parent = use_.syntax().parent()?; + // If possible, explain what is going to be done. + let label = match tree.path().and_then(|path| path.first_segment()) { + Some(name) => format!("Unmerge use of `{name}`"), + None => "Unmerge use".into(), + }; + let target = tree.syntax().text_range(); - acc.add( - AssistId("unmerge_use", AssistKind::RefactorRewrite), - "Unmerge use", - target, - |builder| { - let new_use = make::use_( - use_.visibility(), - make::use_tree( - path, - tree.use_tree_list(), - tree.rename(), - tree.star_token().is_some(), - ), - ) - .clone_for_update(); - - tree.remove(); - ted::insert(Position::after(use_.syntax()), new_use.syntax()); - - builder.replace(old_parent_range, new_parent.to_string()); - }, - ) + acc.add(AssistId("unmerge_use", AssistKind::RefactorRewrite), label, target, |builder| { + let new_use = make::use_( + use_.visibility(), + make::use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()), + ) + .clone_for_update(); + + tree.remove(); + ted::insert(Position::after(use_.syntax()), new_use.syntax()); + + builder.replace(old_parent_range, new_parent.to_string()); + }) } fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index e9d4e270cdc..0bf1782a489 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -1,3 +1,4 @@ +use ide_db::imports::insert_use::ImportScope; use syntax::{ ast::{self, make, AstNode, HasArgList}, TextRange, @@ -17,6 +18,8 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // -> // ``` +// use std::ops::Add; +// // fn main() { // 1.add(2); // } @@ -38,7 +41,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) let first_arg = args_iter.next()?; let second_arg = args_iter.next(); - _ = path.qualifier()?; + let qualifier = path.qualifier()?; let method_name = path.segment()?.name_ref()?; let res = ctx.sema.resolve_path(&path)?; @@ -76,10 +79,51 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) edit.insert(close, ")"); } edit.replace(replace_comma, format!(".{method_name}(")); + add_import(qualifier, ctx, edit); }, ) } +fn add_import( + qualifier: ast::Path, + ctx: &AssistContext<'_>, + edit: &mut ide_db::source_change::SourceChangeBuilder, +) { + if let Some(path_segment) = qualifier.segment() { + // for `<i32 as std::ops::Add>` + let path_type = path_segment.syntax().children().filter_map(ast::PathType::cast).last(); + let import = match path_type { + Some(it) => { + if let Some(path) = it.path() { + path + } else { + return; + } + } + None => qualifier, + }; + + // in case for `<_>` + if import.coloncolon_token().is_none() { + return; + } + + let scope = ide_db::imports::insert_use::ImportScope::find_insert_use_container( + import.syntax(), + &ctx.sema, + ); + + if let Some(scope) = scope { + let scope = match scope { + ImportScope::File(it) => ImportScope::File(edit.make_mut(it)), + ImportScope::Module(it) => ImportScope::Module(edit.make_mut(it)), + ImportScope::Block(it) => ImportScope::Block(edit.make_mut(it)), + }; + ide_db::imports::insert_use::insert_use(&scope, import, &ctx.config.insert_use); + } + } +} + fn needs_parens_as_receiver(expr: &ast::Expr) -> bool { // Make `(expr).dummy()` let dummy_call = make::expr_method_call( @@ -127,6 +171,8 @@ fn f() { S.f(S); }"#, //- minicore: add fn f() { <u32 as core::ops::Add>::$0add(2, 2); }"#, r#" +use core::ops::Add; + fn f() { 2.add(2); }"#, ); @@ -136,6 +182,8 @@ fn f() { 2.add(2); }"#, //- minicore: add fn f() { core::ops::Add::$0add(2, 2); }"#, r#" +use core::ops::Add; + fn f() { 2.add(2); }"#, ); @@ -179,6 +227,8 @@ impl core::ops::Deref for S { } fn f() { core::ops::Deref::$0deref(&S); }"#, r#" +use core::ops::Deref; + struct S; impl core::ops::Deref for S { type Target = S; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs index f235b554e61..03e6dfebebf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs @@ -123,10 +123,8 @@ fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) { for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) } } - Expr::ReturnExpr(ret_expr) => { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, &mut |e| tail_cb_impl(acc, e)); - } + Expr::ReturnExpr(_) => { + // all return expressions have already been handled by the walk loop } e => acc.push(e.clone()), } @@ -801,6 +799,24 @@ fn foo() -> i32 { } #[test] + fn wrap_return_in_tail_position() { + check_assist( + unwrap_result_return_type, + r#" +//- minicore: result +fn foo(num: i32) -> $0Result<i32, String> { + return Ok(num) +} +"#, + r#" +fn foo(num: i32) -> i32 { + return num +} +"#, + ); + } + + #[test] fn unwrap_result_return_type_simple_with_closure() { check_assist( unwrap_result_return_type, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index 61e9bcdcc51..b68ed00f772 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -98,10 +98,8 @@ fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) { for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) } } - Expr::ReturnExpr(ret_expr) => { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, &mut |e| tail_cb_impl(acc, e)); - } + Expr::ReturnExpr(_) => { + // all return expressions have already been handled by the walk loop } e => acc.push(e.clone()), } @@ -733,6 +731,24 @@ fn foo() -> Result<i32, ${0:_}> { } #[test] + fn wrap_return_in_tail_position() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +fn foo(num: i32) -> $0i32 { + return num +} +"#, + r#" +fn foo(num: i32) -> Result<i32, ${0:_}> { + return Ok(num) +} +"#, + ); + } + + #[test] fn wrap_return_type_in_result_simple_with_closure() { check_assist( wrap_return_type_in_result, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 6f973ab53ee..e6f03214ed3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -115,6 +115,7 @@ mod handlers { mod apply_demorgan; mod auto_import; mod bind_unused_param; + mod bool_to_enum; mod change_visibility; mod convert_bool_then; mod convert_comment_block; @@ -124,6 +125,7 @@ mod handlers { mod convert_let_else_to_match; mod convert_match_to_let_else; mod convert_nested_function_to_closure; + mod convert_tuple_return_type_to_struct; mod convert_tuple_struct_to_named_struct; mod convert_named_struct_to_tuple_struct; mod convert_to_guarded_return; @@ -158,6 +160,7 @@ mod handlers { mod generate_getter_or_setter; mod generate_impl; mod generate_is_empty_from_len; + mod generate_mut_trait_impl; mod generate_new; mod generate_delegate_methods; mod generate_trait_from_impl; @@ -193,6 +196,7 @@ mod handlers { mod replace_try_expr_with_match; mod replace_derive_with_manual_impl; mod replace_if_let_with_match; + mod replace_is_method_with_if_let_method; mod replace_method_eager_lazy; mod replace_arith_op; mod introduce_named_generic; @@ -225,8 +229,10 @@ mod handlers { add_return_type::add_return_type, add_turbo_fish::add_turbo_fish, apply_demorgan::apply_demorgan, + apply_demorgan::apply_demorgan_iterator, auto_import::auto_import, bind_unused_param::bind_unused_param, + bool_to_enum::bool_to_enum, change_visibility::change_visibility, convert_bool_then::convert_bool_then_to_if, convert_bool_then::convert_if_to_bool_then, @@ -237,6 +243,7 @@ mod handlers { convert_iter_for_each_to_for::convert_for_loop_with_for_each, convert_let_else_to_match::convert_let_else_to_match, convert_match_to_let_else::convert_match_to_let_else, + convert_tuple_return_type_to_struct::convert_tuple_return_type_to_struct, convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct, convert_nested_function_to_closure::convert_nested_function_to_closure, convert_to_guarded_return::convert_to_guarded_return, @@ -268,6 +275,7 @@ mod handlers { generate_function::generate_function, generate_impl::generate_impl, generate_impl::generate_trait_impl, + generate_mut_trait_impl::generate_mut_trait_impl, generate_is_empty_from_len::generate_is_empty_from_len, generate_new::generate_new, generate_trait_from_impl::generate_trait_from_impl, @@ -308,6 +316,7 @@ mod handlers { replace_derive_with_manual_impl::replace_derive_with_manual_impl, replace_if_let_with_match::replace_if_let_with_match, replace_if_let_with_match::replace_match_with_if_let, + replace_is_method_with_if_let_method::replace_is_method_with_if_let_method, replace_let_with_if_let::replace_let_with_if_let, replace_method_eager_lazy::replace_with_eager_method, replace_method_eager_lazy::replace_with_lazy_method, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index cc3e251a8f2..25b3d6d9da9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + prefer_prelude: true, assist_emit_must_use: false, }; @@ -44,6 +45,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + prefer_prelude: true, assist_emit_must_use: false, }; @@ -98,6 +100,11 @@ pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) { check(assist, ra_fixture, ExpectedResult::NotApplicable, None); } +#[track_caller] +pub(crate) fn check_assist_not_applicable_by_label(assist: Handler, ra_fixture: &str, label: &str) { + check(assist, ra_fixture, ExpectedResult::NotApplicable, Some(label)); +} + /// Check assist in unresolved state. Useful to check assists for lazy computation. #[track_caller] pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index dfaa53449f4..da5822bba9c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -245,6 +245,30 @@ fn main() { } #[test] +fn doctest_apply_demorgan_iterator() { + check_doc_test( + "apply_demorgan_iterator", + r#####" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + if !arr.into_iter().$0any(|num| num == 4) { + println!("foo"); + } +} +"#####, + r#####" +fn main() { + let arr = [1, 2, 3]; + if arr.into_iter().all(|num| num != 4) { + println!("foo"); + } +} +"#####, + ) +} + +#[test] fn doctest_auto_import() { check_doc_test( "auto_import", @@ -281,6 +305,34 @@ fn some_function(x: i32) { } #[test] +fn doctest_bool_to_enum() { + check_doc_test( + "bool_to_enum", + r#####" +fn main() { + let $0bool = true; + + if bool { + println!("foo"); + } +} +"#####, + r#####" +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let bool = Bool::True; + + if bool == Bool::True { + println!("foo"); + } +} +"#####, + ) +} + +#[test] fn doctest_change_visibility() { check_doc_test( "change_visibility", @@ -559,6 +611,33 @@ fn main() { } #[test] +fn doctest_convert_tuple_return_type_to_struct() { + check_doc_test( + "convert_tuple_return_type_to_struct", + r#####" +fn bar() { + let (a, b, c) = foo(); +} + +fn foo() -> ($0u32, u32, u32) { + (1, 2, 3) +} +"#####, + r#####" +fn bar() { + let FooResult(a, b, c) = foo(); +} + +struct FooResult(u32, u32, u32); + +fn foo() -> FooResult { + FooResult(1, 2, 3) +} +"#####, + ) +} + +#[test] fn doctest_convert_tuple_struct_to_named_struct() { check_doc_test( "convert_tuple_struct_to_named_struct", @@ -1460,6 +1539,42 @@ impl MyStruct { } #[test] +fn doctest_generate_mut_trait_impl() { + check_doc_test( + "generate_mut_trait_impl", + r#####" +//- minicore: index +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl<T> core::ops::Index$0<Axis> for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#####, + r#####" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +$0impl<T> core::ops::IndexMut<Axis> for [T; 3] { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + &self[index as usize] + } +} + +impl<T> core::ops::Index<Axis> for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#####, + ) +} + +#[test] fn doctest_generate_new() { check_doc_test( "generate_new", @@ -2480,6 +2595,25 @@ fn handle(action: Action) { } #[test] +fn doctest_replace_is_some_with_if_let_some() { + check_doc_test( + "replace_is_some_with_if_let_some", + r#####" +fn main() { + let x = Some(1); + if x.is_som$0e() {} +} +"#####, + r#####" +fn main() { + let x = Some(1); + if let Some(${0:x}) = x {} +} +"#####, + ) +} + +#[test] fn doctest_replace_let_with_if_let() { check_doc_test( "replace_let_with_if_let", @@ -2850,6 +2984,8 @@ fn main() { mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for i32 {} } } "#####, r#####" +use std::ops::Add; + fn main() { 1.add(2); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index a262570d94e..f51e99a914e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -106,8 +106,18 @@ pub fn filter_assoc_items( .iter() .copied() .filter(|assoc_item| { - !(ignore_items == IgnoreAssocItems::DocHiddenAttrPresent - && assoc_item.attrs(sema.db).has_doc_hidden()) + if ignore_items == IgnoreAssocItems::DocHiddenAttrPresent + && assoc_item.attrs(sema.db).has_doc_hidden() + { + if let hir::AssocItem::Function(f) = assoc_item { + if !f.has_body(sema.db) { + return true; + } + } + return false; + } + + return true; }) // Note: This throws away items with no source. .filter_map(|assoc_item| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml index 092fb303668..60f90a41b96 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true once_cell = "1.17.0" smallvec.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index f60ac150164..7d38c638a8e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -626,6 +626,7 @@ fn enum_variants_with_paths( ctx.db, hir::ModuleDef::from(variant), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 9daa6984c3e..d3c817d4b43 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -175,6 +175,7 @@ pub(crate) fn complete_expr_path( ctx.db, hir::ModuleDef::from(strukt), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .filter(|it| it.len() > 1); @@ -197,6 +198,7 @@ pub(crate) fn complete_expr_path( ctx.db, hir::ModuleDef::from(un), ctx.config.prefer_no_std, + ctx.config.prefer_prelude, ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 39c1b7f7b3f..d74d3b264ae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -13,10 +13,9 @@ use crate::{ TypeLocation, }, render::{render_resolution_with_import, render_resolution_with_import_pat, RenderContext}, + Completions, }; -use super::Completions; - // Feature: Completion With Autoimport // // When completing names in the current scope, proposes additional imports from other modules or crates, @@ -258,7 +257,12 @@ fn import_on_the_fly( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(ns_filter) .filter(|import| { @@ -300,7 +304,12 @@ fn import_on_the_fly_pat_( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(ns_filter) .filter(|import| { @@ -337,7 +346,12 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); import_assets - .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .search_for_imports( + &ctx.sema, + ctx.config.insert_use.prefix_kind, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) .into_iter() .filter(|import| { !ctx.is_item_hidden(&import.item_to_import) @@ -377,9 +391,12 @@ fn import_assets_for_path( &ctx.sema, ctx.token.parent()?, )?; - if fuzzy_name_length < 3 { - cov_mark::hit!(flyimport_exact_on_short_path); - assets_for_path.path_fuzzy_name_to_exact(false); + if fuzzy_name_length == 0 { + // nothing matches the empty string exactly, but we still compute assoc items in this case + assets_for_path.path_fuzzy_name_to_exact(); + } else if fuzzy_name_length < 3 { + cov_mark::hit!(flyimport_prefix_on_short_path); + assets_for_path.path_fuzzy_name_to_prefix(); } Some(assets_for_path) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 8f6a97e1e09..ed5ddde8fbf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -14,10 +14,12 @@ pub struct CompletionConfig { pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, pub enable_private_editable: bool, + pub full_function_signatures: bool, pub callable: Option<CallableSnippets>, pub snippet_cap: Option<SnippetCap>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub snippets: Vec<Snippet>, pub limit: Option<usize>, } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index c45cc8d7b37..ed74ef7b667 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -89,7 +89,7 @@ impl fmt::Debug for CompletionItem { let mut s = f.debug_struct("CompletionItem"); s.field("label", &self.label).field("source_range", &self.source_range); if self.text_edit.len() == 1 { - let atom = &self.text_edit.iter().next().unwrap(); + let atom = self.text_edit.iter().next().unwrap(); s.field("delete", &atom.delete); s.field("insert", &atom.insert); } else { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 2eaa42040a0..aaf7cd7843a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -169,6 +169,28 @@ pub fn completions( return Some(completions.into()); } + // when the user types a bare `_` (that is it does not belong to an identifier) + // the user might just wanted to type a `_` for type inference or pattern discarding + // so try to suppress completions in those cases + if trigger_character == Some('_') && ctx.original_token.kind() == syntax::SyntaxKind::UNDERSCORE + { + if let CompletionAnalysis::NameRef(NameRefContext { + kind: + NameRefKind::Path( + path_ctx @ PathCompletionCtx { + kind: PathKind::Type { .. } | PathKind::Pat { .. }, + .. + }, + ), + .. + }) = analysis + { + if path_ctx.is_trivial_path() { + return None; + } + } + } + { let acc = &mut completions; @@ -241,6 +263,7 @@ pub fn resolve_completion_edits( candidate, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) }) .find(|mod_path| mod_path.display(db).to_string() == full_import_path); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 8afce8db5ea..dfae715afe3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -98,9 +98,14 @@ fn render( _ => (), } + let detail = if ctx.completion.config.full_function_signatures { + detail_full(db, func) + } else { + detail(db, func) + }; item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) - .detail(detail(db, func)) + .detail(detail) .lookup_by(name.unescaped().to_smol_str()); match ctx.completion.config.snippet_cap { @@ -263,6 +268,21 @@ fn detail(db: &dyn HirDatabase, func: hir::Function) -> String { detail } +fn detail_full(db: &dyn HirDatabase, func: hir::Function) -> String { + let signature = format!("{}", func.display(db)); + let mut detail = String::with_capacity(signature.len()); + + for segment in signature.split_whitespace() { + if !detail.is_empty() { + detail.push(' '); + } + + detail.push_str(segment); + } + + detail +} + fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String { if let Some(self_param) = func.self_param(db) { let assoc_fn_params = func.assoc_fn_params(db); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 343719c5369..50618296ee4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -179,6 +179,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<V item, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item, None))) }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 2464e8d5f81..9db8e972dd4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -64,9 +64,11 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { enable_imports_on_the_fly: true, enable_self_on_the_fly: true, enable_private_editable: false, + full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), prefer_no_std: false, + prefer_prelude: true, insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, prefix_kind: PrefixKind::Plain, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 4cdfd546f6a..21f693d79f1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -116,19 +116,47 @@ fn main() { } #[test] -fn short_paths_are_ignored() { - cov_mark::check!(flyimport_exact_on_short_path); +fn short_paths_are_prefix_matched() { + cov_mark::check!(flyimport_prefix_on_short_path); check( r#" //- /lib.rs crate:dep -pub struct Bar; +pub struct Barc; pub struct Rcar; pub struct Rc; +pub const RC: () = (); pub mod some_module { pub struct Bar; pub struct Rcar; pub struct Rc; + pub const RC: () = (); +} + +//- /main.rs crate:main deps:dep +fn main() { + Rc$0 +} +"#, + expect![[r#" + st Rc (use dep::Rc) + st Rcar (use dep::Rcar) + st Rc (use dep::some_module::Rc) + st Rcar (use dep::some_module::Rcar) + "#]], + ); + check( + r#" +//- /lib.rs crate:dep +pub struct Barc; +pub struct Rcar; +pub struct Rc; +pub const RC: () = (); +pub mod some_module { + pub struct Bar; + pub struct Rcar; + pub struct Rc; + pub const RC: () = (); } //- /main.rs crate:main deps:dep @@ -137,8 +165,36 @@ fn main() { } "#, expect![[r#" + ct RC (use dep::RC) st Rc (use dep::Rc) + st Rcar (use dep::Rcar) + ct RC (use dep::some_module::RC) st Rc (use dep::some_module::Rc) + st Rcar (use dep::some_module::Rcar) + "#]], + ); + check( + r#" +//- /lib.rs crate:dep +pub struct Barc; +pub struct Rcar; +pub struct Rc; +pub const RC: () = (); +pub mod some_module { + pub struct Bar; + pub struct Rcar; + pub struct Rc; + pub const RC: () = (); +} + +//- /main.rs crate:main deps:dep +fn main() { + RC$0 +} +"#, + expect![[r#" + ct RC (use dep::RC) + ct RC (use dep::some_module::RC) "#]], ); } @@ -841,8 +897,8 @@ fn main() { TES$0 }"#, expect![[r#" - ct TEST_CONST (use foo::TEST_CONST) - "#]], + ct TEST_CONST (use foo::TEST_CONST) + "#]], ); check( @@ -858,9 +914,9 @@ fn main() { tes$0 }"#, expect![[r#" - ct TEST_CONST (use foo::TEST_CONST) - fn test_function() (use foo::test_function) fn() -> i32 - "#]], + ct TEST_CONST (use foo::TEST_CONST) + fn test_function() (use foo::test_function) fn() -> i32 + "#]], ); check( @@ -873,9 +929,9 @@ mod foo { } fn main() { - Te$0 + Tes$0 }"#, - expect![[]], + expect![""], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index e80a289049f..d3dbd7cc227 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -2,10 +2,15 @@ use expect_test::{expect, Expect}; -use crate::tests::{ - check_edit, completion_list, completion_list_no_kw, completion_list_with_trigger_character, +use crate::{ + tests::{ + check_edit, completion_list, completion_list_no_kw, completion_list_with_trigger_character, + }, + CompletionItemKind, }; +use super::{do_completion_with_config, TEST_CONFIG}; + fn check_no_kw(ra_fixture: &str, expect: Expect) { let actual = completion_list_no_kw(ra_fixture); expect.assert_eq(&actual) @@ -1303,3 +1308,176 @@ struct Foo<T: PartialOrd "#, ); } + +fn check_signatures(src: &str, kind: CompletionItemKind, reduced: Expect, full: Expect) { + const FULL_SIGNATURES_CONFIG: crate::CompletionConfig = { + let mut x = TEST_CONFIG; + x.full_function_signatures = true; + x + }; + + // reduced signature + let completion = do_completion_with_config(TEST_CONFIG, src, kind); + assert!(completion[0].detail.is_some()); + reduced.assert_eq(completion[0].detail.as_ref().unwrap()); + + // full signature + let completion = do_completion_with_config(FULL_SIGNATURES_CONFIG, src, kind); + assert!(completion[0].detail.is_some()); + full.assert_eq(completion[0].detail.as_ref().unwrap()); +} + +#[test] +fn respects_full_function_signatures() { + check_signatures( + r#" +pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone, { 0u8 } +fn main() { fo$0 } +"#, + CompletionItemKind::SymbolKind(ide_db::SymbolKind::Function), + expect!("fn(&mut T) -> u8"), + expect!("pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone,"), + ); + + check_signatures( + r#" +struct Foo; +struct Bar; +impl Bar { + pub const fn baz(x: Foo) -> ! { loop {} }; +} + +fn main() { Bar::b$0 } +"#, + CompletionItemKind::SymbolKind(ide_db::SymbolKind::Function), + expect!("const fn(Foo) -> !"), + expect!("pub const fn baz(x: Foo) -> !"), + ); + + check_signatures( + r#" +struct Foo; +struct Bar; +impl Bar { + pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> ! { loop {} }; +} + +fn main() { + let mut bar = Bar; + bar.b$0 +} +"#, + CompletionItemKind::Method, + expect!("const fn(&'foo mut self, &Foo) -> !"), + expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), + ); +} + +#[test] +fn skips_underscore() { + check_with_trigger_character( + r#" +fn foo(_$0) { } +"#, + Some('_'), + expect![[r#""#]], + ); + check_with_trigger_character( + r#" +fn foo(_: _$0) { } +"#, + Some('_'), + expect![[r#""#]], + ); + check_with_trigger_character( + r#" +fn foo<T>() { + foo::<_$0>(); +} +"#, + Some('_'), + expect![[r#""#]], + ); + // underscore expressions are fine, they are invalid so the user definitely meant to type an + // underscored name here + check_with_trigger_character( + r#" +fn foo() { + _$0 +} +"#, + Some('_'), + expect![[r#" + fn foo() fn() + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn no_skip_underscore_ident() { + check_with_trigger_character( + r#" +fn foo(a_$0) { } +"#, + Some('_'), + expect![[r#" + kw mut + kw ref + "#]], + ); + check_with_trigger_character( + r#" +fn foo(_: a_$0) { } +"#, + Some('_'), + expect![[r#" + bt u32 + kw crate:: + kw self:: + "#]], + ); + check_with_trigger_character( + r#" +fn foo<T>() { + foo::<a_$0>(); +} +"#, + Some('_'), + expect![[r#" + tp T + bt u32 + kw crate:: + kw self:: + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index faec7420681..4a2e770f193 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -13,16 +13,16 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -tracing = "0.1.35" -rayon = "1.6.1" +tracing.workspace = true +rayon.workspace = true fst = { version = "0.4.7", default-features = false } rustc-hash = "1.1.0" once_cell = "1.17.0" -either = "1.7.0" -itertools = "0.10.5" +either.workspace = true +itertools.workspace = true arrayvec = "0.7.2" -indexmap = "2.0.0" -memchr = "2.5.0" +indexmap.workspace = true +memchr = "2.6.4" triomphe.workspace = true nohash-hasher.workspace = true @@ -43,7 +43,7 @@ line-index.workspace = true [dev-dependencies] expect-test = "1.4.0" oorandom = "11.1.3" -xshell = "0.2.2" +xshell.workspace = true # local deps test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 4ce80532e8e..ef72fc3861a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -161,8 +161,8 @@ impl IdentClass { ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator), ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator), ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator), - ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema,&prefix_expr).map(IdentClass::Operator), - ast::TryExpr(try_expr) => OperatorClass::classify_try(sema,&try_expr).map(IdentClass::Operator), + ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema, &prefix_expr).map(IdentClass::Operator), + ast::TryExpr(try_expr) => OperatorClass::classify_try(sema, &try_expr).map(IdentClass::Operator), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index b63dde2c21e..722517a7677 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -54,6 +54,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_Index(&self) -> Option<Trait> { + self.find_trait("core:ops:Index") + } + pub fn core_option_Option(&self) -> Option<Enum> { self.find_enum("core:option:Option") } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 57563a17483..1cb6ff8627a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -9,12 +9,18 @@ pub struct LintGroup { pub lint: Lint, pub children: &'static [&'static str], } + pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "absolute_paths_not_starting_with_crate", description: r##"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"##, }, Lint { label: "ambiguous_associated_items", description: r##"ambiguous associated items"## }, + Lint { + label: "ambiguous_glob_imports", + description: r##"detects certain glob imports that require reporting an ambiguity error"##, + }, + Lint { label: "ambiguous_glob_reexports", description: r##"ambiguous glob re-exports"## }, Lint { label: "anonymous_parameters", description: r##"detects anonymous parameters"## }, Lint { label: "arithmetic_overflow", description: r##"arithmetic operation overflows"## }, Lint { @@ -25,6 +31,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "asm_sub_register", description: r##"using only a subset of a register for inline asm inputs"##, }, + Lint { + label: "async_fn_in_trait", + description: r##"use of `async fn` in definition of a publicly-reachable trait"##, + }, Lint { label: "bad_asm_style", description: r##"incorrect use of inline assembly"## }, Lint { label: "bare_trait_objects", @@ -40,6 +50,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"`break` expression with label and unlabeled loop as value expression"##, }, Lint { + label: "byte_slice_in_packed_struct_with_derive", + description: r##"`[u8]` or `str` used in a packed struct with `derive`"##, + }, + Lint { label: "cenum_impl_drop_cast", description: r##"a C-like enum implementing Drop is cast"##, }, @@ -52,6 +66,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"distinct impls distinguished only by the leak-check code"##, }, Lint { + label: "coinductive_overlap_in_coherence", + description: r##"impls that are not considered to overlap may be considered to overlap in the future"##, + }, + Lint { label: "conflicting_repr_hints", description: r##"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"##, }, @@ -60,10 +78,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects visually confusable pairs between identifiers"##, }, Lint { - label: "const_err", - description: r##"constant evaluation encountered erroneous expression"##, - }, - Lint { label: "const_evaluatable_unchecked", description: r##"detects a generic constant is used in a type without a emitting a warning"##, }, @@ -71,13 +85,25 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "const_item_mutation", description: r##"detects attempts to mutate a `const` item"##, }, + Lint { + label: "const_patterns_without_partial_eq", + description: r##"constant in pattern does not implement `PartialEq`"##, + }, Lint { label: "dead_code", description: r##"detect unused, unexported items"## }, Lint { label: "deprecated", description: r##"detects use of deprecated items"## }, Lint { + label: "deprecated_cfg_attr_crate_type_name", + description: r##"detects usage of `#![cfg_attr(..., crate_type/crate_name = "...")]`"##, + }, + Lint { label: "deprecated_in_future", description: r##"detects use of items that will be deprecated in a future version"##, }, Lint { + label: "deprecated_where_clause_location", + description: r##"deprecated where clause location"##, + }, + Lint { label: "deref_into_dyn_supertrait", description: r##"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future"##, }, @@ -90,10 +116,23 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"bounds of the form `T: Drop` are most likely incorrect"##, }, Lint { + label: "dropping_copy_types", + description: r##"calls to `std::mem::drop` with a value that implements Copy"##, + }, + Lint { + label: "dropping_references", + description: r##"calls to `std::mem::drop` with a reference instead of an owned value"##, + }, + Lint { label: "duplicate_macro_attributes", description: r##"duplicated attribute"## }, + Lint { label: "dyn_drop", description: r##"trait objects of the form `dyn Drop` are useless"##, }, Lint { + label: "elided_lifetimes_in_associated_constant", + description: r##"elided lifetimes cannot be used in associated constants in impls"##, + }, + Lint { label: "elided_lifetimes_in_paths", description: r##"hidden lifetime parameters in types are deprecated"##, }, @@ -113,14 +152,38 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "exported_private_dependencies", description: r##"public interface leaks type from a private dependency"##, }, + Lint { + label: "ffi_unwind_calls", + description: r##"call to foreign functions or function pointers with FFI-unwind ABI"##, + }, + Lint { + label: "for_loops_over_fallibles", + description: r##"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"##, + }, Lint { label: "forbidden_lint_groups", description: r##"applying forbid to lint-groups"## }, Lint { + label: "forgetting_copy_types", + description: r##"calls to `std::mem::forget` with a value that implements Copy"##, + }, + Lint { + label: "forgetting_references", + description: r##"calls to `std::mem::forget` with a reference instead of an owned value"##, + }, + Lint { label: "function_item_references", description: r##"suggest casting to a function pointer when attempting to take references to function items"##, }, Lint { label: "future_incompatible", - description: r##"lint group for: forbidden-lint-groups, illegal-floating-point-literal-pattern, private-in-public, pub-use-of-private-extern-crate, invalid-type-param-default, const-err, unaligned-references, patterns-in-fns-without-body, missing-fragment-specifier, late-bound-lifetime-arguments, order-dependent-trait-objects, coherence-leak-check, unstable-name-collisions, where-clauses-object-safety, proc-macro-derive-resolution-fallback, macro-expanded-macro-exports-accessed-by-absolute-paths, ill-formed-attribute-input, conflicting-repr-hints, ambiguous-associated-items, mutable-borrow-reservation-conflict, indirect-structural-match, pointer-structural-match, nontrivial-structural-match, soft-unstable, cenum-impl-drop-cast, const-evaluatable-unchecked, uninhabited-static, unsupported-naked-functions, invalid-doc-attributes, semicolon-in-expressions-from-macros, legacy-derive-helpers, proc-macro-back-compat, unsupported-calling-conventions, deref-into-dyn-supertrait"##, + description: r##"lint group for: deref-into-dyn-supertrait, ambiguous-associated-items, ambiguous-glob-imports, byte-slice-in-packed-struct-with-derive, cenum-impl-drop-cast, coherence-leak-check, coinductive-overlap-in-coherence, conflicting-repr-hints, const-evaluatable-unchecked, const-patterns-without-partial-eq, deprecated-cfg-attr-crate-type-name, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, illegal-floating-point-literal-pattern, implied-bounds-entailment, indirect-structural-match, invalid-alignment, invalid-doc-attributes, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, nontrivial-structural-match, order-dependent-trait-objects, patterns-in-fns-without-body, pointer-structural-match, proc-macro-back-compat, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, semicolon-in-expressions-from-macros, soft-unstable, suspicious-auto-trait-impls, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, where-clauses-object-safety"##, + }, + Lint { + label: "fuzzy_provenance_casts", + description: r##"a fuzzy integer to pointer cast is used"##, + }, + Lint { + label: "hidden_glob_reexports", + description: r##"name introduced by a private item shadows a name introduced by a public glob re-export"##, }, Lint { label: "ill_formed_attribute_input", @@ -131,6 +194,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"floating-point literals cannot be used in patterns"##, }, Lint { + label: "implied_bounds_entailment", + description: r##"impl method assumes more implied bounds than its corresponding trait method"##, + }, + Lint { label: "improper_ctypes", description: r##"proper use of libc types in foreign modules"##, }, @@ -156,6 +223,14 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"##, }, Lint { + label: "internal_features", + description: r##"internal features are not supposed to be used"##, + }, + Lint { + label: "invalid_alignment", + description: r##"raw pointers must be aligned before dereferencing"##, + }, + Lint { label: "invalid_atomic_ordering", description: r##"usage of invalid atomic ordering in atomic operations and memory fences"##, }, @@ -164,6 +239,26 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects invalid `#[doc(...)]` attributes"##, }, Lint { + label: "invalid_from_utf8", + description: r##"using a non UTF-8 literal in `std::str::from_utf8`"##, + }, + Lint { + label: "invalid_from_utf8_unchecked", + description: r##"using a non UTF-8 literal in `std::str::from_utf8_unchecked`"##, + }, + Lint { + label: "invalid_macro_export_arguments", + description: r##""invalid_parameter" isn't a valid argument for `#[macro_export]`"##, + }, + Lint { + label: "invalid_nan_comparisons", + description: r##"detects invalid floating point NaN comparisons"##, + }, + Lint { + label: "invalid_reference_casting", + description: r##"casts of `&T` to `&mut T` without interior mutability"##, + }, + Lint { label: "invalid_type_param_default", description: r##"type parameter default erroneously allowed in invalid location"##, }, @@ -189,6 +284,26 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects derive helper attributes that are used before they are introduced"##, }, Lint { + label: "let_underscore", + description: r##"lint group for: let-underscore-drop, let-underscore-lock"##, + }, + Lint { + label: "let_underscore_drop", + description: r##"non-binding let on a type that implements `Drop`"##, + }, + Lint { + label: "let_underscore_lock", + description: r##"non-binding let on a synchronization lock"##, + }, + Lint { + label: "long_running_const_eval", + description: r##"detects long const eval operations"##, + }, + Lint { + label: "lossy_provenance_casts", + description: r##"a lossy pointer to integer cast is used"##, + }, + Lint { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r##"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"##, }, @@ -197,6 +312,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"##, }, Lint { + label: "map_unit_fn", + description: r##"`Iterator::map` call that discard the iterator's values"##, + }, + Lint { label: "meta_variable_misuse", description: r##"possible meta-variable misuse at macro definition"##, }, @@ -222,16 +341,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects Unicode scripts whose mixed script confusables codepoints are solely used"##, }, Lint { + label: "multiple_supertrait_upcastable", + description: r##"detect when an object-safe trait has multiple supertraits"##, + }, + Lint { label: "must_not_suspend", description: r##"use of a `#[must_not_suspend]` value across a yield point"##, }, Lint { - label: "mutable_borrow_reservation_conflict", - description: r##"reservation of a two-phased borrow conflicts with other shared borrows"##, + label: "mutable_transmutes", + description: r##"transmuting &T to &mut T is undefined behavior, even if the reference is unused"##, }, Lint { - label: "mutable_transmutes", - description: r##"mutating transmuted &mut T from &T may cause undefined behavior"##, + label: "named_arguments_used_positionally", + description: r##"named arguments in format used positionally"##, }, Lint { label: "named_asm_labels", description: r##"named labels in inline assembly"## }, Lint { @@ -277,6 +400,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects the use of well-known noop methods"##, }, Lint { + label: "opaque_hidden_inferred_bound", + description: r##"detects the use of nested `impl Trait` types in associated type bounds that are not general enough"##, + }, + Lint { label: "order_dependent_trait_objects", description: r##"trait-object types were treated as different depending on marker-trait order"##, }, @@ -295,8 +422,12 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"pointers are not structural-match"##, }, Lint { - label: "private_in_public", - description: r##"detect private items in public interfaces not caught by the old implementation"##, + label: "private_bounds", + description: r##"private type in secondary interface of an item"##, + }, + Lint { + label: "private_interfaces", + description: r##"private type in primary interface of an item"##, }, Lint { label: "proc_macro_back_compat", @@ -315,12 +446,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects unnecessary trailing semicolons"##, }, Lint { + label: "refining_impl_trait", + description: r##"impl trait in impl method signature does not match trait method signature"##, + }, + Lint { label: "renamed_and_removed_lints", description: r##"lints that have been renamed or removed"##, }, Lint { + label: "repr_transparent_external_private_fields", + description: r##"transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields"##, + }, + Lint { label: "rust_2018_compatibility", - description: r##"lint group for: keyword-idents, anonymous-parameters, tyvar-behind-raw-pointer, absolute-paths-not-starting-with-crate"##, + description: r##"lint group for: keyword-idents, anonymous-parameters, absolute-paths-not-starting-with-crate, tyvar-behind-raw-pointer"##, }, Lint { label: "rust_2018_idioms", @@ -328,7 +467,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "rust_2021_compatibility", - description: r##"lint group for: ellipsis-inclusive-range-patterns, bare-trait-objects, rust-2021-incompatible-closure-captures, rust-2021-incompatible-or-patterns, rust-2021-prelude-collisions, rust-2021-prefixes-incompatible-syntax, array-into-iter, non-fmt-panics"##, + description: r##"lint group for: ellipsis-inclusive-range-patterns, bare-trait-objects, rust-2021-incompatible-closure-captures, rust-2021-incompatible-or-patterns, rust-2021-prefixes-incompatible-syntax, rust-2021-prelude-collisions, array-into-iter, non-fmt-panics"##, }, Lint { label: "rust_2021_incompatible_closure_captures", @@ -359,14 +498,30 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"a feature gate that doesn't break dependent crates"##, }, Lint { + label: "special_module_name", + description: r##"module declarations for files with a special meaning"##, + }, + Lint { label: "stable_features", description: r##"stable features found in `#[feature]` directive"##, }, Lint { + label: "suspicious_auto_trait_impls", + description: r##"the rules governing auto traits have recently changed resulting in potential breakage"##, + }, + Lint { + label: "suspicious_double_ref_op", + description: r##"suspicious call of trait method on `&&T`"##, + }, + Lint { label: "temporary_cstring_as_ptr", description: r##"detects getting the inner pointer of a temporary `CString`"##, }, Lint { + label: "test_unstable_lint", + description: r##"this unstable lint is only for testing"##, + }, + Lint { label: "text_direction_codepoint_in_comment", description: r##"invisible directionality-changing codepoints in comment"##, }, @@ -395,10 +550,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"raw pointer to an inference variable"##, }, Lint { - label: "unaligned_references", - description: r##"detects unaligned references to fields of packed structs"##, - }, - Lint { label: "uncommon_codepoints", description: r##"detects uncommon Unicode codepoints in identifiers"##, }, @@ -410,6 +561,26 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "unconditional_recursion", description: r##"functions that cannot return without calling themselves"##, }, + Lint { + label: "undefined_naked_function_abi", + description: r##"undefined naked function ABI"##, + }, + Lint { + label: "undropped_manually_drops", + description: r##"calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of it's inner value"##, + }, + Lint { + label: "unexpected_cfgs", + description: r##"detects unexpected names and values in `#[cfg]` conditions"##, + }, + Lint { + label: "unfulfilled_lint_expectations", + description: r##"unfulfilled lint expectation"##, + }, + Lint { + label: "ungated_async_fn_track_caller", + description: r##"enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled"##, + }, Lint { label: "uninhabited_static", description: r##"uninhabited static"## }, Lint { label: "unknown_crate_types", @@ -417,16 +588,27 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "unknown_lints", description: r##"unrecognized lint attribute"## }, Lint { + label: "unknown_or_malformed_diagnostic_attributes", + description: r##"unrecognized or malformed diagnostic attribute"##, + }, + Lint { label: "unnameable_test_items", description: r##"detects an item that cannot be named being marked as `#[test_case]`"##, }, + Lint { + label: "unnameable_types", + description: r##"effective visibility of a type is larger than the area in which it can be named"##, + }, Lint { label: "unreachable_code", description: r##"detects unreachable code paths"## }, Lint { label: "unreachable_patterns", description: r##"detects unreachable patterns"## }, Lint { label: "unreachable_pub", description: r##"`pub` items not reachable from crate root"##, }, - Lint { label: "unsafe_code", description: r##"usage of `unsafe` code"## }, + Lint { + label: "unsafe_code", + description: r##"usage of `unsafe` code and other potentially unsound constructs"##, + }, Lint { label: "unsafe_op_in_unsafe_fn", description: r##"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"##, @@ -440,16 +622,16 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects name collision with an existing but unstable method"##, }, Lint { - label: "unsupported_calling_conventions", - description: r##"use of unsupported calling convention"##, + label: "unstable_syntax_pre_expansion", + description: r##"unstable syntax can change at any point in the future, causing a hard error!"##, }, Lint { - label: "unsupported_naked_functions", - description: r##"unsupported naked function definitions"##, + label: "unsupported_calling_conventions", + description: r##"use of unsupported calling convention"##, }, Lint { label: "unused", - description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons"##, + description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##, }, Lint { label: "unused_allocation", @@ -460,6 +642,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detect assignments that will never be read"##, }, Lint { + label: "unused_associated_type_bounds", + description: r##"detects unused `Foo = Bar` bounds in `dyn Trait<Foo = Bar>`"##, + }, + Lint { label: "unused_attributes", description: r##"detects attributes that were not used by the compiler"##, }, @@ -491,6 +677,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "unused_lifetimes", description: r##"detects lifetime parameters that are never used"##, }, + Lint { + label: "unused_macro_rules", + description: r##"detects macro rules that were not used"##, + }, Lint { label: "unused_macros", description: r##"detects macros that were not used"## }, Lint { label: "unused_must_use", @@ -512,6 +702,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "unused_results", description: r##"unused result of an expression in a statement"##, }, + Lint { + label: "unused_tuple_struct_fields", + description: r##"detects tuple struct fields that are never read"##, + }, Lint { label: "unused_unsafe", description: r##"unnecessary use of an `unsafe` block"## }, Lint { label: "unused_variables", @@ -522,6 +716,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects deprecation attributes with no effect"##, }, Lint { + label: "useless_ptr_null_checks", + description: r##"useless checking of non-null-typed pointer"##, + }, + Lint { label: "variant_size_differences", description: r##"detects enums with widely varying variant sizes"##, }, @@ -542,51 +740,65 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"suggest using `loop { }` instead of `while true { }`"##, }, ]; + pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "future_incompatible", - description: r##"lint group for: forbidden-lint-groups, illegal-floating-point-literal-pattern, private-in-public, pub-use-of-private-extern-crate, invalid-type-param-default, const-err, unaligned-references, patterns-in-fns-without-body, missing-fragment-specifier, late-bound-lifetime-arguments, order-dependent-trait-objects, coherence-leak-check, unstable-name-collisions, where-clauses-object-safety, proc-macro-derive-resolution-fallback, macro-expanded-macro-exports-accessed-by-absolute-paths, ill-formed-attribute-input, conflicting-repr-hints, ambiguous-associated-items, mutable-borrow-reservation-conflict, indirect-structural-match, pointer-structural-match, nontrivial-structural-match, soft-unstable, cenum-impl-drop-cast, const-evaluatable-unchecked, uninhabited-static, unsupported-naked-functions, invalid-doc-attributes, semicolon-in-expressions-from-macros, legacy-derive-helpers, proc-macro-back-compat, unsupported-calling-conventions, deref-into-dyn-supertrait"##, + description: r##"lint group for: deref-into-dyn-supertrait, ambiguous-associated-items, ambiguous-glob-imports, byte-slice-in-packed-struct-with-derive, cenum-impl-drop-cast, coherence-leak-check, coinductive-overlap-in-coherence, conflicting-repr-hints, const-evaluatable-unchecked, const-patterns-without-partial-eq, deprecated-cfg-attr-crate-type-name, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, illegal-floating-point-literal-pattern, implied-bounds-entailment, indirect-structural-match, invalid-alignment, invalid-doc-attributes, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, nontrivial-structural-match, order-dependent-trait-objects, patterns-in-fns-without-body, pointer-structural-match, proc-macro-back-compat, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, semicolon-in-expressions-from-macros, soft-unstable, suspicious-auto-trait-impls, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, where-clauses-object-safety"##, }, children: &[ + "deref_into_dyn_supertrait", + "ambiguous_associated_items", + "ambiguous_glob_imports", + "byte_slice_in_packed_struct_with_derive", + "cenum_impl_drop_cast", + "coherence_leak_check", + "coinductive_overlap_in_coherence", + "conflicting_repr_hints", + "const_evaluatable_unchecked", + "const_patterns_without_partial_eq", + "deprecated_cfg_attr_crate_type_name", + "elided_lifetimes_in_associated_constant", "forbidden_lint_groups", + "ill_formed_attribute_input", "illegal_floating_point_literal_pattern", - "private_in_public", - "pub_use_of_private_extern_crate", + "implied_bounds_entailment", + "indirect_structural_match", + "invalid_alignment", + "invalid_doc_attributes", "invalid_type_param_default", - "const_err", - "unaligned_references", - "patterns_in_fns_without_body", - "missing_fragment_specifier", "late_bound_lifetime_arguments", - "order_dependent_trait_objects", - "coherence_leak_check", - "unstable_name_collisions", - "where_clauses_object_safety", - "proc_macro_derive_resolution_fallback", + "legacy_derive_helpers", "macro_expanded_macro_exports_accessed_by_absolute_paths", - "ill_formed_attribute_input", - "conflicting_repr_hints", - "ambiguous_associated_items", - "mutable_borrow_reservation_conflict", - "indirect_structural_match", - "pointer_structural_match", + "missing_fragment_specifier", "nontrivial_structural_match", + "order_dependent_trait_objects", + "patterns_in_fns_without_body", + "pointer_structural_match", + "proc_macro_back_compat", + "proc_macro_derive_resolution_fallback", + "pub_use_of_private_extern_crate", + "repr_transparent_external_private_fields", + "semicolon_in_expressions_from_macros", "soft_unstable", - "cenum_impl_drop_cast", - "const_evaluatable_unchecked", + "suspicious_auto_trait_impls", "uninhabited_static", - "unsupported_naked_functions", - "invalid_doc_attributes", - "semicolon_in_expressions_from_macros", - "legacy_derive_helpers", - "proc_macro_back_compat", + "unstable_name_collisions", + "unstable_syntax_pre_expansion", "unsupported_calling_conventions", - "deref_into_dyn_supertrait", + "where_clauses_object_safety", ], }, LintGroup { lint: Lint { + label: "let_underscore", + description: r##"lint group for: let-underscore-drop, let-underscore-lock"##, + }, + children: &["let_underscore_drop", "let_underscore_lock"], + }, + LintGroup { + lint: Lint { label: "nonstandard_style", description: r##"lint group for: non-camel-case-types, non-snake-case, non-upper-case-globals"##, }, @@ -595,13 +807,13 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "rust_2018_compatibility", - description: r##"lint group for: keyword-idents, anonymous-parameters, tyvar-behind-raw-pointer, absolute-paths-not-starting-with-crate"##, + description: r##"lint group for: keyword-idents, anonymous-parameters, absolute-paths-not-starting-with-crate, tyvar-behind-raw-pointer"##, }, children: &[ "keyword_idents", "anonymous_parameters", - "tyvar_behind_raw_pointer", "absolute_paths_not_starting_with_crate", + "tyvar_behind_raw_pointer", ], }, LintGroup { @@ -620,15 +832,15 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "rust_2021_compatibility", - description: r##"lint group for: ellipsis-inclusive-range-patterns, bare-trait-objects, rust-2021-incompatible-closure-captures, rust-2021-incompatible-or-patterns, rust-2021-prelude-collisions, rust-2021-prefixes-incompatible-syntax, array-into-iter, non-fmt-panics"##, + description: r##"lint group for: ellipsis-inclusive-range-patterns, bare-trait-objects, rust-2021-incompatible-closure-captures, rust-2021-incompatible-or-patterns, rust-2021-prefixes-incompatible-syntax, rust-2021-prelude-collisions, array-into-iter, non-fmt-panics"##, }, children: &[ "ellipsis_inclusive_range_patterns", "bare_trait_objects", "rust_2021_incompatible_closure_captures", "rust_2021_incompatible_or_patterns", - "rust_2021_prelude_collisions", "rust_2021_prefixes_incompatible_syntax", + "rust_2021_prelude_collisions", "array_into_iter", "non_fmt_panics", ], @@ -636,7 +848,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "unused", - description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons"##, + description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##, }, children: &[ "unused_imports", @@ -651,6 +863,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ "path_statements", "unused_attributes", "unused_macros", + "unused_macro_rules", "unused_allocation", "unused_doc_comments", "unused_extern_crates", @@ -659,6 +872,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ "unused_parens", "unused_braces", "redundant_semicolons", + "map_unit_fn", ], }, LintGroup { @@ -673,7 +887,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ pub const RUSTDOC_LINTS: &[Lint] = &[ Lint { label: "rustdoc::all", - description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::missing-doc-code-examples, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs"##, + description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links"##, }, Lint { label: "rustdoc::bare_urls", description: r##"detects URLs that are not hyperlinks"## }, Lint { @@ -708,27 +922,70 @@ pub const RUSTDOC_LINTS: &[Lint] = &[ label: "rustdoc::private_intra_doc_links", description: r##"linking from a public item to a private one"##, }, + Lint { + label: "rustdoc::redundant_explicit_links", + description: r##"detects redundant explicit links in doc comments"##, + }, + Lint { + label: "rustdoc::unescaped_backticks", + description: r##"detects unescaped backticks in doc comments"##, + }, ]; + pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &[LintGroup { lint: Lint { label: "rustdoc::all", - description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::missing-doc-code-examples, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs"##, + description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links"##, }, children: &[ "rustdoc::broken_intra_doc_links", "rustdoc::private_intra_doc_links", - "rustdoc::missing_doc_code_examples", "rustdoc::private_doc_tests", "rustdoc::invalid_codeblock_attributes", "rustdoc::invalid_rust_codeblocks", "rustdoc::invalid_html_tags", "rustdoc::bare_urls", "rustdoc::missing_crate_level_docs", + "rustdoc::unescaped_backticks", + "rustdoc::redundant_explicit_links", ], }]; pub const FEATURES: &[Lint] = &[ Lint { + label: "aarch64_ver_target_feature", + description: r##"# `aarch64_ver_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "abi_amdgpu_kernel", + description: r##"# `abi_amdgpu_kernel` + +The tracking issue for this feature is: [#51575] + +[#51575]: https://github.com/rust-lang/rust/issues/51575 + +------------------------ +"##, + }, + Lint { + label: "abi_avr_interrupt", + description: r##"# `abi_avr_interrupt` + +The tracking issue for this feature is: [#69664] + +[#69664]: https://github.com/rust-lang/rust/issues/69664 + +------------------------ +"##, + }, + Lint { label: "abi_c_cmse_nonsecure_call", description: r##"# `abi_c_cmse_nonsecure_call` @@ -931,6 +1188,121 @@ $ cat $(find -name '*.s') "##, }, Lint { + label: "abi_riscv_interrupt", + description: r##"# `abi_riscv_interrupt` + +The tracking issue for this feature is: [#111889] + +[#111889]: https://github.com/rust-lang/rust/issues/111889 + +------------------------ +"##, + }, + Lint { + label: "abi_unadjusted", + description: r##"# `abi_unadjusted` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "abi_vectorcall", + description: r##"# `abi_vectorcall` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "abi_x86_interrupt", + description: r##"# `abi_x86_interrupt` + +The tracking issue for this feature is: [#40180] + +[#40180]: https://github.com/rust-lang/rust/issues/40180 + +------------------------ +"##, + }, + Lint { + label: "absolute_path", + description: r##"# `absolute_path` + +The tracking issue for this feature is: [#92750] + +[#92750]: https://github.com/rust-lang/rust/issues/92750 + +------------------------ +"##, + }, + Lint { + label: "addr_parse_ascii", + description: r##"# `addr_parse_ascii` + +The tracking issue for this feature is: [#101035] + +[#101035]: https://github.com/rust-lang/rust/issues/101035 + +------------------------ +"##, + }, + Lint { + label: "adt_const_params", + description: r##"# `adt_const_params` + +The tracking issue for this feature is: [#95174] + +[#95174]: https://github.com/rust-lang/rust/issues/95174 + +------------------------ +"##, + }, + Lint { + label: "alloc_error_handler", + description: r##"# `alloc_error_handler` + +The tracking issue for this feature is: [#51540] + +[#51540]: https://github.com/rust-lang/rust/issues/51540 + +------------------------ +"##, + }, + Lint { + label: "alloc_error_hook", + description: r##"# `alloc_error_hook` + +The tracking issue for this feature is: [#51245] + +[#51245]: https://github.com/rust-lang/rust/issues/51245 + +------------------------ +"##, + }, + Lint { + label: "alloc_internals", + description: r##"# `alloc_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "alloc_layout_extra", + description: r##"# `alloc_layout_extra` + +The tracking issue for this feature is: [#55724] + +[#55724]: https://github.com/rust-lang/rust/issues/55724 + +------------------------ +"##, + }, + Lint { label: "allocator_api", description: r##"# `allocator_api` @@ -961,53 +1333,171 @@ compiler. "##, }, Lint { - label: "arbitrary_enum_discriminant", - description: r##"# `arbitrary_enum_discriminant` + label: "allow_internal_unsafe", + description: r##"# `allow_internal_unsafe` -The tracking issue for this feature is: [#60553] +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. -[#60553]: https://github.com/rust-lang/rust/issues/60553 +------------------------ +"##, + }, + Lint { + label: "allow_internal_unstable", + description: r##"# `allow_internal_unstable` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ +"##, + }, + Lint { + label: "anonymous_lifetime_in_impl_trait", + description: r##"# `anonymous_lifetime_in_impl_trait` -The `arbitrary_enum_discriminant` feature permits tuple-like and -struct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants. +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. -## Examples +------------------------ +"##, + }, + Lint { + label: "arbitrary_self_types", + description: r##"# `arbitrary_self_types` -```rust -#![feature(arbitrary_enum_discriminant)] - -#[allow(dead_code)] -#[repr(u8)] -enum Enum { - Unit = 3, - Tuple(u16) = 2, - Struct { - a: u8, - b: u16, - } = 1, -} +The tracking issue for this feature is: [#44874] -impl Enum { - fn tag(&self) -> u8 { - unsafe { *(self as *const Self as *const u8) } - } -} +[#44874]: https://github.com/rust-lang/rust/issues/44874 -assert_eq!(3, Enum::Unit.tag()); -assert_eq!(2, Enum::Tuple(5).tag()); -assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag()); -``` +------------------------ +"##, + }, + Lint { + label: "arc_unwrap_or_clone", + description: r##"# `arc_unwrap_or_clone` + +The tracking issue for this feature is: [#93610] + +[#93610]: https://github.com/rust-lang/rust/issues/93610 + +------------------------ +"##, + }, + Lint { + label: "arm_target_feature", + description: r##"# `arm_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "array_chunks", + description: r##"# `array_chunks` + +The tracking issue for this feature is: [#74985] + +[#74985]: https://github.com/rust-lang/rust/issues/74985 + +------------------------ +"##, + }, + Lint { + label: "array_into_iter_constructors", + description: r##"# `array_into_iter_constructors` + +The tracking issue for this feature is: [#91583] + +[#91583]: https://github.com/rust-lang/rust/issues/91583 + +------------------------ +"##, + }, + Lint { + label: "array_methods", + description: r##"# `array_methods` + +The tracking issue for this feature is: [#76118] + +[#76118]: https://github.com/rust-lang/rust/issues/76118 + +------------------------ +"##, + }, + Lint { + label: "array_try_from_fn", + description: r##"# `array_try_from_fn` + +The tracking issue for this feature is: [#89379] + +[#89379]: https://github.com/rust-lang/rust/issues/89379 + +------------------------ +"##, + }, + Lint { + label: "array_try_map", + description: r##"# `array_try_map` + +The tracking issue for this feature is: [#79711] + +[#79711]: https://github.com/rust-lang/rust/issues/79711 + +------------------------ +"##, + }, + Lint { + label: "array_windows", + description: r##"# `array_windows` + +The tracking issue for this feature is: [#75027] + +[#75027]: https://github.com/rust-lang/rust/issues/75027 + +------------------------ +"##, + }, + Lint { + label: "as_array_of_cells", + description: r##"# `as_array_of_cells` + +The tracking issue for this feature is: [#88248] + +[#88248]: https://github.com/rust-lang/rust/issues/88248 + +------------------------ +"##, + }, + Lint { + label: "ascii_char", + description: r##"# `ascii_char` + +The tracking issue for this feature is: [#110998] + +[#110998]: https://github.com/rust-lang/rust/issues/110998 + +------------------------ +"##, + }, + Lint { + label: "ascii_char_variants", + description: r##"# `ascii_char_variants` + +The tracking issue for this feature is: [#110998] + +[#110998]: https://github.com/rust-lang/rust/issues/110998 + +------------------------ "##, }, Lint { label: "asm_const", description: r##"# `asm_const` -The tracking issue for this feature is: [#72016] +The tracking issue for this feature is: [#93332] -[#72016]: https://github.com/rust-lang/rust/issues/72016 +[#93332]: https://github.com/rust-lang/rust/issues/93332 ------------------------ @@ -1020,9 +1510,9 @@ This feature adds a `const <expr>` operand type to `asm!` and `global_asm!`. label: "asm_experimental_arch", description: r##"# `asm_experimental_arch` -The tracking issue for this feature is: [#72016] +The tracking issue for this feature is: [#93335] -[#72016]: https://github.com/rust-lang/rust/issues/72016 +[#93335]: https://github.com/rust-lang/rust/issues/93335 ------------------------ @@ -1035,6 +1525,10 @@ This feature tracks `asm!` and `global_asm!` support for the following architect - BPF - SPIR-V - AVR +- MSP430 +- M68k +- CSKY +- s390x ## Register classes @@ -1059,6 +1553,14 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | AVR | `reg_pair` | `r3r2` .. `r25r24`, `X`, `Z` | `r` | | AVR | `reg_iw` | `r25r24`, `X`, `Z` | `w` | | AVR | `reg_ptr` | `X`, `Z` | `e` | +| MSP430 | `reg` | `r[0-15]` | `r` | +| M68k | `reg` | `d[0-7]`, `a[0-7]` | `r` | +| M68k | `reg_data` | `d[0-7]` | `d` | +| M68k | `reg_addr` | `a[0-3]` | `a` | +| CSKY | `reg` | `r[0-31]` | `r` | +| CSKY | `freg` | `f[0-31]` | `f` | +| s390x | `reg` | `r[0-10]`, `r[12-14]` | `r` | +| s390x | `freg` | `f[0-15]` | `f` | > **Notes**: > - NVPTX doesn't have a fixed register set, so named registers are not supported. @@ -1087,6 +1589,13 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | BPF | `wreg` | `alu32` | `i8` `i16` `i32` | | AVR | `reg`, `reg_upper` | None | `i8` | | AVR | `reg_pair`, `reg_iw`, `reg_ptr` | None | `i16` | +| MSP430 | `reg` | None | `i8`, `i16` | +| M68k | `reg`, `reg_addr` | None | `i16`, `i32` | +| M68k | `reg_data` | None | `i8`, `i16`, `i32` | +| CSKY | `reg` | None | `i8`, `i16`, `i32` | +| CSKY | `freg` | None | `f32`, | +| s390x | `reg` | None | `i8`, `i16`, `i32`, `i64` | +| s390x | `freg` | None | `f32`, `f64` | ## Register aliases @@ -1100,13 +1609,36 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | AVR | `XL` | `r26` | | AVR | `ZH` | `r31` | | AVR | `ZL` | `r30` | +| MSP430 | `r0` | `pc` | +| MSP430 | `r1` | `sp` | +| MSP430 | `r2` | `sr` | +| MSP430 | `r3` | `cg` | +| MSP430 | `r4` | `fp` | +| M68k | `a5` | `bp` | +| M68k | `a6` | `fp` | +| M68k | `a7` | `sp`, `usp`, `ssp`, `isp` | +| CSKY | `r[0-3]` | `a[0-3]` | +| CSKY | `r[4-11]` | `l[0-7]` | +| CSKY | `r[12-13]` | `t[0-1]` | +| CSKY | `r14` | `sp` | +| CSKY | `r15` | `lr` | +| CSKY | `r[16-17]` | `l[8-9]` | +| CSKY | `r[18-25]` | `t[2-9]` | +| CSKY | `r28` | `rgb` | +| CSKY | `r29` | `rtb` | +| CSKY | `r30` | `svbr` | +| CSKY | `r31` | `tls` | + +> **Notes**: +> - TI does not mandate a frame pointer for MSP430, but toolchains are allowed + to use one; LLVM uses `r4`. ## Unsupported registers | Architecture | Unsupported register | Reason | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | The frame pointer cannot be used as an input or output. | +| All | `sp`, `r15` (s390x) | The stack pointer must be restored to its original value at the end of an asm code block. | +| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x) | The frame pointer cannot be used as an input or output. | | All | `r19` (Hexagon) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$1` or `$at` | Reserved for assembler. | @@ -1115,6 +1647,15 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | | AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | +|MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. | +| M68k | `a4`, `a5` | Used internally by LLVM for the base pointer and global base pointer. | +| CSKY | `r7`, `r28` | Used internally by LLVM for the base pointer and global base pointer. | +| CSKY | `r8` | Used internally by LLVM for the frame pointer. | +| CSKY | `r14` | Used internally by LLVM for the stack pointer. | +| CSKY | `r15` | This is the link register. | +| CSKY | `r[26-30]` | Reserved by its ABI. | +| CSKY | `r31` | This is the TLS register. | + ## Template modifiers @@ -1129,42 +1670,167 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | PowerPC | `reg` | None | `0` | None | | PowerPC | `reg_nonzero` | None | `3` | `b` | | PowerPC | `freg` | None | `0` | None | +| s390x | `reg` | None | `%r0` | None | +| s390x | `freg` | None | `%f0` | None | +| CSKY | `reg` | None | `r0` | None | +| CSKY | `freg` | None | `f0` | None | # Flags covered by `preserves_flags` These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: - AVR - The status register `SREG`. +- MSP430 + - The status register `r2`. +- M68k + - The condition code register `ccr`. +- s390x + - The condition code register `cc`. "##, }, Lint { - label: "asm_sym", - description: r##"# `asm_sym` + label: "asm_unwind", + description: r##"# `asm_unwind` -The tracking issue for this feature is: [#72016] +The tracking issue for this feature is: [#93334] -[#72016]: https://github.com/rust-lang/rust/issues/72016 +[#93334]: https://github.com/rust-lang/rust/issues/93334 ------------------------ -This feature adds a `sym <path>` operand type to `asm!` and `global_asm!`. -- `<path>` must refer to a `fn` or `static`. -- A mangled symbol name referring to the item is substituted into the asm template string. -- The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc). -- `<path>` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data. +This feature adds a `may_unwind` option to `asm!` which allows an `asm` block to unwind stack and be part of the stack unwinding process. This option is only supported by the LLVM backend right now. "##, }, Lint { - label: "asm_unwind", - description: r##"# `asm_unwind` + label: "assert_matches", + description: r##"# `assert_matches` -The tracking issue for this feature is: [#72016] +The tracking issue for this feature is: [#82775] -[#72016]: https://github.com/rust-lang/rust/issues/72016 +[#82775]: https://github.com/rust-lang/rust/issues/82775 ------------------------ +"##, + }, + Lint { + label: "associated_const_equality", + description: r##"# `associated_const_equality` -This feature adds a `may_unwind` option to `asm!` which allows an `asm` block to unwind stack and be part of the stack unwinding process. This option is only supported by the LLVM backend right now. +The tracking issue for this feature is: [#92827] + +[#92827]: https://github.com/rust-lang/rust/issues/92827 + +------------------------ +"##, + }, + Lint { + label: "associated_type_bounds", + description: r##"# `associated_type_bounds` + +The tracking issue for this feature is: [#52662] + +[#52662]: https://github.com/rust-lang/rust/issues/52662 + +------------------------ +"##, + }, + Lint { + label: "associated_type_defaults", + description: r##"# `associated_type_defaults` + +The tracking issue for this feature is: [#29661] + +[#29661]: https://github.com/rust-lang/rust/issues/29661 + +------------------------ +"##, + }, + Lint { + label: "async_closure", + description: r##"# `async_closure` + +The tracking issue for this feature is: [#62290] + +[#62290]: https://github.com/rust-lang/rust/issues/62290 + +------------------------ +"##, + }, + Lint { + label: "async_fn_in_trait", + description: r##"# `async_fn_in_trait` + +The tracking issue for this feature is: [#91611] + +[#91611]: https://github.com/rust-lang/rust/issues/91611 + +------------------------ +"##, + }, + Lint { + label: "async_fn_track_caller", + description: r##"# `async_fn_track_caller` + +The tracking issue for this feature is: [#110011] + +[#110011]: https://github.com/rust-lang/rust/issues/110011 + +------------------------ +"##, + }, + Lint { + label: "async_iter_from_iter", + description: r##"# `async_iter_from_iter` + +The tracking issue for this feature is: [#81798] + +[#81798]: https://github.com/rust-lang/rust/issues/81798 + +------------------------ +"##, + }, + Lint { + label: "async_iterator", + description: r##"# `async_iterator` + +The tracking issue for this feature is: [#79024] + +[#79024]: https://github.com/rust-lang/rust/issues/79024 + +------------------------ +"##, + }, + Lint { + label: "atomic_bool_fetch_not", + description: r##"# `atomic_bool_fetch_not` + +The tracking issue for this feature is: [#98485] + +[#98485]: https://github.com/rust-lang/rust/issues/98485 + +------------------------ +"##, + }, + Lint { + label: "atomic_from_mut", + description: r##"# `atomic_from_mut` + +The tracking issue for this feature is: [#76314] + +[#76314]: https://github.com/rust-lang/rust/issues/76314 + +------------------------ +"##, + }, + Lint { + label: "atomic_from_ptr", + description: r##"# `atomic_from_ptr` + +The tracking issue for this feature is: [#108652] + +[#108652]: https://github.com/rust-lang/rust/issues/108652 + +------------------------ "##, }, Lint { @@ -1184,8 +1850,8 @@ that are automatically implemented for every type, unless the type, or a type it has explicitly opted out via a negative impl. (Negative impls are separately controlled by the `negative_impls` feature.) -[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html -[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html +[`Send`]: ../../std/marker/trait.Send.html +[`Sync`]: ../../std/marker/trait.Sync.html ```rust,ignore (partial-example) impl !Trait for Type {} @@ -1278,6 +1944,116 @@ Auto traits cannot have supertraits. This is for soundness reasons, as the inter "##, }, Lint { + label: "avx512_target_feature", + description: r##"# `avx512_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "backtrace_frames", + description: r##"# `backtrace_frames` + +The tracking issue for this feature is: [#79676] + +[#79676]: https://github.com/rust-lang/rust/issues/79676 + +------------------------ +"##, + }, + Lint { + label: "bigint_helper_methods", + description: r##"# `bigint_helper_methods` + +The tracking issue for this feature is: [#85532] + +[#85532]: https://github.com/rust-lang/rust/issues/85532 + +------------------------ +"##, + }, + Lint { + label: "binary_heap_as_slice", + description: r##"# `binary_heap_as_slice` + +The tracking issue for this feature is: [#83659] + +[#83659]: https://github.com/rust-lang/rust/issues/83659 + +------------------------ +"##, + }, + Lint { + label: "binary_heap_drain_sorted", + description: r##"# `binary_heap_drain_sorted` + +The tracking issue for this feature is: [#59278] + +[#59278]: https://github.com/rust-lang/rust/issues/59278 + +------------------------ +"##, + }, + Lint { + label: "binary_heap_into_iter_sorted", + description: r##"# `binary_heap_into_iter_sorted` + +The tracking issue for this feature is: [#59278] + +[#59278]: https://github.com/rust-lang/rust/issues/59278 + +------------------------ +"##, + }, + Lint { + label: "bound_as_ref", + description: r##"# `bound_as_ref` + +The tracking issue for this feature is: [#80996] + +[#80996]: https://github.com/rust-lang/rust/issues/80996 + +------------------------ +"##, + }, + Lint { + label: "bound_map", + description: r##"# `bound_map` + +The tracking issue for this feature is: [#86026] + +[#86026]: https://github.com/rust-lang/rust/issues/86026 + +------------------------ +"##, + }, + Lint { + label: "box_into_boxed_slice", + description: r##"# `box_into_boxed_slice` + +The tracking issue for this feature is: [#71582] + +[#71582]: https://github.com/rust-lang/rust/issues/71582 + +------------------------ +"##, + }, + Lint { + label: "box_into_inner", + description: r##"# `box_into_inner` + +The tracking issue for this feature is: [#80437] + +[#80437]: https://github.com/rust-lang/rust/issues/80437 + +------------------------ +"##, + }, + Lint { label: "box_patterns", description: r##"# `box_patterns` @@ -1285,8 +2061,6 @@ The tracking issue for this feature is: [#29641] [#29641]: https://github.com/rust-lang/rust/issues/29641 -See also [`box_syntax`](box-syntax.md) - ------------------------ Box patterns let you match on `Box<T>`s: @@ -1299,10 +2073,10 @@ fn main() { let b = Some(Box::new(5)); match b { Some(box n) if n < 0 => { - println!("Box contains negative number {}", n); + println!("Box contains negative number {n}"); }, Some(box n) if n >= 0 => { - println!("Box contains non-negative number {}", n); + println!("Box contains non-negative number {n}"); }, None => { println!("No box"); @@ -1314,29 +2088,102 @@ fn main() { "##, }, Lint { - label: "box_syntax", - description: r##"# `box_syntax` + label: "bpf_target_feature", + description: r##"# `bpf_target_feature` -The tracking issue for this feature is: [#49733] +The tracking issue for this feature is: [#44839] -[#49733]: https://github.com/rust-lang/rust/issues/49733 +[#44839]: https://github.com/rust-lang/rust/issues/44839 -See also [`box_patterns`](box-patterns.md) +------------------------ +"##, + }, + Lint { + label: "btree_cursors", + description: r##"# `btree_cursors` + +The tracking issue for this feature is: [#107540] + +[#107540]: https://github.com/rust-lang/rust/issues/107540 ------------------------ +"##, + }, + Lint { + label: "btree_extract_if", + description: r##"# `btree_extract_if` -Currently the only stable way to create a `Box` is via the `Box::new` method. -Also it is not possible in stable Rust to destructure a `Box` in a match -pattern. The unstable `box` keyword can be used to create a `Box`. An example -usage would be: +The tracking issue for this feature is: [#70530] -```rust -#![feature(box_syntax)] +[#70530]: https://github.com/rust-lang/rust/issues/70530 -fn main() { - let b = box 5; -} -``` +------------------------ +"##, + }, + Lint { + label: "btreemap_alloc", + description: r##"# `btreemap_alloc` + +The tracking issue for this feature is: [#32838] + +[#32838]: https://github.com/rust-lang/rust/issues/32838 + +------------------------ +"##, + }, + Lint { + label: "buf_read_has_data_left", + description: r##"# `buf_read_has_data_left` + +The tracking issue for this feature is: [#86423] + +[#86423]: https://github.com/rust-lang/rust/issues/86423 + +------------------------ +"##, + }, + Lint { + label: "builtin_syntax", + description: r##"# `builtin_syntax` + +The tracking issue for this feature is: [#110680] + +[#110680]: https://github.com/rust-lang/rust/issues/110680 + +------------------------ +"##, + }, + Lint { + label: "byte_slice_trim_ascii", + description: r##"# `byte_slice_trim_ascii` + +The tracking issue for this feature is: [#94035] + +[#94035]: https://github.com/rust-lang/rust/issues/94035 + +------------------------ +"##, + }, + Lint { + label: "c_size_t", + description: r##"# `c_size_t` + +The tracking issue for this feature is: [#88345] + +[#88345]: https://github.com/rust-lang/rust/issues/88345 + +------------------------ +"##, + }, + Lint { + label: "c_str_literals", + description: r##"# `c_str_literals` + +The tracking issue for this feature is: [#105723] + +[#105723]: https://github.com/rust-lang/rust/issues/105723 + +------------------------ "##, }, Lint { @@ -1349,9 +2196,20 @@ The tracking issue for this feature is: [#74990] ------------------------ -Introduces four new ABI strings: "C-unwind", "stdcall-unwind", -"thiscall-unwind", and "system-unwind". These enable unwinding from other -languages (such as C++) into Rust frames and from Rust into other languages. +Introduces new ABI strings: +- "C-unwind" +- "cdecl-unwind" +- "stdcall-unwind" +- "fastcall-unwind" +- "vectorcall-unwind" +- "thiscall-unwind" +- "aapcs-unwind" +- "win64-unwind" +- "sysv64-unwind" +- "system-unwind" + +These enable unwinding from other languages (such as C++) into Rust frames and +from Rust into other languages. See [RFC 2945] for more information. @@ -1369,7 +2227,7 @@ The tracking issue for this feature is: [#44930] ------------------------ The `c_variadic` language feature enables C-variadic functions to be -defined in Rust. The may be called both from within Rust and via FFI. +defined in Rust. They may be called both from within Rust and via FFI. ## Examples @@ -1426,45 +2284,91 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { - label: "cfg_panic", - description: r##"# `cfg_panic` + label: "can_vector", + description: r##"# `can_vector` -The tracking issue for this feature is: [#77443] +The tracking issue for this feature is: [#69941] -[#77443]: https://github.com/rust-lang/rust/issues/77443 +[#69941]: https://github.com/rust-lang/rust/issues/69941 ------------------------ +"##, + }, + Lint { + label: "cell_leak", + description: r##"# `cell_leak` -The `cfg_panic` feature makes it possible to execute different code -depending on the panic strategy. +The tracking issue for this feature is: [#69099] -Possible values at the moment are `"unwind"` or `"abort"`, although -it is possible that new panic strategies may be added to Rust in the -future. +[#69099]: https://github.com/rust-lang/rust/issues/69099 -## Examples +------------------------ +"##, + }, + Lint { + label: "cell_update", + description: r##"# `cell_update` -```rust -#![feature(cfg_panic)] +The tracking issue for this feature is: [#50186] -#[cfg(panic = "unwind")] -fn a() { - // ... -} +[#50186]: https://github.com/rust-lang/rust/issues/50186 -#[cfg(not(panic = "unwind"))] -fn a() { - // ... -} +------------------------ +"##, + }, + Lint { + label: "cfg_accessible", + description: r##"# `cfg_accessible` -fn b() { - if cfg!(panic = "abort") { - // ... - } else { - // ... - } -} -``` +The tracking issue for this feature is: [#64797] + +[#64797]: https://github.com/rust-lang/rust/issues/64797 + +------------------------ +"##, + }, + Lint { + label: "cfg_eval", + description: r##"# `cfg_eval` + +The tracking issue for this feature is: [#82679] + +[#82679]: https://github.com/rust-lang/rust/issues/82679 + +------------------------ +"##, + }, + Lint { + label: "cfg_match", + description: r##"# `cfg_match` + +The tracking issue for this feature is: [#115585] + +[#115585]: https://github.com/rust-lang/rust/issues/115585 + +------------------------ +"##, + }, + Lint { + label: "cfg_overflow_checks", + description: r##"# `cfg_overflow_checks` + +The tracking issue for this feature is: [#111466] + +[#111466]: https://github.com/rust-lang/rust/issues/111466 + +------------------------ +"##, + }, + Lint { + label: "cfg_relocation_model", + description: r##"# `cfg_relocation_model` + +The tracking issue for this feature is: [#114929] + +[#114929]: https://github.com/rust-lang/rust/issues/114929 + +------------------------ "##, }, Lint { @@ -1506,6 +2410,61 @@ fn b() { "##, }, Lint { + label: "cfg_target_abi", + description: r##"# `cfg_target_abi` + +The tracking issue for this feature is: [#80970] + +[#80970]: https://github.com/rust-lang/rust/issues/80970 + +------------------------ +"##, + }, + Lint { + label: "cfg_target_compact", + description: r##"# `cfg_target_compact` + +The tracking issue for this feature is: [#96901] + +[#96901]: https://github.com/rust-lang/rust/issues/96901 + +------------------------ +"##, + }, + Lint { + label: "cfg_target_has_atomic", + description: r##"# `cfg_target_has_atomic` + +The tracking issue for this feature is: [#94039] + +[#94039]: https://github.com/rust-lang/rust/issues/94039 + +------------------------ +"##, + }, + Lint { + label: "cfg_target_has_atomic_equal_alignment", + description: r##"# `cfg_target_has_atomic_equal_alignment` + +The tracking issue for this feature is: [#93822] + +[#93822]: https://github.com/rust-lang/rust/issues/93822 + +------------------------ +"##, + }, + Lint { + label: "cfg_target_thread_local", + description: r##"# `cfg_target_thread_local` + +The tracking issue for this feature is: [#29594] + +[#29594]: https://github.com/rust-lang/rust/issues/29594 + +------------------------ +"##, + }, + Lint { label: "cfg_version", description: r##"# `cfg_version` @@ -1545,10 +2504,72 @@ fn b() { "##, }, Lint { - label: "char_error_internals", - description: r##"# `char_error_internals` + label: "cfi_encoding", + description: r##"# `cfi_encoding` -This feature is internal to the Rust compiler and is not intended for general use. +The tracking issue for this feature is: [#89653] + +[#89653]: https://github.com/rust-lang/rust/issues/89653 + +------------------------ + +The `cfi_encoding` feature allows the user to define a CFI encoding for a type. +It allows the user to use a different names for types that otherwise would be +required to have the same name as used in externally defined C functions. + +## Examples + +```rust +#![feature(cfi_encoding, extern_types)] + +#[cfi_encoding = "3Foo"] +pub struct Type1(i32); + +extern { + #[cfi_encoding = "3Bar"] + type Type2; +} +``` +"##, + }, + Lint { + label: "char_indices_offset", + description: r##"# `char_indices_offset` + +The tracking issue for this feature is: [#83871] + +[#83871]: https://github.com/rust-lang/rust/issues/83871 + +------------------------ +"##, + }, + Lint { + label: "char_internals", + description: r##"# `char_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "char_min", + description: r##"# `char_min` + +The tracking issue for this feature is: [#114298] + +[#114298]: https://github.com/rust-lang/rust/issues/114298 + +------------------------ +"##, + }, + Lint { + label: "closure_lifetime_binder", + description: r##"# `closure_lifetime_binder` + +The tracking issue for this feature is: [#97362] + +[#97362]: https://github.com/rust-lang/rust/issues/97362 ------------------------ "##, @@ -1570,6 +2591,17 @@ available through `std::panic::Location::caller()`, just like using "##, }, Lint { + label: "cmp_minmax", + description: r##"# `cmp_minmax` + +The tracking issue for this feature is: [#115939] + +[#115939]: https://github.com/rust-lang/rust/issues/115939 + +------------------------ +"##, + }, + Lint { label: "cmse_nonsecure_entry", description: r##"# `cmse_nonsecure_entry` @@ -1655,6 +2687,28 @@ $ arm-none-eabi-objdump -D function.o "##, }, Lint { + label: "coerce_unsized", + description: r##"# `coerce_unsized` + +The tracking issue for this feature is: [#18598] + +[#18598]: https://github.com/rust-lang/rust/issues/18598 + +------------------------ +"##, + }, + Lint { + label: "collapse_debuginfo", + description: r##"# `collapse_debuginfo` + +The tracking issue for this feature is: [#100758] + +[#100758]: https://github.com/rust-lang/rust/issues/100758 + +------------------------ +"##, + }, + Lint { label: "compiler_builtins", description: r##"# `compiler_builtins` @@ -1664,6 +2718,17 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "concat_bytes", + description: r##"# `concat_bytes` + +The tracking issue for this feature is: [#87555] + +[#87555]: https://github.com/rust-lang/rust/issues/87555 + +------------------------ +"##, + }, + Lint { label: "concat_idents", description: r##"# `concat_idents` @@ -1690,14 +2755,1027 @@ fn main() { "##, }, Lint { - label: "const_eval_limit", - description: r##"# `const_eval_limit` + label: "const_align_of_val", + description: r##"# `const_align_of_val` + +The tracking issue for this feature is: [#46571] + +[#46571]: https://github.com/rust-lang/rust/issues/46571 + +------------------------ +"##, + }, + Lint { + label: "const_align_of_val_raw", + description: r##"# `const_align_of_val_raw` + +The tracking issue for this feature is: [#46571] + +[#46571]: https://github.com/rust-lang/rust/issues/46571 + +------------------------ +"##, + }, + Lint { + label: "const_align_offset", + description: r##"# `const_align_offset` + +The tracking issue for this feature is: [#90962] + +[#90962]: https://github.com/rust-lang/rust/issues/90962 + +------------------------ +"##, + }, + Lint { + label: "const_alloc_error", + description: r##"# `const_alloc_error` + +The tracking issue for this feature is: [#92523] + +[#92523]: https://github.com/rust-lang/rust/issues/92523 + +------------------------ +"##, + }, + Lint { + label: "const_alloc_layout", + description: r##"# `const_alloc_layout` + +The tracking issue for this feature is: [#67521] + +[#67521]: https://github.com/rust-lang/rust/issues/67521 + +------------------------ +"##, + }, + Lint { + label: "const_arguments_as_str", + description: r##"# `const_arguments_as_str` + +The tracking issue for this feature is: [#103900] + +[#103900]: https://github.com/rust-lang/rust/issues/103900 + +------------------------ +"##, + }, + Lint { + label: "const_array_from_ref", + description: r##"# `const_array_from_ref` + +The tracking issue for this feature is: [#90206] + +[#90206]: https://github.com/rust-lang/rust/issues/90206 + +------------------------ +"##, + }, + Lint { + label: "const_array_into_iter_constructors", + description: r##"# `const_array_into_iter_constructors` + +The tracking issue for this feature is: [#91583] + +[#91583]: https://github.com/rust-lang/rust/issues/91583 + +------------------------ +"##, + }, + Lint { + label: "const_assert_type2", + description: r##"# `const_assert_type2` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_assume", + description: r##"# `const_assume` + +The tracking issue for this feature is: [#76972] + +[#76972]: https://github.com/rust-lang/rust/issues/76972 + +------------------------ +"##, + }, + Lint { + label: "const_async_blocks", + description: r##"# `const_async_blocks` + +The tracking issue for this feature is: [#85368] + +[#85368]: https://github.com/rust-lang/rust/issues/85368 + +------------------------ +"##, + }, + Lint { + label: "const_bigint_helper_methods", + description: r##"# `const_bigint_helper_methods` + +The tracking issue for this feature is: [#85532] + +[#85532]: https://github.com/rust-lang/rust/issues/85532 + +------------------------ +"##, + }, + Lint { + label: "const_black_box", + description: r##"# `const_black_box` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_box", + description: r##"# `const_box` + +The tracking issue for this feature is: [#92521] + +[#92521]: https://github.com/rust-lang/rust/issues/92521 + +------------------------ +"##, + }, + Lint { + label: "const_btree_len", + description: r##"# `const_btree_len` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_caller_location", + description: r##"# `const_caller_location` + +The tracking issue for this feature is: [#76156] + +[#76156]: https://github.com/rust-lang/rust/issues/76156 + +------------------------ +"##, + }, + Lint { + label: "const_cell_into_inner", + description: r##"# `const_cell_into_inner` + +The tracking issue for this feature is: [#78729] + +[#78729]: https://github.com/rust-lang/rust/issues/78729 + +------------------------ +"##, + }, + Lint { + label: "const_char_from_u32_unchecked", + description: r##"# `const_char_from_u32_unchecked` + +The tracking issue for this feature is: [#89259] + +[#89259]: https://github.com/rust-lang/rust/issues/89259 + +------------------------ +"##, + }, + Lint { + label: "const_closures", + description: r##"# `const_closures` + +The tracking issue for this feature is: [#106003] + +[#106003]: https://github.com/rust-lang/rust/issues/106003 + +------------------------ +"##, + }, + Lint { + label: "const_collections_with_hasher", + description: r##"# `const_collections_with_hasher` + +The tracking issue for this feature is: [#102575] + +[#102575]: https://github.com/rust-lang/rust/issues/102575 + +------------------------ +"##, + }, + Lint { + label: "const_cow_is_borrowed", + description: r##"# `const_cow_is_borrowed` + +The tracking issue for this feature is: [#65143] + +[#65143]: https://github.com/rust-lang/rust/issues/65143 + +------------------------ +"##, + }, + Lint { + label: "const_cstr_from_ptr", + description: r##"# `const_cstr_from_ptr` + +The tracking issue for this feature is: [#113219] + +[#113219]: https://github.com/rust-lang/rust/issues/113219 + +------------------------ +"##, + }, + Lint { + label: "const_discriminant", + description: r##"# `const_discriminant` + +The tracking issue for this feature is: [#69821] + +[#69821]: https://github.com/rust-lang/rust/issues/69821 + +------------------------ +"##, + }, + Lint { + label: "const_eval_select", + description: r##"# `const_eval_select` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_exact_div", + description: r##"# `const_exact_div` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_extern_fn", + description: r##"# `const_extern_fn` + +The tracking issue for this feature is: [#64926] + +[#64926]: https://github.com/rust-lang/rust/issues/64926 + +------------------------ +"##, + }, + Lint { + label: "const_float_bits_conv", + description: r##"# `const_float_bits_conv` + +The tracking issue for this feature is: [#72447] + +[#72447]: https://github.com/rust-lang/rust/issues/72447 + +------------------------ +"##, + }, + Lint { + label: "const_float_classify", + description: r##"# `const_float_classify` + +The tracking issue for this feature is: [#72505] + +[#72505]: https://github.com/rust-lang/rust/issues/72505 + +------------------------ +"##, + }, + Lint { + label: "const_fmt_arguments_new", + description: r##"# `const_fmt_arguments_new` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_fn_floating_point_arithmetic", + description: r##"# `const_fn_floating_point_arithmetic` + +The tracking issue for this feature is: [#57241] + +[#57241]: https://github.com/rust-lang/rust/issues/57241 + +------------------------ +"##, + }, + Lint { + label: "const_for", + description: r##"# `const_for` + +The tracking issue for this feature is: [#87575] + +[#87575]: https://github.com/rust-lang/rust/issues/87575 + +------------------------ +"##, + }, + Lint { + label: "const_format_args", + description: r##"# `const_format_args` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_hash", + description: r##"# `const_hash` + +The tracking issue for this feature is: [#104061] + +[#104061]: https://github.com/rust-lang/rust/issues/104061 + +------------------------ +"##, + }, + Lint { + label: "const_heap", + description: r##"# `const_heap` + +The tracking issue for this feature is: [#79597] + +[#79597]: https://github.com/rust-lang/rust/issues/79597 + +------------------------ +"##, + }, + Lint { + label: "const_index_range_slice_index", + description: r##"# `const_index_range_slice_index` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_inherent_unchecked_arith", + description: r##"# `const_inherent_unchecked_arith` + +The tracking issue for this feature is: [#85122] + +[#85122]: https://github.com/rust-lang/rust/issues/85122 + +------------------------ +"##, + }, + Lint { + label: "const_int_unchecked_arith", + description: r##"# `const_int_unchecked_arith` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_intoiterator_identity", + description: r##"# `const_intoiterator_identity` + +The tracking issue for this feature is: [#90603] + +[#90603]: https://github.com/rust-lang/rust/issues/90603 + +------------------------ +"##, + }, + Lint { + label: "const_intrinsic_compare_bytes", + description: r##"# `const_intrinsic_compare_bytes` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_intrinsic_forget", + description: r##"# `const_intrinsic_forget` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_intrinsic_raw_eq", + description: r##"# `const_intrinsic_raw_eq` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_io_structs", + description: r##"# `const_io_structs` + +The tracking issue for this feature is: [#78812] + +[#78812]: https://github.com/rust-lang/rust/issues/78812 + +------------------------ +"##, + }, + Lint { + label: "const_ip", + description: r##"# `const_ip` + +The tracking issue for this feature is: [#76205] + +[#76205]: https://github.com/rust-lang/rust/issues/76205 + +------------------------ +"##, + }, + Lint { + label: "const_ipv4", + description: r##"# `const_ipv4` + +The tracking issue for this feature is: [#76205] + +[#76205]: https://github.com/rust-lang/rust/issues/76205 + +------------------------ +"##, + }, + Lint { + label: "const_ipv6", + description: r##"# `const_ipv6` + +The tracking issue for this feature is: [#76205] + +[#76205]: https://github.com/rust-lang/rust/issues/76205 + +------------------------ +"##, + }, + Lint { + label: "const_likely", + description: r##"# `const_likely` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_location_fields", + description: r##"# `const_location_fields` + +The tracking issue for this feature is: [#102911] + +[#102911]: https://github.com/rust-lang/rust/issues/102911 + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_array_assume_init", + description: r##"# `const_maybe_uninit_array_assume_init` + +The tracking issue for this feature is: [#96097] + +[#96097]: https://github.com/rust-lang/rust/issues/96097 + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_as_mut_ptr", + description: r##"# `const_maybe_uninit_as_mut_ptr` + +The tracking issue for this feature is: [#75251] + +[#75251]: https://github.com/rust-lang/rust/issues/75251 + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_assume_init", + description: r##"# `const_maybe_uninit_assume_init` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_assume_init_read", + description: r##"# `const_maybe_uninit_assume_init_read` + +The tracking issue for this feature is: [#63567] + +[#63567]: https://github.com/rust-lang/rust/issues/63567 + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_uninit_array", + description: r##"# `const_maybe_uninit_uninit_array` + +The tracking issue for this feature is: [#96097] + +[#96097]: https://github.com/rust-lang/rust/issues/96097 + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_write", + description: r##"# `const_maybe_uninit_write` + +The tracking issue for this feature is: [#63567] + +[#63567]: https://github.com/rust-lang/rust/issues/63567 + +------------------------ +"##, + }, + Lint { + label: "const_maybe_uninit_zeroed", + description: r##"# `const_maybe_uninit_zeroed` + +The tracking issue for this feature is: [#91850] + +[#91850]: https://github.com/rust-lang/rust/issues/91850 + +------------------------ +"##, + }, + Lint { + label: "const_mut_refs", + description: r##"# `const_mut_refs` -The tracking issue for this feature is: [#67217] +The tracking issue for this feature is: [#57349] -[#67217]: https://github.com/rust-lang/rust/issues/67217 +[#57349]: https://github.com/rust-lang/rust/issues/57349 -The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`. +------------------------ +"##, + }, + Lint { + label: "const_nonnull_new", + description: r##"# `const_nonnull_new` + +The tracking issue for this feature is: [#93235] + +[#93235]: https://github.com/rust-lang/rust/issues/93235 + +------------------------ +"##, + }, + Lint { + label: "const_num_midpoint", + description: r##"# `const_num_midpoint` + +The tracking issue for this feature is: [#110840] + +[#110840]: https://github.com/rust-lang/rust/issues/110840 + +------------------------ +"##, + }, + Lint { + label: "const_option", + description: r##"# `const_option` + +The tracking issue for this feature is: [#67441] + +[#67441]: https://github.com/rust-lang/rust/issues/67441 + +------------------------ +"##, + }, + Lint { + label: "const_option_ext", + description: r##"# `const_option_ext` + +The tracking issue for this feature is: [#91930] + +[#91930]: https://github.com/rust-lang/rust/issues/91930 + +------------------------ +"##, + }, + Lint { + label: "const_pin", + description: r##"# `const_pin` + +The tracking issue for this feature is: [#76654] + +[#76654]: https://github.com/rust-lang/rust/issues/76654 + +------------------------ +"##, + }, + Lint { + label: "const_pointer_byte_offsets", + description: r##"# `const_pointer_byte_offsets` + +The tracking issue for this feature is: [#96283] + +[#96283]: https://github.com/rust-lang/rust/issues/96283 + +------------------------ +"##, + }, + Lint { + label: "const_pointer_is_aligned", + description: r##"# `const_pointer_is_aligned` + +The tracking issue for this feature is: [#104203] + +[#104203]: https://github.com/rust-lang/rust/issues/104203 + +------------------------ +"##, + }, + Lint { + label: "const_precise_live_drops", + description: r##"# `const_precise_live_drops` + +The tracking issue for this feature is: [#73255] + +[#73255]: https://github.com/rust-lang/rust/issues/73255 + +------------------------ +"##, + }, + Lint { + label: "const_pref_align_of", + description: r##"# `const_pref_align_of` + +The tracking issue for this feature is: [#91971] + +[#91971]: https://github.com/rust-lang/rust/issues/91971 + +------------------------ +"##, + }, + Lint { + label: "const_ptr_as_ref", + description: r##"# `const_ptr_as_ref` + +The tracking issue for this feature is: [#91822] + +[#91822]: https://github.com/rust-lang/rust/issues/91822 + +------------------------ +"##, + }, + Lint { + label: "const_ptr_is_null", + description: r##"# `const_ptr_is_null` + +The tracking issue for this feature is: [#74939] + +[#74939]: https://github.com/rust-lang/rust/issues/74939 + +------------------------ +"##, + }, + Lint { + label: "const_ptr_sub_ptr", + description: r##"# `const_ptr_sub_ptr` + +The tracking issue for this feature is: [#95892] + +[#95892]: https://github.com/rust-lang/rust/issues/95892 + +------------------------ +"##, + }, + Lint { + label: "const_ptr_write", + description: r##"# `const_ptr_write` + +The tracking issue for this feature is: [#86302] + +[#86302]: https://github.com/rust-lang/rust/issues/86302 + +------------------------ +"##, + }, + Lint { + label: "const_range_bounds", + description: r##"# `const_range_bounds` + +The tracking issue for this feature is: [#108082] + +[#108082]: https://github.com/rust-lang/rust/issues/108082 + +------------------------ +"##, + }, + Lint { + label: "const_raw_ptr_comparison", + description: r##"# `const_raw_ptr_comparison` + +The tracking issue for this feature is: [#53020] + +[#53020]: https://github.com/rust-lang/rust/issues/53020 + +------------------------ +"##, + }, + Lint { + label: "const_refs_to_cell", + description: r##"# `const_refs_to_cell` + +The tracking issue for this feature is: [#80384] + +[#80384]: https://github.com/rust-lang/rust/issues/80384 + +------------------------ +"##, + }, + Lint { + label: "const_replace", + description: r##"# `const_replace` + +The tracking issue for this feature is: [#83164] + +[#83164]: https://github.com/rust-lang/rust/issues/83164 + +------------------------ +"##, + }, + Lint { + label: "const_result", + description: r##"# `const_result` + +The tracking issue for this feature is: [#82814] + +[#82814]: https://github.com/rust-lang/rust/issues/82814 + +------------------------ +"##, + }, + Lint { + label: "const_size_of_val", + description: r##"# `const_size_of_val` + +The tracking issue for this feature is: [#46571] + +[#46571]: https://github.com/rust-lang/rust/issues/46571 + +------------------------ +"##, + }, + Lint { + label: "const_size_of_val_raw", + description: r##"# `const_size_of_val_raw` + +The tracking issue for this feature is: [#46571] + +[#46571]: https://github.com/rust-lang/rust/issues/46571 + +------------------------ +"##, + }, + Lint { + label: "const_slice_first_last", + description: r##"# `const_slice_first_last` + +The tracking issue for this feature is: [#83570] + +[#83570]: https://github.com/rust-lang/rust/issues/83570 + +------------------------ +"##, + }, + Lint { + label: "const_slice_from_mut_ptr_range", + description: r##"# `const_slice_from_mut_ptr_range` + +The tracking issue for this feature is: [#89792] + +[#89792]: https://github.com/rust-lang/rust/issues/89792 + +------------------------ +"##, + }, + Lint { + label: "const_slice_from_ptr_range", + description: r##"# `const_slice_from_ptr_range` + +The tracking issue for this feature is: [#89792] + +[#89792]: https://github.com/rust-lang/rust/issues/89792 + +------------------------ +"##, + }, + Lint { + label: "const_slice_from_raw_parts_mut", + description: r##"# `const_slice_from_raw_parts_mut` + +The tracking issue for this feature is: [#67456] + +[#67456]: https://github.com/rust-lang/rust/issues/67456 + +------------------------ +"##, + }, + Lint { + label: "const_slice_from_ref", + description: r##"# `const_slice_from_ref` + +The tracking issue for this feature is: [#90206] + +[#90206]: https://github.com/rust-lang/rust/issues/90206 + +------------------------ +"##, + }, + Lint { + label: "const_slice_index", + description: r##"# `const_slice_index` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "const_slice_ptr_len", + description: r##"# `const_slice_ptr_len` + +The tracking issue for this feature is: [#71146] + +[#71146]: https://github.com/rust-lang/rust/issues/71146 + +------------------------ +"##, + }, + Lint { + label: "const_slice_split_at_mut", + description: r##"# `const_slice_split_at_mut` + +The tracking issue for this feature is: [#101804] + +[#101804]: https://github.com/rust-lang/rust/issues/101804 + +------------------------ +"##, + }, + Lint { + label: "const_str_from_utf8", + description: r##"# `const_str_from_utf8` + +The tracking issue for this feature is: [#91006] + +[#91006]: https://github.com/rust-lang/rust/issues/91006 + +------------------------ +"##, + }, + Lint { + label: "const_str_from_utf8_unchecked_mut", + description: r##"# `const_str_from_utf8_unchecked_mut` + +The tracking issue for this feature is: [#91005] + +[#91005]: https://github.com/rust-lang/rust/issues/91005 + +------------------------ +"##, + }, + Lint { + label: "const_swap", + description: r##"# `const_swap` + +The tracking issue for this feature is: [#83163] + +[#83163]: https://github.com/rust-lang/rust/issues/83163 + +------------------------ +"##, + }, + Lint { + label: "const_trait_impl", + description: r##"# `const_trait_impl` + +The tracking issue for this feature is: [#67792] + +[#67792]: https://github.com/rust-lang/rust/issues/67792 + +------------------------ +"##, + }, + Lint { + label: "const_try", + description: r##"# `const_try` + +The tracking issue for this feature is: [#74935] + +[#74935]: https://github.com/rust-lang/rust/issues/74935 + +------------------------ +"##, + }, + Lint { + label: "const_type_id", + description: r##"# `const_type_id` + +The tracking issue for this feature is: [#77125] + +[#77125]: https://github.com/rust-lang/rust/issues/77125 + +------------------------ +"##, + }, + Lint { + label: "const_type_name", + description: r##"# `const_type_name` + +The tracking issue for this feature is: [#63084] + +[#63084]: https://github.com/rust-lang/rust/issues/63084 + +------------------------ +"##, + }, + Lint { + label: "const_unicode_case_lookup", + description: r##"# `const_unicode_case_lookup` + +The tracking issue for this feature is: [#101400] + +[#101400]: https://github.com/rust-lang/rust/issues/101400 + +------------------------ +"##, + }, + Lint { + label: "const_unsafecell_get_mut", + description: r##"# `const_unsafecell_get_mut` + +The tracking issue for this feature is: [#88836] + +[#88836]: https://github.com/rust-lang/rust/issues/88836 + +------------------------ +"##, + }, + Lint { + label: "const_waker", + description: r##"# `const_waker` + +The tracking issue for this feature is: [#102012] + +[#102012]: https://github.com/rust-lang/rust/issues/102012 + +------------------------ +"##, + }, + Lint { + label: "container_error_extra", + description: r##"# `container_error_extra` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "control_flow_enum", + description: r##"# `control_flow_enum` + +The tracking issue for this feature is: [#75744] + +[#75744]: https://github.com/rust-lang/rust/issues/75744 + +------------------------ +"##, + }, + Lint { + label: "convert_float_to_int", + description: r##"# `convert_float_to_int` + +The tracking issue for this feature is: [#67057] + +[#67057]: https://github.com/rust-lang/rust/issues/67057 + +------------------------ "##, }, Lint { @@ -1737,30 +3815,115 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { - label: "crate_visibility_modifier", - description: r##"# `crate_visibility_modifier` + label: "coverage_attribute", + description: r##"# `coverage_attribute` -The tracking issue for this feature is: [#53120] +The tracking issue for this feature is: [#84605] -[#53120]: https://github.com/rust-lang/rust/issues/53120 +[#84605]: https://github.com/rust-lang/rust/issues/84605 ------ +--- -The `crate_visibility_modifier` feature allows the `crate` keyword to be used -as a visibility modifier synonymous to `pub(crate)`, indicating that a type -(function, _&c._) is to be visible to the entire enclosing crate, but not to -other crates. +The `coverage` attribute can be used to selectively disable coverage +instrumentation in an annotated function. This might be useful to: + +- Avoid instrumentation overhead in a performance critical function +- Avoid generating coverage for a function that is not meant to be executed, + but still target 100% coverage for the rest of the program. + +## Example ```rust -#![feature(crate_visibility_modifier)] +#![feature(coverage_attribute)] -crate struct Foo { - bar: usize, +// `foo()` will get coverage instrumentation (by default) +fn foo() { + // ... +} + +#[coverage(off)] +fn bar() { + // ... } ``` "##, }, Lint { + label: "cow_is_borrowed", + description: r##"# `cow_is_borrowed` + +The tracking issue for this feature is: [#65143] + +[#65143]: https://github.com/rust-lang/rust/issues/65143 + +------------------------ +"##, + }, + Lint { + label: "csky_target_feature", + description: r##"# `csky_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "cstr_count_bytes", + description: r##"# `cstr_count_bytes` + +The tracking issue for this feature is: [#114441] + +[#114441]: https://github.com/rust-lang/rust/issues/114441 + +------------------------ +"##, + }, + Lint { + label: "cursor_remaining", + description: r##"# `cursor_remaining` + +The tracking issue for this feature is: [#86369] + +[#86369]: https://github.com/rust-lang/rust/issues/86369 + +------------------------ +"##, + }, + Lint { + label: "custom_code_classes_in_docs", + description: r##"# `custom_code_classes_in_docs` + +The tracking issue for this feature is: [#79483] + +[#79483]: https://github.com/rust-lang/rust/issues/79483 + +------------------------ +"##, + }, + Lint { + label: "custom_inner_attributes", + description: r##"# `custom_inner_attributes` + +The tracking issue for this feature is: [#54726] + +[#54726]: https://github.com/rust-lang/rust/issues/54726 + +------------------------ +"##, + }, + Lint { + label: "custom_mir", + description: r##"# `custom_mir` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { label: "custom_test_frameworks", description: r##"# `custom_test_frameworks` @@ -1797,6 +3960,17 @@ const WILL_FAIL: i32 = 4; "##, }, Lint { + label: "deadline_api", + description: r##"# `deadline_api` + +The tracking issue for this feature is: [#46316] + +[#46316]: https://github.com/rust-lang/rust/issues/46316 + +------------------------ +"##, + }, + Lint { label: "dec2flt", description: r##"# `dec2flt` @@ -1806,54 +3980,47 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { - label: "default_free_fn", - description: r##"# `default_free_fn` + label: "decl_macro", + description: r##"# `decl_macro` -The tracking issue for this feature is: [#73014] +The tracking issue for this feature is: [#39412] -[#73014]: https://github.com/rust-lang/rust/issues/73014 +[#39412]: https://github.com/rust-lang/rust/issues/39412 ------------------------ +"##, + }, + Lint { + label: "default_type_parameter_fallback", + description: r##"# `default_type_parameter_fallback` -Adds a free `default()` function to the `std::default` module. This function -just forwards to [`Default::default()`], but may remove repetition of the word -"default" from the call site. +The tracking issue for this feature is: [#27336] -[`Default::default()`]: https://doc.rust-lang.org/nightly/std/default/trait.Default.html#tymethod.default +[#27336]: https://github.com/rust-lang/rust/issues/27336 -Here is an example: +------------------------ +"##, + }, + Lint { + label: "deprecated_safe", + description: r##"# `deprecated_safe` -```rust -#![feature(default_free_fn)] -use std::default::default; +The tracking issue for this feature is: [#94978] -#[derive(Default)] -struct AppConfig { - foo: FooConfig, - bar: BarConfig, -} +[#94978]: https://github.com/rust-lang/rust/issues/94978 -#[derive(Default)] -struct FooConfig { - foo: i32, -} +------------------------ +"##, + }, + Lint { + label: "deprecated_suggestion", + description: r##"# `deprecated_suggestion` -#[derive(Default)] -struct BarConfig { - bar: f32, - baz: u8, -} +The tracking issue for this feature is: [#94785] -fn main() { - let options = AppConfig { - foo: default(), - bar: BarConfig { - bar: 10.1, - ..default() - }, - }; -} -``` +[#94785]: https://github.com/rust-lang/rust/issues/94785 + +------------------------ "##, }, Lint { @@ -1866,6 +4033,15 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "derive_const", + description: r##"# `derive_const` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { label: "derive_eq", description: r##"# `derive_eq` @@ -1875,6 +4051,79 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "diagnostic_namespace", + description: r##"# `diagnostic_namespace` + +The tracking issue for this feature is: [#111996] + +[#111996]: https://github.com/rust-lang/rust/issues/111996 + +------------------------ +"##, + }, + Lint { + label: "dir_entry_ext2", + description: r##"# `dir_entry_ext2` + +The tracking issue for this feature is: [#85573] + +[#85573]: https://github.com/rust-lang/rust/issues/85573 + +------------------------ +"##, + }, + Lint { + label: "discriminant_kind", + description: r##"# `discriminant_kind` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "dispatch_from_dyn", + description: r##"# `dispatch_from_dyn` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "div_duration", + description: r##"# `div_duration` + +The tracking issue for this feature is: [#63139] + +[#63139]: https://github.com/rust-lang/rust/issues/63139 + +------------------------ +"##, + }, + Lint { + label: "do_not_recommend", + description: r##"# `do_not_recommend` + +The tracking issue for this feature is: [#51992] + +[#51992]: https://github.com/rust-lang/rust/issues/51992 + +------------------------ +"##, + }, + Lint { + label: "doc_auto_cfg", + description: r##"# `doc_auto_cfg` + +The tracking issue for this feature is: [#43781] + +[#43781]: https://github.com/rust-lang/rust/issues/43781 + +------------------------ +"##, + }, + Lint { label: "doc_cfg", description: r##"# `doc_cfg` @@ -1885,7 +4134,7 @@ The tracking issue for this feature is: [#43781] The `doc_cfg` feature allows an API be documented as only available in some specific platforms. This attribute has two effects: -1. In the annotated item's documentation, there will be a message saying "This is supported on +1. In the annotated item's documentation, there will be a message saying "Available on (platform) only". 2. The item's doc-tests will only run on the specific platform. @@ -1925,6 +4174,17 @@ pub struct Icon { "##, }, Lint { + label: "doc_cfg_hide", + description: r##"# `doc_cfg_hide` + +The tracking issue for this feature is: [#43781] + +[#43781]: https://github.com/rust-lang/rust/issues/43781 + +------------------------ +"##, + }, + Lint { label: "doc_masked", description: r##"# `doc_masked` @@ -1990,6 +4250,180 @@ See also its documentation in [the rustdoc book][rustdoc-book-notable_trait]. "##, }, Lint { + label: "downcast_unchecked", + description: r##"# `downcast_unchecked` + +The tracking issue for this feature is: [#90850] + +[#90850]: https://github.com/rust-lang/rust/issues/90850 + +------------------------ +"##, + }, + Lint { + label: "drain_keep_rest", + description: r##"# `drain_keep_rest` + +The tracking issue for this feature is: [#101122] + +[#101122]: https://github.com/rust-lang/rust/issues/101122 + +------------------------ +"##, + }, + Lint { + label: "dropck_eyepatch", + description: r##"# `dropck_eyepatch` + +The tracking issue for this feature is: [#34761] + +[#34761]: https://github.com/rust-lang/rust/issues/34761 + +------------------------ +"##, + }, + Lint { + label: "duration_constants", + description: r##"# `duration_constants` + +The tracking issue for this feature is: [#57391] + +[#57391]: https://github.com/rust-lang/rust/issues/57391 + +------------------------ +"##, + }, + Lint { + label: "duration_consts_float", + description: r##"# `duration_consts_float` + +The tracking issue for this feature is: [#72440] + +[#72440]: https://github.com/rust-lang/rust/issues/72440 + +------------------------ +"##, + }, + Lint { + label: "dyn_star", + description: r##"# `dyn_star` + +The tracking issue for this feature is: [#102425] + +[#102425]: https://github.com/rust-lang/rust/issues/102425 + +------------------------ +"##, + }, + Lint { + label: "edition_panic", + description: r##"# `edition_panic` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "effects", + description: r##"# `effects` + +The tracking issue for this feature is: [#102090] + +[#102090]: https://github.com/rust-lang/rust/issues/102090 + +------------------------ +"##, + }, + Lint { + label: "entry_insert", + description: r##"# `entry_insert` + +The tracking issue for this feature is: [#65225] + +[#65225]: https://github.com/rust-lang/rust/issues/65225 + +------------------------ +"##, + }, + Lint { + label: "ermsb_target_feature", + description: r##"# `ermsb_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "error_generic_member_access", + description: r##"# `error_generic_member_access` + +The tracking issue for this feature is: [#99301] + +[#99301]: https://github.com/rust-lang/rust/issues/99301 + +------------------------ +"##, + }, + Lint { + label: "error_in_core", + description: r##"# `error_in_core` + +The tracking issue for this feature is: [#103765] + +[#103765]: https://github.com/rust-lang/rust/issues/103765 + +------------------------ +"##, + }, + Lint { + label: "error_iter", + description: r##"# `error_iter` + +The tracking issue for this feature is: [#58520] + +[#58520]: https://github.com/rust-lang/rust/issues/58520 + +------------------------ +"##, + }, + Lint { + label: "error_reporter", + description: r##"# `error_reporter` + +The tracking issue for this feature is: [#90172] + +[#90172]: https://github.com/rust-lang/rust/issues/90172 + +------------------------ +"##, + }, + Lint { + label: "error_type_id", + description: r##"# `error_type_id` + +The tracking issue for this feature is: [#60784] + +[#60784]: https://github.com/rust-lang/rust/issues/60784 + +------------------------ +"##, + }, + Lint { + label: "exact_size_is_empty", + description: r##"# `exact_size_is_empty` + +The tracking issue for this feature is: [#35428] + +[#35428]: https://github.com/rust-lang/rust/issues/35428 + +------------------------ +"##, + }, + Lint { label: "exclusive_range_pattern", description: r##"# `exclusive_range_pattern` @@ -2020,60 +4454,105 @@ stabilized. "##, }, Lint { - label: "explicit_generic_args_with_impl_trait", - description: r##"# `explicit_generic_args_with_impl_trait` + label: "exclusive_wrapper", + description: r##"# `exclusive_wrapper` -The tracking issue for this feature is: [#83701] +The tracking issue for this feature is: [#98407] -[#83701]: https://github.com/rust-lang/rust/issues/83701 +[#98407]: https://github.com/rust-lang/rust/issues/98407 ------------------------ +"##, + }, + Lint { + label: "exhaustive_patterns", + description: r##"# `exhaustive_patterns` -The `explicit_generic_args_with_impl_trait` feature gate lets you specify generic arguments even -when `impl Trait` is used in argument position. +The tracking issue for this feature is: [#51085] -A simple example is: +[#51085]: https://github.com/rust-lang/rust/issues/51085 -```rust -#![feature(explicit_generic_args_with_impl_trait)] +------------------------ +"##, + }, + Lint { + label: "exit_status_error", + description: r##"# `exit_status_error` -fn foo<T: ?Sized>(_f: impl AsRef<T>) {} +The tracking issue for this feature is: [#84908] -fn main() { - foo::<str>("".to_string()); -} -``` +[#84908]: https://github.com/rust-lang/rust/issues/84908 -This is currently rejected: +------------------------ +"##, + }, + Lint { + label: "exitcode_exit_method", + description: r##"# `exitcode_exit_method` -```text -error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position - --> src/main.rs:6:11 - | -6 | foo::<str>("".to_string()); - | ^^^ explicit generic argument not allowed +The tracking issue for this feature is: [#97100] -``` +[#97100]: https://github.com/rust-lang/rust/issues/97100 -However it would compile if `explicit_generic_args_with_impl_trait` is enabled. +------------------------ +"##, + }, + Lint { + label: "explicit_tail_calls", + description: r##"# `explicit_tail_calls` -Note that the synthetic type parameters from `impl Trait` are still implicit and you -cannot explicitly specify these: +The tracking issue for this feature is: [#112788] -```rust,compile_fail -#![feature(explicit_generic_args_with_impl_trait)] +[#112788]: https://github.com/rust-lang/rust/issues/112788 -fn foo<T: ?Sized>(_f: impl AsRef<T>) {} -fn bar<T: ?Sized, F: AsRef<T>>(_f: F) {} +------------------------ +"##, + }, + Lint { + label: "extend_one", + description: r##"# `extend_one` -fn main() { - bar::<str, _>("".to_string()); // Okay - bar::<str, String>("".to_string()); // Okay +The tracking issue for this feature is: [#72631] - foo::<str>("".to_string()); // Okay - foo::<str, String>("".to_string()); // Error, you cannot specify `impl Trait` explicitly -} -``` +[#72631]: https://github.com/rust-lang/rust/issues/72631 + +------------------------ +"##, + }, + Lint { + label: "extended_varargs_abi_support", + description: r##"# `extended_varargs_abi_support` + +The tracking issue for this feature is: [#100189] + +[#100189]: https://github.com/rust-lang/rust/issues/100189 + +------------------------ + +This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling +conventions on functions with varargs. +"##, + }, + Lint { + label: "extern_types", + description: r##"# `extern_types` + +The tracking issue for this feature is: [#43467] + +[#43467]: https://github.com/rust-lang/rust/issues/43467 + +------------------------ +"##, + }, + Lint { + label: "extract_if", + description: r##"# `extract_if` + +The tracking issue for this feature is: [#43244] + +[#43244]: https://github.com/rust-lang/rust/issues/43244 + +------------------------ "##, }, Lint { @@ -2211,6 +4690,72 @@ against are compatible with those of the `#[ffi_pure]`. "##, }, Lint { + label: "ffi_returns_twice", + description: r##"# `ffi_returns_twice` + +The tracking issue for this feature is: [#58314] + +[#58314]: https://github.com/rust-lang/rust/issues/58314 + +------------------------ +"##, + }, + Lint { + label: "file_create_new", + description: r##"# `file_create_new` + +The tracking issue for this feature is: [#105135] + +[#105135]: https://github.com/rust-lang/rust/issues/105135 + +------------------------ +"##, + }, + Lint { + label: "file_set_times", + description: r##"# `file_set_times` + +The tracking issue for this feature is: [#98245] + +[#98245]: https://github.com/rust-lang/rust/issues/98245 + +------------------------ +"##, + }, + Lint { + label: "float_gamma", + description: r##"# `float_gamma` + +The tracking issue for this feature is: [#99842] + +[#99842]: https://github.com/rust-lang/rust/issues/99842 + +------------------------ +"##, + }, + Lint { + label: "float_minimum_maximum", + description: r##"# `float_minimum_maximum` + +The tracking issue for this feature is: [#91079] + +[#91079]: https://github.com/rust-lang/rust/issues/91079 + +------------------------ +"##, + }, + Lint { + label: "float_next_up_down", + description: r##"# `float_next_up_down` + +The tracking issue for this feature is: [#91399] + +[#91399]: https://github.com/rust-lang/rust/issues/91399 + +------------------------ +"##, + }, + Lint { label: "flt2dec", description: r##"# `flt2dec` @@ -2220,6 +4765,15 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "fmt_helpers_for_derive", + description: r##"# `fmt_helpers_for_derive` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { label: "fmt_internals", description: r##"# `fmt_internals` @@ -2229,6 +4783,26 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "fn_align", + description: r##"# `fn_align` + +The tracking issue for this feature is: [#82232] + +[#82232]: https://github.com/rust-lang/rust/issues/82232 + +------------------------ +"##, + }, + Lint { + label: "fn_ptr_trait", + description: r##"# `fn_ptr_trait` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { label: "fn_traits", description: r##"# `fn_traits` @@ -2243,7 +4817,7 @@ See Also: [`unboxed_closures`](../language-features/unboxed-closures.md) The `fn_traits` feature allows for implementation of the [`Fn*`] traits for creating custom closure-like types. -[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html +[`Fn*`]: ../../std/ops/trait.Fn.html ```rust #![feature(unboxed_closures)] @@ -2268,6 +4842,90 @@ fn main() { "##, }, Lint { + label: "forget_unsized", + description: r##"# `forget_unsized` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "format_args_nl", + description: r##"# `format_args_nl` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "fs_try_exists", + description: r##"# `fs_try_exists` + +The tracking issue for this feature is: [#83186] + +[#83186]: https://github.com/rust-lang/rust/issues/83186 + +------------------------ +"##, + }, + Lint { + label: "fundamental", + description: r##"# `fundamental` + +The tracking issue for this feature is: [#29635] + +[#29635]: https://github.com/rust-lang/rust/issues/29635 + +------------------------ +"##, + }, + Lint { + label: "future_join", + description: r##"# `future_join` + +The tracking issue for this feature is: [#91642] + +[#91642]: https://github.com/rust-lang/rust/issues/91642 + +------------------------ +"##, + }, + Lint { + label: "gen_future", + description: r##"# `gen_future` + +The tracking issue for this feature is: [#50547] + +[#50547]: https://github.com/rust-lang/rust/issues/50547 + +------------------------ +"##, + }, + Lint { + label: "generator_clone", + description: r##"# `generator_clone` + +The tracking issue for this feature is: [#95360] + +[#95360]: https://github.com/rust-lang/rust/issues/95360 + +------------------------ +"##, + }, + Lint { + label: "generator_trait", + description: r##"# `generator_trait` + +The tracking issue for this feature is: [#43122] + +[#43122]: https://github.com/rust-lang/rust/issues/43122 + +------------------------ +"##, + }, + Lint { label: "generators", description: r##"# `generators` @@ -2518,82 +5176,253 @@ does. "##, }, Lint { - label: "half_open_range_patterns", - description: r##"# `half_open_range_patterns` + label: "generic_arg_infer", + description: r##"# `generic_arg_infer` + +The tracking issue for this feature is: [#85077] + +[#85077]: https://github.com/rust-lang/rust/issues/85077 + +------------------------ +"##, + }, + Lint { + label: "generic_assert", + description: r##"# `generic_assert` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "generic_assert_internals", + description: r##"# `generic_assert_internals` + +The tracking issue for this feature is: [#44838] + +[#44838]: https://github.com/rust-lang/rust/issues/44838 + +------------------------ +"##, + }, + Lint { + label: "generic_associated_types_extended", + description: r##"# `generic_associated_types_extended` + +The tracking issue for this feature is: [#95451] + +[#95451]: https://github.com/rust-lang/rust/issues/95451 + +------------------------ +"##, + }, + Lint { + label: "generic_const_exprs", + description: r##"# `generic_const_exprs` + +The tracking issue for this feature is: [#76560] + +[#76560]: https://github.com/rust-lang/rust/issues/76560 + +------------------------ +"##, + }, + Lint { + label: "generic_const_items", + description: r##"# `generic_const_items` + +The tracking issue for this feature is: [#113521] + +[#113521]: https://github.com/rust-lang/rust/issues/113521 + +------------------------ +"##, + }, + Lint { + label: "get_many_mut", + description: r##"# `get_many_mut` + +The tracking issue for this feature is: [#104642] + +[#104642]: https://github.com/rust-lang/rust/issues/104642 + +------------------------ +"##, + }, + Lint { + label: "get_mut_unchecked", + description: r##"# `get_mut_unchecked` + +The tracking issue for this feature is: [#63292] + +[#63292]: https://github.com/rust-lang/rust/issues/63292 + +------------------------ +"##, + }, + Lint { + label: "half_open_range_patterns_in_slices", + description: r##"# `half_open_range_patterns_in_slices` The tracking issue for this feature is: [#67264] -It is part of the `#![exclusive_range_pattern]` feature, +It is part of the `exclusive_range_pattern` feature, tracked at [#37854]. [#67264]: https://github.com/rust-lang/rust/issues/67264 [#37854]: https://github.com/rust-lang/rust/issues/37854 ----- -The `half_open_range_patterns` feature allows RangeTo patterns -(`..10`) to be used in appropriate pattern matching contexts. -This requires also enabling the `exclusive_range_pattern` feature. - -It also enabled RangeFrom patterns but that has since been -stabilized. +This feature allow using top-level half-open range patterns in slices. ```rust -#![feature(half_open_range_patterns)] +#![feature(half_open_range_patterns_in_slices)] #![feature(exclusive_range_pattern)] - let x = 5; - match x { - ..0 => println!("negative!"), // "RangeTo" pattern. Unstable. - 0 => println!("zero!"), - 1.. => println!("positive!"), // "RangeFrom" pattern. Stable. - } + +fn main() { + let xs = [13, 1, 5, 2, 3, 1, 21, 8]; + let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs else { return; }; +} +``` + +Note that this feature is not required if the patterns are wrapped between parenthesis. + +```rust +fn main() { + let xs = [13, 1]; + let [(a @ 3..), c] = xs else { return; }; +} ``` "##, }, Lint { - label: "infer_static_outlives_requirements", - description: r##"# `infer_static_outlives_requirements` + label: "hash_extract_if", + description: r##"# `hash_extract_if` -The tracking issue for this feature is: [#54185] +The tracking issue for this feature is: [#59618] -[#54185]: https://github.com/rust-lang/rust/issues/54185 +[#59618]: https://github.com/rust-lang/rust/issues/59618 ------------------------ -The `infer_static_outlives_requirements` feature indicates that certain -`'static` outlives requirements can be inferred by the compiler rather than -stating them explicitly. +"##, + }, + Lint { + label: "hash_raw_entry", + description: r##"# `hash_raw_entry` -Note: It is an accompanying feature to `infer_outlives_requirements`, -which must be enabled to infer outlives requirements. +The tracking issue for this feature is: [#56167] -For example, currently generic struct definitions that contain -references, require where-clauses of the form T: 'static. By using -this feature the outlives predicates will be inferred, although -they may still be written explicitly. +[#56167]: https://github.com/rust-lang/rust/issues/56167 -```rust,ignore (pseudo-Rust) -struct Foo<U> where U: 'static { // <-- currently required - bar: Bar<U> -} -struct Bar<T: 'static> { - x: T, -} -``` +------------------------ +"##, + }, + Lint { + label: "hash_set_entry", + description: r##"# `hash_set_entry` +The tracking issue for this feature is: [#60896] -## Examples: +[#60896]: https://github.com/rust-lang/rust/issues/60896 -```rust,ignore (pseudo-Rust) -#![feature(infer_outlives_requirements)] -#![feature(infer_static_outlives_requirements)] +------------------------ +"##, + }, + Lint { + label: "hasher_prefixfree_extras", + description: r##"# `hasher_prefixfree_extras` -#[rustc_outlives] -// Implicitly infer U: 'static -struct Foo<U> { - bar: Bar<U> -} -struct Bar<T: 'static> { - x: T, -} -``` +The tracking issue for this feature is: [#96762] + +[#96762]: https://github.com/rust-lang/rust/issues/96762 + +------------------------ +"##, + }, + Lint { + label: "hashmap_internals", + description: r##"# `hashmap_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "hexagon_target_feature", + description: r##"# `hexagon_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "hint_must_use", + description: r##"# `hint_must_use` + +The tracking issue for this feature is: [#94745] + +[#94745]: https://github.com/rust-lang/rust/issues/94745 + +------------------------ +"##, + }, + Lint { + label: "if_let_guard", + description: r##"# `if_let_guard` + +The tracking issue for this feature is: [#51114] + +[#51114]: https://github.com/rust-lang/rust/issues/51114 + +------------------------ +"##, + }, + Lint { + label: "impl_trait_in_assoc_type", + description: r##"# `impl_trait_in_assoc_type` + +The tracking issue for this feature is: [#63063] + +[#63063]: https://github.com/rust-lang/rust/issues/63063 + +------------------------ +"##, + }, + Lint { + label: "impl_trait_in_fn_trait_return", + description: r##"# `impl_trait_in_fn_trait_return` + +The tracking issue for this feature is: [#99697] + +[#99697]: https://github.com/rust-lang/rust/issues/99697 + +------------------------ +"##, + }, + Lint { + label: "imported_main", + description: r##"# `imported_main` + +The tracking issue for this feature is: [#28937] + +[#28937]: https://github.com/rust-lang/rust/issues/28937 + +------------------------ +"##, + }, + Lint { + label: "inherent_associated_types", + description: r##"# `inherent_associated_types` + +The tracking issue for this feature is: [#8995] + +[#8995]: https://github.com/rust-lang/rust/issues/8995 + +------------------------ "##, }, Lint { @@ -2661,10 +5490,41 @@ match some_int { "##, }, Lint { - label: "int_error_internals", - description: r##"# `int_error_internals` + label: "inplace_iteration", + description: r##"# `inplace_iteration` -This feature is internal to the Rust compiler and is not intended for general use. +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "int_roundings", + description: r##"# `int_roundings` + +The tracking issue for this feature is: [#88581] + +[#88581]: https://github.com/rust-lang/rust/issues/88581 + +------------------------ +"##, + }, + Lint { + label: "integer_atomics", + description: r##"# `integer_atomics` + +The tracking issue for this feature is: [#99069] + +[#99069]: https://github.com/rust-lang/rust/issues/99069 + +------------------------ +"##, + }, + Lint { + label: "internal_impls_macro", + description: r##"# `internal_impls_macro` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -2718,12 +5578,13 @@ via a declaration like ```rust #![feature(intrinsics)] +#![allow(internal_features)] # fn main() {} extern "rust-intrinsic" { fn transmute<T, U>(x: T) -> U; - fn offset<T>(dst: *const T, offset: isize) -> *const T; + fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; } ``` @@ -2731,6 +5592,92 @@ As with any other FFI functions, these are always `unsafe` to call. "##, }, Lint { + label: "io_error_downcast", + description: r##"# `io_error_downcast` + +The tracking issue for this feature is: [#99262] + +[#99262]: https://github.com/rust-lang/rust/issues/99262 + +------------------------ +"##, + }, + Lint { + label: "io_error_more", + description: r##"# `io_error_more` + +The tracking issue for this feature is: [#86442] + +[#86442]: https://github.com/rust-lang/rust/issues/86442 + +------------------------ +"##, + }, + Lint { + label: "io_error_uncategorized", + description: r##"# `io_error_uncategorized` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "io_slice_advance", + description: r##"# `io_slice_advance` + +The tracking issue for this feature is: [#62726] + +[#62726]: https://github.com/rust-lang/rust/issues/62726 + +------------------------ +"##, + }, + Lint { + label: "ip", + description: r##"# `ip` + +The tracking issue for this feature is: [#27709] + +[#27709]: https://github.com/rust-lang/rust/issues/27709 + +------------------------ +"##, + }, + Lint { + label: "ip_bits", + description: r##"# `ip_bits` + +The tracking issue for this feature is: [#113744] + +[#113744]: https://github.com/rust-lang/rust/issues/113744 + +------------------------ +"##, + }, + Lint { + label: "ip_in_core", + description: r##"# `ip_in_core` + +The tracking issue for this feature is: [#108443] + +[#108443]: https://github.com/rust-lang/rust/issues/108443 + +------------------------ +"##, + }, + Lint { + label: "is_ascii_octdigit", + description: r##"# `is_ascii_octdigit` + +The tracking issue for this feature is: [#101288] + +[#101288]: https://github.com/rust-lang/rust/issues/101288 + +------------------------ +"##, + }, + Lint { label: "is_sorted", description: r##"# `is_sorted` @@ -2746,6 +5693,160 @@ add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to "##, }, Lint { + label: "isqrt", + description: r##"# `isqrt` + +The tracking issue for this feature is: [#116226] + +[#116226]: https://github.com/rust-lang/rust/issues/116226 + +------------------------ +"##, + }, + Lint { + label: "iter_advance_by", + description: r##"# `iter_advance_by` + +The tracking issue for this feature is: [#77404] + +[#77404]: https://github.com/rust-lang/rust/issues/77404 + +------------------------ +"##, + }, + Lint { + label: "iter_array_chunks", + description: r##"# `iter_array_chunks` + +The tracking issue for this feature is: [#100450] + +[#100450]: https://github.com/rust-lang/rust/issues/100450 + +------------------------ +"##, + }, + Lint { + label: "iter_collect_into", + description: r##"# `iter_collect_into` + +The tracking issue for this feature is: [#94780] + +[#94780]: https://github.com/rust-lang/rust/issues/94780 + +------------------------ +"##, + }, + Lint { + label: "iter_from_generator", + description: r##"# `iter_from_generator` + +The tracking issue for this feature is: [#43122] + +[#43122]: https://github.com/rust-lang/rust/issues/43122 + +------------------------ +"##, + }, + Lint { + label: "iter_intersperse", + description: r##"# `iter_intersperse` + +The tracking issue for this feature is: [#79524] + +[#79524]: https://github.com/rust-lang/rust/issues/79524 + +------------------------ +"##, + }, + Lint { + label: "iter_is_partitioned", + description: r##"# `iter_is_partitioned` + +The tracking issue for this feature is: [#62544] + +[#62544]: https://github.com/rust-lang/rust/issues/62544 + +------------------------ +"##, + }, + Lint { + label: "iter_map_windows", + description: r##"# `iter_map_windows` + +The tracking issue for this feature is: [#87155] + +[#87155]: https://github.com/rust-lang/rust/issues/87155 + +------------------------ +"##, + }, + Lint { + label: "iter_next_chunk", + description: r##"# `iter_next_chunk` + +The tracking issue for this feature is: [#98326] + +[#98326]: https://github.com/rust-lang/rust/issues/98326 + +------------------------ +"##, + }, + Lint { + label: "iter_order_by", + description: r##"# `iter_order_by` + +The tracking issue for this feature is: [#64295] + +[#64295]: https://github.com/rust-lang/rust/issues/64295 + +------------------------ +"##, + }, + Lint { + label: "iter_partition_in_place", + description: r##"# `iter_partition_in_place` + +The tracking issue for this feature is: [#62543] + +[#62543]: https://github.com/rust-lang/rust/issues/62543 + +------------------------ +"##, + }, + Lint { + label: "iter_repeat_n", + description: r##"# `iter_repeat_n` + +The tracking issue for this feature is: [#104434] + +[#104434]: https://github.com/rust-lang/rust/issues/104434 + +------------------------ +"##, + }, + Lint { + label: "iterator_try_collect", + description: r##"# `iterator_try_collect` + +The tracking issue for this feature is: [#94047] + +[#94047]: https://github.com/rust-lang/rust/issues/94047 + +------------------------ +"##, + }, + Lint { + label: "iterator_try_reduce", + description: r##"# `iterator_try_reduce` + +The tracking issue for this feature is: [#87053] + +[#87053]: https://github.com/rust-lang/rust/issues/87053 + +------------------------ +"##, + }, + Lint { label: "lang_items", description: r##"# `lang_items` @@ -2758,304 +5859,190 @@ functionality that isn't hard-coded into the language, but is implemented in libraries, with a special marker to tell the compiler it exists. The marker is the attribute `#[lang = "..."]` and there are various different values of `...`, i.e. various different 'lang -items'. +items'. Most of them can only be defined once. -For example, `Box` pointers require two lang items, one for allocation -and one for deallocation. A freestanding program that uses the `Box` -sugar for dynamic allocations via `malloc` and `free`: +Lang items are loaded lazily by the compiler; e.g. if one never uses `Box` +then there is no need to define a function for `exchange_malloc`. +`rustc` will emit an error when an item is needed but not found in the current +crate or any that it depends on. + +Some features provided by lang items: + +- overloadable operators via traits: the traits corresponding to the + `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all + marked with lang items; those specific four are `eq`, `partial_ord`, + `deref`/`deref_mut`, and `add` respectively. +- panicking: the `panic` and `panic_impl` lang items, among others. +- stack unwinding: the lang item `eh_personality` is a function used by the + failure mechanisms of the compiler. This is often mapped to GCC's personality + function (see the [`std` implementation][personality] for more information), + but programs which don't trigger a panic can be assured that this function is + never called. Additionally, a `eh_catch_typeinfo` static is needed for certain + targets which implement Rust panics on top of C++ exceptions. +- the traits in `core::marker` used to indicate types of + various kinds; e.g. lang items `sized`, `sync` and `copy`. +- memory allocation, see below. + +Most lang items are defined by `core`, but if you're trying to build +an executable without the `std` crate, you might run into the need +for lang item definitions. + +[personality]: https://github.com/rust-lang/rust/blob/master/library/std/src/sys/personality/gcc.rs + +## Example: Implementing a `Box` + +`Box` pointers require two lang items: one for the type itself and one for +allocation. A freestanding program that uses the `Box` sugar for dynamic +allocations via `malloc` and `free`: ```rust,ignore (libc-is-finicky) -#![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)] +#![feature(lang_items, start, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)] +#![allow(internal_features)] #![no_std] + +extern crate libc; +extern crate unwind; + +use core::ffi::c_void; use core::intrinsics; use core::panic::PanicInfo; +use core::ptr::NonNull; -extern crate libc; +pub struct Global; // the global allocator +struct Unique<T>(NonNull<T>); #[lang = "owned_box"] -pub struct Box<T>(*mut T); +pub struct Box<T, A = Global>(Unique<T>, A); + +impl<T> Box<T> { + pub fn new(x: T) -> Self { + #[rustc_box] + Box::new(x) + } +} + +impl<T, A> Drop for Box<T, A> { + fn drop(&mut self) { + unsafe { + libc::free(self.0.0.as_ptr() as *mut c_void); + } + } +} #[lang = "exchange_malloc"] unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { - let p = libc::malloc(size as libc::size_t) as *mut u8; + let p = libc::malloc(size) as *mut u8; // Check if `malloc` failed: - if p as usize == 0 { + if p.is_null() { intrinsics::abort(); } p } -#[lang = "box_free"] -unsafe fn box_free<T: ?Sized>(ptr: *mut T) { - libc::free(ptr as *mut libc::c_void) -} - #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - let _x = box 1; + let _x = Box::new(1); 0 } -#[lang = "eh_personality"] extern fn rust_eh_personality() {} -#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } } -#[no_mangle] pub extern fn rust_eh_register_frames () {} -#[no_mangle] pub extern fn rust_eh_unregister_frames () {} +#[lang = "eh_personality"] +fn rust_eh_personality() {} + +#[panic_handler] +fn panic_handler(_info: &PanicInfo) -> ! { intrinsics::abort() } ``` Note the use of `abort`: the `exchange_malloc` lang item is assumed to return a valid pointer, and so needs to do the check internally. -Other features provided by lang items include: - -- overloadable operators via traits: the traits corresponding to the - `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all - marked with lang items; those specific four are `eq`, `ord`, - `deref`, and `add` respectively. -- stack unwinding and general failure; the `eh_personality`, - `panic` and `panic_bounds_check` lang items. -- the traits in `std::marker` used to indicate types of - various kinds; lang items `send`, `sync` and `copy`. -- the marker types and variance indicators found in - `std::marker`; lang items `covariant_type`, - `contravariant_lifetime`, etc. - -Lang items are loaded lazily by the compiler; e.g. if one never uses -`Box` then there is no need to define functions for `exchange_malloc` -and `box_free`. `rustc` will emit an error when an item is needed -but not found in the current crate or any that it depends on. - -Most lang items are defined by `libcore`, but if you're trying to build -an executable without the standard library, you'll run into the need -for lang items. The rest of this page focuses on this use-case, even though -lang items are a bit broader than that. - -### Using libc - -In order to build a `#[no_std]` executable we will need libc as a dependency. -We can specify this using our `Cargo.toml` file: - -```toml -[dependencies] -libc = { version = "0.2.14", default-features = false } -``` - -Note that the default features have been disabled. This is a critical step - -**the default features of libc include the standard library and so must be -disabled.** - -### Writing an executable without stdlib - -Controlling the entry point is possible in two ways: the `#[start]` attribute, -or overriding the default shim for the C `main` function with your own. - -The function marked `#[start]` is passed the command line parameters -in the same format as C: +## List of all language items -```rust,ignore (libc-is-finicky) -#![feature(lang_items, core_intrinsics, rustc_private)] -#![feature(start)] -#![no_std] -use core::intrinsics; -use core::panic::PanicInfo; +An up-to-date list of all language items can be found [here] in the compiler code. -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; +[here]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_hir/src/lang_items.rs +"##, + }, + Lint { + label: "large_assignments", + description: r##"# `large_assignments` -// Entry point for this program. -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} +The tracking issue for this feature is: [#83518] -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. -#[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} +[#83518]: https://github.com/rust-lang/rust/issues/83518 -#[lang = "panic_impl"] -#[no_mangle] -pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { - unsafe { intrinsics::abort() } -} -``` +------------------------ +"##, + }, + Lint { + label: "layout_for_ptr", + description: r##"# `layout_for_ptr` -To override the compiler-inserted `main` shim, one has to disable it -with `#![no_main]` and then create the appropriate symbol with the -correct ABI and the correct name, which requires overriding the -compiler's name mangling too: +The tracking issue for this feature is: [#69835] -```rust,ignore (libc-is-finicky) -#![feature(lang_items, core_intrinsics, rustc_private)] -#![feature(start)] -#![no_std] -#![no_main] -use core::intrinsics; -use core::panic::PanicInfo; +[#69835]: https://github.com/rust-lang/rust/issues/69835 -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; +------------------------ +"##, + }, + Lint { + label: "lazy_cell", + description: r##"# `lazy_cell` -// Entry point for this program. -#[no_mangle] // ensure that this symbol is called `main` in the output -pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { - 0 -} +The tracking issue for this feature is: [#109736] -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. -#[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} +[#109736]: https://github.com/rust-lang/rust/issues/109736 -#[lang = "panic_impl"] -#[no_mangle] -pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { - unsafe { intrinsics::abort() } -} -``` +------------------------ +"##, + }, + Lint { + label: "lazy_cell_consume", + description: r##"# `lazy_cell_consume` -In many cases, you may need to manually link to the `compiler_builtins` crate -when building a `no_std` binary. You may observe this via linker error messages -such as "```undefined reference to `__rust_probestack'```". +The tracking issue for this feature is: [#109736] -## More about the language items +[#109736]: https://github.com/rust-lang/rust/issues/109736 -The compiler currently makes a few assumptions about symbols which are -available in the executable to call. Normally these functions are provided by -the standard library, but without it you must define your own. These symbols -are called "language items", and they each have an internal name, and then a -signature that an implementation must conform to. +------------------------ +"##, + }, + Lint { + label: "lazy_type_alias", + description: r##"# `lazy_type_alias` -The first of these functions, `rust_eh_personality`, is used by the failure -mechanisms of the compiler. This is often mapped to GCC's personality function -(see the [libstd implementation][unwind] for more information), but crates -which do not trigger a panic can be assured that this function is never -called. The language item's name is `eh_personality`. +The tracking issue for this feature is: [#112792] -[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs +[#112792]: https://github.com/rust-lang/rust/issues/112792 -The second function, `rust_begin_panic`, is also used by the failure mechanisms of the -compiler. When a panic happens, this controls the message that's displayed on -the screen. While the language item's name is `panic_impl`, the symbol name is -`rust_begin_panic`. +------------------------ +"##, + }, + Lint { + label: "let_chains", + description: r##"# `let_chains` -Finally, a `eh_catch_typeinfo` static is needed for certain targets which -implement Rust panics on top of C++ exceptions. +The tracking issue for this feature is: [#53667] -## List of all language items +[#53667]: https://github.com/rust-lang/rust/issues/53667 -This is a list of all language items in Rust along with where they are located in -the source code. - -- Primitives - - `i8`: `libcore/num/mod.rs` - - `i16`: `libcore/num/mod.rs` - - `i32`: `libcore/num/mod.rs` - - `i64`: `libcore/num/mod.rs` - - `i128`: `libcore/num/mod.rs` - - `isize`: `libcore/num/mod.rs` - - `u8`: `libcore/num/mod.rs` - - `u16`: `libcore/num/mod.rs` - - `u32`: `libcore/num/mod.rs` - - `u64`: `libcore/num/mod.rs` - - `u128`: `libcore/num/mod.rs` - - `usize`: `libcore/num/mod.rs` - - `f32`: `libstd/f32.rs` - - `f64`: `libstd/f64.rs` - - `char`: `libcore/char.rs` - - `slice`: `liballoc/slice.rs` - - `str`: `liballoc/str.rs` - - `const_ptr`: `libcore/ptr.rs` - - `mut_ptr`: `libcore/ptr.rs` - - `unsafe_cell`: `libcore/cell.rs` -- Runtime - - `start`: `libstd/rt.rs` - - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) - - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) - - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) - - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) - - `panic`: `libcore/panicking.rs` - - `panic_bounds_check`: `libcore/panicking.rs` - - `panic_impl`: `libcore/panicking.rs` - - `panic_impl`: `libstd/panicking.rs` -- Allocations - - `owned_box`: `liballoc/boxed.rs` - - `exchange_malloc`: `liballoc/heap.rs` - - `box_free`: `liballoc/heap.rs` -- Operands - - `not`: `libcore/ops/bit.rs` - - `bitand`: `libcore/ops/bit.rs` - - `bitor`: `libcore/ops/bit.rs` - - `bitxor`: `libcore/ops/bit.rs` - - `shl`: `libcore/ops/bit.rs` - - `shr`: `libcore/ops/bit.rs` - - `bitand_assign`: `libcore/ops/bit.rs` - - `bitor_assign`: `libcore/ops/bit.rs` - - `bitxor_assign`: `libcore/ops/bit.rs` - - `shl_assign`: `libcore/ops/bit.rs` - - `shr_assign`: `libcore/ops/bit.rs` - - `deref`: `libcore/ops/deref.rs` - - `deref_mut`: `libcore/ops/deref.rs` - - `index`: `libcore/ops/index.rs` - - `index_mut`: `libcore/ops/index.rs` - - `add`: `libcore/ops/arith.rs` - - `sub`: `libcore/ops/arith.rs` - - `mul`: `libcore/ops/arith.rs` - - `div`: `libcore/ops/arith.rs` - - `rem`: `libcore/ops/arith.rs` - - `neg`: `libcore/ops/arith.rs` - - `add_assign`: `libcore/ops/arith.rs` - - `sub_assign`: `libcore/ops/arith.rs` - - `mul_assign`: `libcore/ops/arith.rs` - - `div_assign`: `libcore/ops/arith.rs` - - `rem_assign`: `libcore/ops/arith.rs` - - `eq`: `libcore/cmp.rs` - - `ord`: `libcore/cmp.rs` -- Functions - - `fn`: `libcore/ops/function.rs` - - `fn_mut`: `libcore/ops/function.rs` - - `fn_once`: `libcore/ops/function.rs` - - `generator_state`: `libcore/ops/generator.rs` - - `generator`: `libcore/ops/generator.rs` -- Other - - `coerce_unsized`: `libcore/ops/unsize.rs` - - `drop`: `libcore/ops/drop.rs` - - `drop_in_place`: `libcore/ptr.rs` - - `clone`: `libcore/clone.rs` - - `copy`: `libcore/marker.rs` - - `send`: `libcore/marker.rs` - - `sized`: `libcore/marker.rs` - - `unsize`: `libcore/marker.rs` - - `sync`: `libcore/marker.rs` - - `phantom_data`: `libcore/marker.rs` - - `discriminant_kind`: `libcore/marker.rs` - - `freeze`: `libcore/marker.rs` - - `debug_trait`: `libcore/fmt/mod.rs` - - `non_zero`: `libcore/nonzero.rs` - - `arc`: `liballoc/sync.rs` - - `rc`: `liballoc/rc.rs` +------------------------ "##, }, Lint { - label: "libstd_sys_internals", - description: r##"# `libstd_sys_internals` + label: "liballoc_internals", + description: r##"# `liballoc_internals` -This feature is internal to the Rust compiler and is not intended for general use. +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, }, Lint { - label: "libstd_thread_internals", - description: r##"# `libstd_thread_internals` + label: "libstd_sys_internals", + description: r##"# `libstd_sys_internals` This feature is internal to the Rust compiler and is not intended for general use. @@ -3072,197 +6059,124 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { - label: "llvm_asm", - description: r##"# `llvm_asm` + label: "link_llvm_intrinsics", + description: r##"# `link_llvm_intrinsics` -The tracking issue for this feature is: [#70173] +The tracking issue for this feature is: [#29602] -[#70173]: https://github.com/rust-lang/rust/issues/70173 +[#29602]: https://github.com/rust-lang/rust/issues/29602 ------------------------ +"##, + }, + Lint { + label: "linkage", + description: r##"# `linkage` -For extremely low-level manipulations and performance reasons, one -might wish to control the CPU directly. Rust supports using inline -assembly to do this via the `llvm_asm!` macro. - -```rust,ignore (pseudo-code) -llvm_asm!(assembly template - : output operands - : input operands - : clobbers - : options - ); -``` - -Any use of `llvm_asm` is feature gated (requires `#![feature(llvm_asm)]` on the -crate to allow) and of course requires an `unsafe` block. - -> **Note**: the examples here are given in x86/x86-64 assembly, but -> all platforms are supported. - -## Assembly template +The tracking issue for this feature is: [#29603] -The `assembly template` is the only required parameter and must be a -literal string (i.e. `""`) +[#29603]: https://github.com/rust-lang/rust/issues/29603 -```rust -#![feature(llvm_asm)] +------------------------ +"##, + }, + Lint { + label: "linked_list_cursors", + description: r##"# `linked_list_cursors` -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn foo() { - unsafe { - llvm_asm!("NOP"); - } -} +The tracking issue for this feature is: [#58533] -// Other platforms: -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -fn foo() { /* ... */ } +[#58533]: https://github.com/rust-lang/rust/issues/58533 -fn main() { - // ... - foo(); - // ... -} -``` +------------------------ +"##, + }, + Lint { + label: "linked_list_remove", + description: r##"# `linked_list_remove` -(The `feature(llvm_asm)` and `#[cfg]`s are omitted from now on.) +The tracking issue for this feature is: [#69210] -Output operands, input operands, clobbers and options are all optional -but you must add the right number of `:` if you skip them: +[#69210]: https://github.com/rust-lang/rust/issues/69210 -```rust -# #![feature(llvm_asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { unsafe { -llvm_asm!("xor %eax, %eax" - : - : - : "eax" - ); -# } } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} -``` +------------------------ +"##, + }, + Lint { + label: "lint_reasons", + description: r##"# `lint_reasons` -Whitespace also doesn't matter: +The tracking issue for this feature is: [#54503] -```rust -# #![feature(llvm_asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { unsafe { -llvm_asm!("xor %eax, %eax" ::: "eax"); -# } } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} -``` +[#54503]: https://github.com/rust-lang/rust/issues/54503 -## Operands +------------------------ +"##, + }, + Lint { + label: "linux_pidfd", + description: r##"# `linux_pidfd` -Input and output operands follow the same format: `: -"constraints1"(expr1), "constraints2"(expr2), ..."`. Output operand -expressions must be mutable place, or not yet assigned: +The tracking issue for this feature is: [#82971] -```rust -# #![feature(llvm_asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn add(a: i32, b: i32) -> i32 { - let c: i32; - unsafe { - llvm_asm!("add $2, $0" - : "=r"(c) - : "0"(a), "r"(b) - ); - } - c -} -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn add(a: i32, b: i32) -> i32 { a + b } +[#82971]: https://github.com/rust-lang/rust/issues/82971 -fn main() { - assert_eq!(add(3, 14159), 14162) -} -``` +------------------------ +"##, + }, + Lint { + label: "log_syntax", + description: r##"# `log_syntax` -If you would like to use real operands in this position, however, -you are required to put curly braces `{}` around the register that -you want, and you are required to put the specific size of the -operand. This is useful for very low level programming, where -which register you use is important: +The tracking issue for this feature is: [#29598] -```rust -# #![feature(llvm_asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# unsafe fn read_byte_in(port: u16) -> u8 { -let result: u8; -llvm_asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port)); -result -# } -``` +[#29598]: https://github.com/rust-lang/rust/issues/29598 -## Clobbers +------------------------ +"##, + }, + Lint { + label: "macro_metavar_expr", + description: r##"# `macro_metavar_expr` -Some instructions modify registers which might otherwise have held -different values so we use the clobbers list to indicate to the -compiler not to assume any values loaded into those registers will -stay valid. +The tracking issue for this feature is: [#83527] -```rust -# #![feature(llvm_asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { unsafe { -// Put the value 0x200 in eax: -llvm_asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "eax"); -# } } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} -``` +[#83527]: https://github.com/rust-lang/rust/issues/83527 -Input and output registers need not be listed since that information -is already communicated by the given constraints. Otherwise, any other -registers used either implicitly or explicitly should be listed. +------------------------ +"##, + }, + Lint { + label: "map_entry_replace", + description: r##"# `map_entry_replace` -If the assembly changes the condition code register `cc` should be -specified as one of the clobbers. Similarly, if the assembly modifies -memory, `memory` should also be specified. +The tracking issue for this feature is: [#44286] -## Options +[#44286]: https://github.com/rust-lang/rust/issues/44286 -The last section, `options` is specific to Rust. The format is comma -separated literal strings (i.e. `:"foo", "bar", "baz"`). It's used to -specify some extra info about the inline assembly: +------------------------ +"##, + }, + Lint { + label: "map_many_mut", + description: r##"# `map_many_mut` -Current valid options are: +The tracking issue for this feature is: [#97601] -1. `volatile` - specifying this is analogous to - `__asm__ __volatile__ (...)` in gcc/clang. -2. `alignstack` - certain instructions expect the stack to be - aligned a certain way (i.e. SSE) and specifying this indicates to - the compiler to insert its usual stack alignment code -3. `intel` - use intel syntax instead of the default AT&T. +[#97601]: https://github.com/rust-lang/rust/issues/97601 -```rust -# #![feature(llvm_asm)] -# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -# fn main() { -let result: i32; -unsafe { - llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel") -} -println!("eax is currently {}", result); -# } -# #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -# fn main() {} -``` +------------------------ +"##, + }, + Lint { + label: "map_try_insert", + description: r##"# `map_try_insert` -## More Information +The tracking issue for this feature is: [#82766] -The current implementation of the `llvm_asm!` macro is a direct binding to [LLVM's -inline assembler expressions][llvm-docs], so be sure to check out [their -documentation as well][llvm-docs] for more information about clobbers, -constraints, etc. +[#82766]: https://github.com/rust-lang/rust/issues/82766 -[llvm-docs]: http://llvm.org/docs/LangRef.html#inline-assembler-expressions +------------------------ "##, }, Lint { @@ -3305,6 +6219,116 @@ feature, which applied to all empty traits (without needing an opt-in). "##, }, Lint { + label: "maybe_uninit_array_assume_init", + description: r##"# `maybe_uninit_array_assume_init` + +The tracking issue for this feature is: [#96097] + +[#96097]: https://github.com/rust-lang/rust/issues/96097 + +------------------------ +"##, + }, + Lint { + label: "maybe_uninit_as_bytes", + description: r##"# `maybe_uninit_as_bytes` + +The tracking issue for this feature is: [#93092] + +[#93092]: https://github.com/rust-lang/rust/issues/93092 + +------------------------ +"##, + }, + Lint { + label: "maybe_uninit_slice", + description: r##"# `maybe_uninit_slice` + +The tracking issue for this feature is: [#63569] + +[#63569]: https://github.com/rust-lang/rust/issues/63569 + +------------------------ +"##, + }, + Lint { + label: "maybe_uninit_uninit_array", + description: r##"# `maybe_uninit_uninit_array` + +The tracking issue for this feature is: [#96097] + +[#96097]: https://github.com/rust-lang/rust/issues/96097 + +------------------------ +"##, + }, + Lint { + label: "maybe_uninit_uninit_array_transpose", + description: r##"# `maybe_uninit_uninit_array_transpose` + +The tracking issue for this feature is: [#96097] + +[#96097]: https://github.com/rust-lang/rust/issues/96097 + +------------------------ +"##, + }, + Lint { + label: "maybe_uninit_write_slice", + description: r##"# `maybe_uninit_write_slice` + +The tracking issue for this feature is: [#79995] + +[#79995]: https://github.com/rust-lang/rust/issues/79995 + +------------------------ +"##, + }, + Lint { + label: "mem_copy_fn", + description: r##"# `mem_copy_fn` + +The tracking issue for this feature is: [#98262] + +[#98262]: https://github.com/rust-lang/rust/issues/98262 + +------------------------ +"##, + }, + Lint { + label: "min_specialization", + description: r##"# `min_specialization` + +The tracking issue for this feature is: [#31844] + +[#31844]: https://github.com/rust-lang/rust/issues/31844 + +------------------------ +"##, + }, + Lint { + label: "mips_target_feature", + description: r##"# `mips_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "more_float_constants", + description: r##"# `more_float_constants` + +The tracking issue for this feature is: [#103883] + +[#103883]: https://github.com/rust-lang/rust/issues/103883 + +------------------------ +"##, + }, + Lint { label: "more_qualified_paths", description: r##"# `more_qualified_paths` @@ -3338,68 +6362,61 @@ impl A for Foo { "##, }, Lint { - label: "native_link_modifiers", - description: r##"# `native_link_modifiers` + label: "multiple_supertrait_upcastable", + description: r##"# `multiple_supertrait_upcastable` -The tracking issue for this feature is: [#81490] - -[#81490]: https://github.com/rust-lang/rust/issues/81490 +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ - -The `native_link_modifiers` feature allows you to use the `modifiers` syntax with the `#[link(..)]` attribute. - -Modifiers are specified as a comma-delimited string with each modifier prefixed with either a `+` or `-` to indicate that the modifier is enabled or disabled, respectively. The last boolean value specified for a given modifier wins. "##, }, Lint { - label: "native_link_modifiers_as_needed", - description: r##"# `native_link_modifiers_as_needed` + label: "must_not_suspend", + description: r##"# `must_not_suspend` -The tracking issue for this feature is: [#81490] +The tracking issue for this feature is: [#83310] -[#81490]: https://github.com/rust-lang/rust/issues/81490 +[#83310]: https://github.com/rust-lang/rust/issues/83310 ------------------------ - -The `native_link_modifiers_as_needed` feature allows you to use the `as-needed` modifier. - -`as-needed` is only compatible with the `dynamic` and `framework` linking kinds. Using any other kind will result in a compiler error. - -`+as-needed` means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard. - -This modifier translates to `--as-needed` for ld-like linkers, and to `-dead_strip_dylibs` / `-needed_library` / `-needed_framework` for ld64. -The modifier does nothing for linkers that don't support it (e.g. `link.exe`). - -The default for this modifier is unclear, some targets currently specify it as `+as-needed`, some do not. We may want to try making `+as-needed` a default for all targets. "##, }, Lint { - label: "native_link_modifiers_bundle", - description: r##"# `native_link_modifiers_bundle` + label: "mutex_unlock", + description: r##"# `mutex_unlock` -The tracking issue for this feature is: [#81490] +The tracking issue for this feature is: [#81872] -[#81490]: https://github.com/rust-lang/rust/issues/81490 +[#81872]: https://github.com/rust-lang/rust/issues/81872 ------------------------ +"##, + }, + Lint { + label: "mutex_unpoison", + description: r##"# `mutex_unpoison` -The `native_link_modifiers_bundle` feature allows you to use the `bundle` modifier. +The tracking issue for this feature is: [#96469] -Only compatible with the `static` linking kind. Using any other kind will result in a compiler error. +[#96469]: https://github.com/rust-lang/rust/issues/96469 -`+bundle` means objects from the static library are bundled into the produced crate (a rlib, for example) and are used from this crate later during linking of the final binary. +------------------------ +"##, + }, + Lint { + label: "naked_functions", + description: r##"# `naked_functions` -`-bundle` means the static library is included into the produced rlib "by name" and object files from it are included only during linking of the final binary, the file search by that name is also performed during final linking. +The tracking issue for this feature is: [#32408] -This modifier is supposed to supersede the `static-nobundle` linking kind defined by [RFC 1717](https://github.com/rust-lang/rfcs/pull/1717). +[#32408]: https://github.com/rust-lang/rust/issues/32408 -The default for this modifier is currently `+bundle`, but it could be changed later on some future edition boundary. +------------------------ "##, }, Lint { - label: "native_link_modifiers_verbatim", - description: r##"# `native_link_modifiers_verbatim` + label: "native_link_modifiers_as_needed", + description: r##"# `native_link_modifiers_as_needed` The tracking issue for this feature is: [#81490] @@ -3407,40 +6424,36 @@ The tracking issue for this feature is: [#81490] ------------------------ -The `native_link_modifiers_verbatim` feature allows you to use the `verbatim` modifier. +The `native_link_modifiers_as_needed` feature allows you to use the `as-needed` modifier. -`+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes (like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the linker. +`as-needed` is only compatible with the `dynamic` and `framework` linking kinds. Using any other kind will result in a compiler error. -For `ld`-like linkers rustc will use the `-l:filename` syntax (note the colon) when passing the library, so the linker won't add any prefixes or suffixes as well. -See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for more details. -For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will be passed as is. +`+as-needed` means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard. -The default for this modifier is `-verbatim`. +This modifier translates to `--as-needed` for ld-like linkers, and to `-dead_strip_dylibs` / `-needed_library` / `-needed_framework` for ld64. +The modifier does nothing for linkers that don't support it (e.g. `link.exe`). -This RFC changes the behavior of `raw-dylib` linking kind specified by [RFC 2627](https://github.com/rust-lang/rfcs/pull/2627). The `.dll` suffix (or other target-specified suffixes for other targets) is now added automatically. -If your DLL doesn't have the `.dll` suffix, it can be specified with `+verbatim`. +The default for this modifier is unclear, some targets currently specify it as `+as-needed`, some do not. We may want to try making `+as-needed` a default for all targets. "##, }, Lint { - label: "native_link_modifiers_whole_archive", - description: r##"# `native_link_modifiers_whole_archive` + label: "needs_panic_runtime", + description: r##"# `needs_panic_runtime` -The tracking issue for this feature is: [#81490] +The tracking issue for this feature is: [#32837] -[#81490]: https://github.com/rust-lang/rust/issues/81490 +[#32837]: https://github.com/rust-lang/rust/issues/32837 ------------------------ +"##, + }, + Lint { + label: "negative_bounds", + description: r##"# `negative_bounds` -The `native_link_modifiers_whole_archive` feature allows you to use the `whole-archive` modifier. - -Only compatible with the `static` linking kind. Using any other kind will result in a compiler error. - -`+whole-archive` means that the static library is linked as a whole archive without throwing any object files away. - -This modifier translates to `--whole-archive` for `ld`-like linkers, to `/WHOLEARCHIVE` for `link.exe`, and to `-force_load` for `ld64`. -The modifier does nothing for linkers that don't support it. +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. -The default for this modifier is `-whole-archive`. +------------------------ "##, }, Lint { @@ -3505,37 +6518,47 @@ This serves two purposes: "##, }, Lint { - label: "coverage", - description: r##"# `coverage` + label: "never_type", + description: r##"# `never_type` -The tracking issue for this feature is: [#84605] +The tracking issue for this feature is: [#35121] -[#84605]: https://github.com/rust-lang/rust/issues/84605 +[#35121]: https://github.com/rust-lang/rust/issues/35121 ---- +------------------------ +"##, + }, + Lint { + label: "never_type_fallback", + description: r##"# `never_type_fallback` -The `coverage` attribute can be used to selectively disable coverage -instrumentation in an annotated function. This might be useful to: +The tracking issue for this feature is: [#65992] -- Avoid instrumentation overhead in a performance critical function -- Avoid generating coverage for a function that is not meant to be executed, - but still target 100% coverage for the rest of the program. +[#65992]: https://github.com/rust-lang/rust/issues/65992 -## Example +------------------------ +"##, + }, + Lint { + label: "new_uninit", + description: r##"# `new_uninit` -```rust -#![feature(coverage)] +The tracking issue for this feature is: [#63291] -// `foo()` will get coverage instrumentation (by default) -fn foo() { - // ... -} +[#63291]: https://github.com/rust-lang/rust/issues/63291 -#[coverage(off)] -fn bar() { - // ... -} -``` +------------------------ +"##, + }, + Lint { + label: "no_core", + description: r##"# `no_core` + +The tracking issue for this feature is: [#29639] + +[#29639]: https://github.com/rust-lang/rust/issues/29639 + +------------------------ "##, }, Lint { @@ -3572,6 +6595,319 @@ fn foo() { "##, }, Lint { + label: "non_exhaustive_omitted_patterns_lint", + description: r##"# `non_exhaustive_omitted_patterns_lint` + +The tracking issue for this feature is: [#89554] + +[#89554]: https://github.com/rust-lang/rust/issues/89554 + +------------------------ +"##, + }, + Lint { + label: "non_lifetime_binders", + description: r##"# `non_lifetime_binders` + +The tracking issue for this feature is: [#108185] + +[#108185]: https://github.com/rust-lang/rust/issues/108185 + +------------------------ +"##, + }, + Lint { + label: "nonzero_ops", + description: r##"# `nonzero_ops` + +The tracking issue for this feature is: [#84186] + +[#84186]: https://github.com/rust-lang/rust/issues/84186 + +------------------------ +"##, + }, + Lint { + label: "noop_waker", + description: r##"# `noop_waker` + +The tracking issue for this feature is: [#98286] + +[#98286]: https://github.com/rust-lang/rust/issues/98286 + +------------------------ +"##, + }, + Lint { + label: "num_midpoint", + description: r##"# `num_midpoint` + +The tracking issue for this feature is: [#110840] + +[#110840]: https://github.com/rust-lang/rust/issues/110840 + +------------------------ +"##, + }, + Lint { + label: "numfmt", + description: r##"# `numfmt` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "object_safe_for_dispatch", + description: r##"# `object_safe_for_dispatch` + +The tracking issue for this feature is: [#43561] + +[#43561]: https://github.com/rust-lang/rust/issues/43561 + +------------------------ +"##, + }, + Lint { + label: "offset_of", + description: r##"# `offset_of` + +The tracking issue for this feature is: [#106655] + +[#106655]: https://github.com/rust-lang/rust/issues/106655 + +------------------------ +"##, + }, + Lint { + label: "omit_gdb_pretty_printer_section", + description: r##"# `omit_gdb_pretty_printer_section` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "once_cell_try", + description: r##"# `once_cell_try` + +The tracking issue for this feature is: [#109737] + +[#109737]: https://github.com/rust-lang/rust/issues/109737 + +------------------------ +"##, + }, + Lint { + label: "one_sided_range", + description: r##"# `one_sided_range` + +The tracking issue for this feature is: [#69780] + +[#69780]: https://github.com/rust-lang/rust/issues/69780 + +------------------------ +"##, + }, + Lint { + label: "optimize_attribute", + description: r##"# `optimize_attribute` + +The tracking issue for this feature is: [#54882] + +[#54882]: https://github.com/rust-lang/rust/issues/54882 + +------------------------ +"##, + }, + Lint { + label: "option_get_or_insert_default", + description: r##"# `option_get_or_insert_default` + +The tracking issue for this feature is: [#82901] + +[#82901]: https://github.com/rust-lang/rust/issues/82901 + +------------------------ +"##, + }, + Lint { + label: "option_take_if", + description: r##"# `option_take_if` + +The tracking issue for this feature is: [#98934] + +[#98934]: https://github.com/rust-lang/rust/issues/98934 + +------------------------ +"##, + }, + Lint { + label: "option_zip", + description: r##"# `option_zip` + +The tracking issue for this feature is: [#70086] + +[#70086]: https://github.com/rust-lang/rust/issues/70086 + +------------------------ +"##, + }, + Lint { + label: "panic_abort", + description: r##"# `panic_abort` + +The tracking issue for this feature is: [#32837] + +[#32837]: https://github.com/rust-lang/rust/issues/32837 + +------------------------ +"##, + }, + Lint { + label: "panic_always_abort", + description: r##"# `panic_always_abort` + +The tracking issue for this feature is: [#84438] + +[#84438]: https://github.com/rust-lang/rust/issues/84438 + +------------------------ +"##, + }, + Lint { + label: "panic_backtrace_config", + description: r##"# `panic_backtrace_config` + +The tracking issue for this feature is: [#93346] + +[#93346]: https://github.com/rust-lang/rust/issues/93346 + +------------------------ +"##, + }, + Lint { + label: "panic_can_unwind", + description: r##"# `panic_can_unwind` + +The tracking issue for this feature is: [#92988] + +[#92988]: https://github.com/rust-lang/rust/issues/92988 + +------------------------ +"##, + }, + Lint { + label: "panic_info_message", + description: r##"# `panic_info_message` + +The tracking issue for this feature is: [#66745] + +[#66745]: https://github.com/rust-lang/rust/issues/66745 + +------------------------ +"##, + }, + Lint { + label: "panic_internals", + description: r##"# `panic_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "panic_runtime", + description: r##"# `panic_runtime` + +The tracking issue for this feature is: [#32837] + +[#32837]: https://github.com/rust-lang/rust/issues/32837 + +------------------------ +"##, + }, + Lint { + label: "panic_unwind", + description: r##"# `panic_unwind` + +The tracking issue for this feature is: [#32837] + +[#32837]: https://github.com/rust-lang/rust/issues/32837 + +------------------------ +"##, + }, + Lint { + label: "panic_update_hook", + description: r##"# `panic_update_hook` + +The tracking issue for this feature is: [#92649] + +[#92649]: https://github.com/rust-lang/rust/issues/92649 + +------------------------ +"##, + }, + Lint { + label: "path_file_prefix", + description: r##"# `path_file_prefix` + +The tracking issue for this feature is: [#86319] + +[#86319]: https://github.com/rust-lang/rust/issues/86319 + +------------------------ +"##, + }, + Lint { + label: "pattern", + description: r##"# `pattern` + +The tracking issue for this feature is: [#27721] + +[#27721]: https://github.com/rust-lang/rust/issues/27721 + +------------------------ +"##, + }, + Lint { + label: "peer_credentials_unix_socket", + description: r##"# `peer_credentials_unix_socket` + +The tracking issue for this feature is: [#42839] + +[#42839]: https://github.com/rust-lang/rust/issues/42839 + +------------------------ +"##, + }, + Lint { + label: "pin_deref_mut", + description: r##"# `pin_deref_mut` + +The tracking issue for this feature is: [#86918] + +[#86918]: https://github.com/rust-lang/rust/issues/86918 + +------------------------ +"##, + }, + Lint { + label: "platform_intrinsics", + description: r##"# `platform_intrinsics` + +The tracking issue for this feature is: [#27731] + +[#27731]: https://github.com/rust-lang/rust/issues/27731 + +------------------------ +"##, + }, + Lint { label: "plugin", description: r##"# `plugin` @@ -3608,24 +6944,24 @@ of a library. Plugins can extend [Rust's lint infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with additional checks for code style, safety, etc. Now let's write a plugin -[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/tests/ui-fulldeps/auxiliary/lint-plugin-test.rs) +[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/tests/ui-fulldeps/plugin/auxiliary/lint-plugin-test.rs) that warns about any item named `lintme`. ```rust,ignore (requires-stage-2) -#![feature(box_syntax, rustc_private)] +#![feature(rustc_private)] extern crate rustc_ast; // Load rustc as a plugin to get macros extern crate rustc_driver; -#[macro_use] extern crate rustc_lint; #[macro_use] extern crate rustc_session; -use rustc_driver::plugin::Registry; -use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; use rustc_ast::ast; +use rustc_driver::plugin::Registry; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; + declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); declare_lint_pass!(Pass => [TEST_LINT]); @@ -3633,9 +6969,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { if it.ident.name.as_str() == "lintme" { - cx.lint(TEST_LINT, |lint| { - lint.build("item is named 'lintme'").set_span(it.span).emit() - }); + cx.lint(TEST_LINT, "item is named 'lintme'", |lint| lint.set_span(it.span)); } } } @@ -3643,7 +6977,7 @@ impl EarlyLintPass for Pass { #[no_mangle] fn __rustc_plugin_registrar(reg: &mut Registry) { reg.lint_store.register_lints(&[&TEST_LINT]); - reg.lint_store.register_early_pass(|| box Pass); + reg.lint_store.register_early_pass(|| Box::new(Pass)); } ``` @@ -3677,7 +7011,7 @@ The components of a lint plugin are: Lint passes are syntax traversals, but they run at a late stage of compilation where type information is available. `rustc`'s [built-in -lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs) +lints](https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint_defs/src/builtin.rs) mostly use the same infrastructure as lint plugins, and provide examples of how to access type information. @@ -3692,6 +7026,88 @@ including those provided by plugins loaded by `foo.rs`. "##, }, Lint { + label: "pointer_byte_offsets", + description: r##"# `pointer_byte_offsets` + +The tracking issue for this feature is: [#96283] + +[#96283]: https://github.com/rust-lang/rust/issues/96283 + +------------------------ +"##, + }, + Lint { + label: "pointer_is_aligned", + description: r##"# `pointer_is_aligned` + +The tracking issue for this feature is: [#96284] + +[#96284]: https://github.com/rust-lang/rust/issues/96284 + +------------------------ +"##, + }, + Lint { + label: "pointer_like_trait", + description: r##"# `pointer_like_trait` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "portable_simd", + description: r##"# `portable_simd` + +The tracking issue for this feature is: [#86656] + +[#86656]: https://github.com/rust-lang/rust/issues/86656 + +------------------------ +"##, + }, + Lint { + label: "powerpc_target_feature", + description: r##"# `powerpc_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "precise_pointer_size_matching", + description: r##"# `precise_pointer_size_matching` + +The tracking issue for this feature is: [#56354] + +[#56354]: https://github.com/rust-lang/rust/issues/56354 + +------------------------ +"##, + }, + Lint { + label: "prelude_2024", + description: r##"# `prelude_2024` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "prelude_import", + description: r##"# `prelude_import` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { label: "print_internals", description: r##"# `print_internals` @@ -3701,6 +7117,123 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "proc_macro_byte_character", + description: r##"# `proc_macro_byte_character` + +The tracking issue for this feature is: [#115268] + +[#115268]: https://github.com/rust-lang/rust/issues/115268 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_def_site", + description: r##"# `proc_macro_def_site` + +The tracking issue for this feature is: [#54724] + +[#54724]: https://github.com/rust-lang/rust/issues/54724 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_diagnostic", + description: r##"# `proc_macro_diagnostic` + +The tracking issue for this feature is: [#54140] + +[#54140]: https://github.com/rust-lang/rust/issues/54140 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_expand", + description: r##"# `proc_macro_expand` + +The tracking issue for this feature is: [#90765] + +[#90765]: https://github.com/rust-lang/rust/issues/90765 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_hygiene", + description: r##"# `proc_macro_hygiene` + +The tracking issue for this feature is: [#54727] + +[#54727]: https://github.com/rust-lang/rust/issues/54727 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_internals", + description: r##"# `proc_macro_internals` + +The tracking issue for this feature is: [#27812] + +[#27812]: https://github.com/rust-lang/rust/issues/27812 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_quote", + description: r##"# `proc_macro_quote` + +The tracking issue for this feature is: [#54722] + +[#54722]: https://github.com/rust-lang/rust/issues/54722 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_span", + description: r##"# `proc_macro_span` + +The tracking issue for this feature is: [#54725] + +[#54725]: https://github.com/rust-lang/rust/issues/54725 + +------------------------ +"##, + }, + Lint { + label: "proc_macro_tracked_env", + description: r##"# `proc_macro_tracked_env` + +The tracking issue for this feature is: [#99515] + +[#99515]: https://github.com/rust-lang/rust/issues/99515 + +------------------------ +"##, + }, + Lint { + label: "process_exitcode_internals", + description: r##"# `process_exitcode_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "process_internals", + description: r##"# `process_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { label: "profiler_runtime", description: r##"# `profiler_runtime` @@ -3719,41 +7252,204 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { - label: "raw_dylib", - description: r##"# `raw_dylib` + label: "ptr_addr_eq", + description: r##"# `ptr_addr_eq` -The tracking issue for this feature is: [#58713] +The tracking issue for this feature is: [#116324] -[#58713]: https://github.com/rust-lang/rust/issues/58713 +[#116324]: https://github.com/rust-lang/rust/issues/116324 ------------------------ +"##, + }, + Lint { + label: "ptr_alignment_type", + description: r##"# `ptr_alignment_type` -The `raw_dylib` feature allows you to link against the implementations of functions in an `extern` -block without, on Windows, linking against an import library. +The tracking issue for this feature is: [#102070] -```rust,ignore (partial-example) -#![feature(raw_dylib)] +[#102070]: https://github.com/rust-lang/rust/issues/102070 -#[link(name="library", kind="raw-dylib")] -extern { - fn extern_function(x: i32); -} +------------------------ +"##, + }, + Lint { + label: "ptr_as_uninit", + description: r##"# `ptr_as_uninit` -fn main() { - unsafe { - extern_function(14); - } -} -``` +The tracking issue for this feature is: [#75402] + +[#75402]: https://github.com/rust-lang/rust/issues/75402 + +------------------------ +"##, + }, + Lint { + label: "ptr_from_ref", + description: r##"# `ptr_from_ref` + +The tracking issue for this feature is: [#106116] + +[#106116]: https://github.com/rust-lang/rust/issues/106116 + +------------------------ +"##, + }, + Lint { + label: "ptr_internals", + description: r##"# `ptr_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "ptr_mask", + description: r##"# `ptr_mask` + +The tracking issue for this feature is: [#98290] + +[#98290]: https://github.com/rust-lang/rust/issues/98290 + +------------------------ +"##, + }, + Lint { + label: "ptr_metadata", + description: r##"# `ptr_metadata` + +The tracking issue for this feature is: [#81513] + +[#81513]: https://github.com/rust-lang/rust/issues/81513 + +------------------------ +"##, + }, + Lint { + label: "ptr_sub_ptr", + description: r##"# `ptr_sub_ptr` + +The tracking issue for this feature is: [#95892] + +[#95892]: https://github.com/rust-lang/rust/issues/95892 + +------------------------ +"##, + }, + Lint { + label: "ptr_to_from_bits", + description: r##"# `ptr_to_from_bits` + +The tracking issue for this feature is: [#91126] + +[#91126]: https://github.com/rust-lang/rust/issues/91126 + +------------------------ +"##, + }, + Lint { + label: "pub_crate_should_not_need_unstable_attr", + description: r##"# `pub_crate_should_not_need_unstable_attr` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "raw_os_error_ty", + description: r##"# `raw_os_error_ty` + +The tracking issue for this feature is: [#107792] + +[#107792]: https://github.com/rust-lang/rust/issues/107792 + +------------------------ +"##, + }, + Lint { + label: "raw_os_nonzero", + description: r##"# `raw_os_nonzero` + +The tracking issue for this feature is: [#82363] + +[#82363]: https://github.com/rust-lang/rust/issues/82363 -## Limitations +------------------------ +"##, + }, + Lint { + label: "raw_ref_op", + description: r##"# `raw_ref_op` + +The tracking issue for this feature is: [#64490] + +[#64490]: https://github.com/rust-lang/rust/issues/64490 + +------------------------ +"##, + }, + Lint { + label: "raw_slice_split", + description: r##"# `raw_slice_split` + +The tracking issue for this feature is: [#95595] -Currently, this feature is only supported on `-windows-msvc` targets. Non-Windows platforms don't have import -libraries, and an incompatibility between LLVM and the BFD linker means that it is not currently supported on -`-windows-gnu` targets. +[#95595]: https://github.com/rust-lang/rust/issues/95595 -On the `i686-pc-windows-msvc` target, this feature supports only the `cdecl`, `stdcall`, `system`, and `fastcall` -calling conventions. +------------------------ +"##, + }, + Lint { + label: "raw_vec_internals", + description: r##"# `raw_vec_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "read_buf", + description: r##"# `read_buf` + +The tracking issue for this feature is: [#78485] + +[#78485]: https://github.com/rust-lang/rust/issues/78485 + +------------------------ +"##, + }, + Lint { + label: "ready_into_inner", + description: r##"# `ready_into_inner` + +The tracking issue for this feature is: [#101196] + +[#101196]: https://github.com/rust-lang/rust/issues/101196 + +------------------------ +"##, + }, + Lint { + label: "receiver_trait", + description: r##"# `receiver_trait` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "register_tool", + description: r##"# `register_tool` + +The tracking issue for this feature is: [#66079] + +[#66079]: https://github.com/rust-lang/rust/issues/66079 + +------------------------ "##, }, Lint { @@ -3779,6 +7475,103 @@ enum Foo { "##, }, Lint { + label: "repr_simd", + description: r##"# `repr_simd` + +The tracking issue for this feature is: [#27731] + +[#27731]: https://github.com/rust-lang/rust/issues/27731 + +------------------------ +"##, + }, + Lint { + label: "restricted_std", + description: r##"# `restricted_std` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "result_flattening", + description: r##"# `result_flattening` + +The tracking issue for this feature is: [#70142] + +[#70142]: https://github.com/rust-lang/rust/issues/70142 + +------------------------ +"##, + }, + Lint { + label: "result_option_inspect", + description: r##"# `result_option_inspect` + +The tracking issue for this feature is: [#91345] + +[#91345]: https://github.com/rust-lang/rust/issues/91345 + +------------------------ +"##, + }, + Lint { + label: "return_position_impl_trait_in_trait", + description: r##"# `return_position_impl_trait_in_trait` + +The tracking issue for this feature is: [#91611] + +[#91611]: https://github.com/rust-lang/rust/issues/91611 + +------------------------ +"##, + }, + Lint { + label: "return_type_notation", + description: r##"# `return_type_notation` + +The tracking issue for this feature is: [#109417] + +[#109417]: https://github.com/rust-lang/rust/issues/109417 + +------------------------ +"##, + }, + Lint { + label: "riscv_target_feature", + description: r##"# `riscv_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "round_char_boundary", + description: r##"# `round_char_boundary` + +The tracking issue for this feature is: [#93743] + +[#93743]: https://github.com/rust-lang/rust/issues/93743 + +------------------------ +"##, + }, + Lint { + label: "round_ties_even", + description: r##"# `round_ties_even` + +The tracking issue for this feature is: [#96710] + +[#96710]: https://github.com/rust-lang/rust/issues/96710 + +------------------------ +"##, + }, + Lint { label: "rt", description: r##"# `rt` @@ -3788,6 +7581,39 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "rtm_target_feature", + description: r##"# `rtm_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "rust_cold_cc", + description: r##"# `rust_cold_cc` + +The tracking issue for this feature is: [#97544] + +[#97544]: https://github.com/rust-lang/rust/issues/97544 + +------------------------ +"##, + }, + Lint { + label: "rustc_allow_const_fn_unstable", + description: r##"# `rustc_allow_const_fn_unstable` + +The tracking issue for this feature is: [#69399] + +[#69399]: https://github.com/rust-lang/rust/issues/69399 + +------------------------ +"##, + }, + Lint { label: "rustc_attrs", description: r##"# `rustc_attrs` @@ -3845,6 +7671,326 @@ error: aborting due to 2 previous errors "##, }, Lint { + label: "rustc_private", + description: r##"# `rustc_private` + +The tracking issue for this feature is: [#27812] + +[#27812]: https://github.com/rust-lang/rust/issues/27812 + +------------------------ +"##, + }, + Lint { + label: "rustdoc_internals", + description: r##"# `rustdoc_internals` + +The tracking issue for this feature is: [#90418] + +[#90418]: https://github.com/rust-lang/rust/issues/90418 + +------------------------ +"##, + }, + Lint { + label: "rustdoc_missing_doc_code_examples", + description: r##"# `rustdoc_missing_doc_code_examples` + +The tracking issue for this feature is: [#101730] + +[#101730]: https://github.com/rust-lang/rust/issues/101730 + +------------------------ +"##, + }, + Lint { + label: "sealed", + description: r##"# `sealed` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "seek_stream_len", + description: r##"# `seek_stream_len` + +The tracking issue for this feature is: [#59359] + +[#59359]: https://github.com/rust-lang/rust/issues/59359 + +------------------------ +"##, + }, + Lint { + label: "set_ptr_value", + description: r##"# `set_ptr_value` + +The tracking issue for this feature is: [#75091] + +[#75091]: https://github.com/rust-lang/rust/issues/75091 + +------------------------ +"##, + }, + Lint { + label: "setgroups", + description: r##"# `setgroups` + +The tracking issue for this feature is: [#90747] + +[#90747]: https://github.com/rust-lang/rust/issues/90747 + +------------------------ +"##, + }, + Lint { + label: "sgx_platform", + description: r##"# `sgx_platform` + +The tracking issue for this feature is: [#56975] + +[#56975]: https://github.com/rust-lang/rust/issues/56975 + +------------------------ +"##, + }, + Lint { + label: "simd_ffi", + description: r##"# `simd_ffi` + +The tracking issue for this feature is: [#27731] + +[#27731]: https://github.com/rust-lang/rust/issues/27731 + +------------------------ +"##, + }, + Lint { + label: "sized_type_properties", + description: r##"# `sized_type_properties` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "slice_as_chunks", + description: r##"# `slice_as_chunks` + +The tracking issue for this feature is: [#74985] + +[#74985]: https://github.com/rust-lang/rust/issues/74985 + +------------------------ +"##, + }, + Lint { + label: "slice_concat_ext", + description: r##"# `slice_concat_ext` + +The tracking issue for this feature is: [#27747] + +[#27747]: https://github.com/rust-lang/rust/issues/27747 + +------------------------ +"##, + }, + Lint { + label: "slice_concat_trait", + description: r##"# `slice_concat_trait` + +The tracking issue for this feature is: [#27747] + +[#27747]: https://github.com/rust-lang/rust/issues/27747 + +------------------------ +"##, + }, + Lint { + label: "slice_first_last_chunk", + description: r##"# `slice_first_last_chunk` + +The tracking issue for this feature is: [#111774] + +[#111774]: https://github.com/rust-lang/rust/issues/111774 + +------------------------ +"##, + }, + Lint { + label: "slice_flatten", + description: r##"# `slice_flatten` + +The tracking issue for this feature is: [#95629] + +[#95629]: https://github.com/rust-lang/rust/issues/95629 + +------------------------ +"##, + }, + Lint { + label: "slice_from_ptr_range", + description: r##"# `slice_from_ptr_range` + +The tracking issue for this feature is: [#89792] + +[#89792]: https://github.com/rust-lang/rust/issues/89792 + +------------------------ +"##, + }, + Lint { + label: "slice_group_by", + description: r##"# `slice_group_by` + +The tracking issue for this feature is: [#80552] + +[#80552]: https://github.com/rust-lang/rust/issues/80552 + +------------------------ +"##, + }, + Lint { + label: "slice_index_methods", + description: r##"# `slice_index_methods` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "slice_internals", + description: r##"# `slice_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "slice_iter_mut_as_mut_slice", + description: r##"# `slice_iter_mut_as_mut_slice` + +The tracking issue for this feature is: [#93079] + +[#93079]: https://github.com/rust-lang/rust/issues/93079 + +------------------------ +"##, + }, + Lint { + label: "slice_partition_dedup", + description: r##"# `slice_partition_dedup` + +The tracking issue for this feature is: [#54279] + +[#54279]: https://github.com/rust-lang/rust/issues/54279 + +------------------------ +"##, + }, + Lint { + label: "slice_pattern", + description: r##"# `slice_pattern` + +The tracking issue for this feature is: [#56345] + +[#56345]: https://github.com/rust-lang/rust/issues/56345 + +------------------------ +"##, + }, + Lint { + label: "slice_ptr_get", + description: r##"# `slice_ptr_get` + +The tracking issue for this feature is: [#74265] + +[#74265]: https://github.com/rust-lang/rust/issues/74265 + +------------------------ +"##, + }, + Lint { + label: "slice_ptr_len", + description: r##"# `slice_ptr_len` + +The tracking issue for this feature is: [#71146] + +[#71146]: https://github.com/rust-lang/rust/issues/71146 + +------------------------ +"##, + }, + Lint { + label: "slice_range", + description: r##"# `slice_range` + +The tracking issue for this feature is: [#76393] + +[#76393]: https://github.com/rust-lang/rust/issues/76393 + +------------------------ +"##, + }, + Lint { + label: "slice_split_at_unchecked", + description: r##"# `slice_split_at_unchecked` + +The tracking issue for this feature is: [#76014] + +[#76014]: https://github.com/rust-lang/rust/issues/76014 + +------------------------ +"##, + }, + Lint { + label: "slice_swap_unchecked", + description: r##"# `slice_swap_unchecked` + +The tracking issue for this feature is: [#88539] + +[#88539]: https://github.com/rust-lang/rust/issues/88539 + +------------------------ +"##, + }, + Lint { + label: "slice_take", + description: r##"# `slice_take` + +The tracking issue for this feature is: [#62280] + +[#62280]: https://github.com/rust-lang/rust/issues/62280 + +------------------------ +"##, + }, + Lint { + label: "solid_ext", + description: r##"# `solid_ext` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "sort_floats", + description: r##"# `sort_floats` + +The tracking issue for this feature is: [#93396] + +[#93396]: https://github.com/rust-lang/rust/issues/93396 + +------------------------ +"##, + }, + Lint { label: "sort_internals", description: r##"# `sort_internals` @@ -3854,6 +8000,184 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "spec_option_partial_eq", + description: r##"# `spec_option_partial_eq` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "specialization", + description: r##"# `specialization` + +The tracking issue for this feature is: [#31844] + +[#31844]: https://github.com/rust-lang/rust/issues/31844 + +------------------------ +"##, + }, + Lint { + label: "split_array", + description: r##"# `split_array` + +The tracking issue for this feature is: [#90091] + +[#90091]: https://github.com/rust-lang/rust/issues/90091 + +------------------------ +"##, + }, + Lint { + label: "split_as_slice", + description: r##"# `split_as_slice` + +The tracking issue for this feature is: [#96137] + +[#96137]: https://github.com/rust-lang/rust/issues/96137 + +------------------------ +"##, + }, + Lint { + label: "sse4a_target_feature", + description: r##"# `sse4a_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "staged_api", + description: r##"# `staged_api` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "start", + description: r##"# `start` + +The tracking issue for this feature is: [#29633] + +[#29633]: https://github.com/rust-lang/rust/issues/29633 + +------------------------ + +Allows you to mark a function as the entry point of the executable, which is +necessary in `#![no_std]` environments. + +The function marked `#[start]` is passed the command line parameters in the same +format as the C main function (aside from the integer types being used). +It has to be non-generic and have the following signature: + +```rust,ignore (only-for-syntax-highlight) +# let _: +fn(isize, *const *const u8) -> isize +# ; +``` + +This feature should not be confused with the `start` *lang item* which is +defined by the `std` crate and is written `#[lang = "start"]`. + +## Usage together with the `std` crate + +`#[start]` can be used in combination with the `std` crate, in which case the +normal `main` function (which would get called from the `std` crate) won't be +used as an entry point. +The initialization code in `std` will be skipped this way. + +Example: + +```rust +#![feature(start)] + +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} +``` + +Unwinding the stack past the `#[start]` function is currently considered +Undefined Behavior (for any unwinding implementation): + +```rust,ignore (UB) +#![feature(start)] + +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + std::panic::catch_unwind(|| { + panic!(); // panic safely gets caught or safely aborts execution + }); + + panic!(); // UB! + + 0 +} +``` +"##, + }, + Lint { + label: "std_internals", + description: r##"# `std_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "stdio_makes_pipe", + description: r##"# `stdio_makes_pipe` + +The tracking issue for this feature is: [#98288] + +[#98288]: https://github.com/rust-lang/rust/issues/98288 + +------------------------ +"##, + }, + Lint { + label: "stdsimd", + description: r##"# `stdsimd` + +The tracking issue for this feature is: [#48556] + +[#48556]: https://github.com/rust-lang/rust/issues/48556 + +------------------------ +"##, + }, + Lint { + label: "step_trait", + description: r##"# `step_trait` + +The tracking issue for this feature is: [#42168] + +[#42168]: https://github.com/rust-lang/rust/issues/42168 + +------------------------ +"##, + }, + Lint { + label: "stmt_expr_attributes", + description: r##"# `stmt_expr_attributes` + +The tracking issue for this feature is: [#15701] + +[#15701]: https://github.com/rust-lang/rust/issues/15701 + +------------------------ +"##, + }, + Lint { label: "str_internals", description: r##"# `str_internals` @@ -3863,6 +8187,186 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "str_split_inclusive_remainder", + description: r##"# `str_split_inclusive_remainder` + +The tracking issue for this feature is: [#77998] + +[#77998]: https://github.com/rust-lang/rust/issues/77998 + +------------------------ +"##, + }, + Lint { + label: "str_split_remainder", + description: r##"# `str_split_remainder` + +The tracking issue for this feature is: [#77998] + +[#77998]: https://github.com/rust-lang/rust/issues/77998 + +------------------------ +"##, + }, + Lint { + label: "str_split_whitespace_remainder", + description: r##"# `str_split_whitespace_remainder` + +The tracking issue for this feature is: [#77998] + +[#77998]: https://github.com/rust-lang/rust/issues/77998 + +------------------------ +"##, + }, + Lint { + label: "strict_provenance", + description: r##"# `strict_provenance` + +The tracking issue for this feature is: [#95228] + +[#95228]: https://github.com/rust-lang/rust/issues/95228 +----- + +The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. +These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model. +The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`). + +## Example + +```rust +#![feature(strict_provenance)] +#![warn(fuzzy_provenance_casts)] + +fn main() { + let _dangling = 16_usize as *const u8; + //~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8` +} +``` +"##, + }, + Lint { + label: "strict_provenance_atomic_ptr", + description: r##"# `strict_provenance_atomic_ptr` + +The tracking issue for this feature is: [#99108] + +[#99108]: https://github.com/rust-lang/rust/issues/99108 + +------------------------ +"##, + }, + Lint { + label: "string_deref_patterns", + description: r##"# `string_deref_patterns` + +The tracking issue for this feature is: [#87121] + +[#87121]: https://github.com/rust-lang/rust/issues/87121 + +------------------------ +"##, + }, + Lint { + label: "string_extend_from_within", + description: r##"# `string_extend_from_within` + +The tracking issue for this feature is: [#103806] + +[#103806]: https://github.com/rust-lang/rust/issues/103806 + +------------------------ +"##, + }, + Lint { + label: "string_remove_matches", + description: r##"# `string_remove_matches` + +The tracking issue for this feature is: [#72826] + +[#72826]: https://github.com/rust-lang/rust/issues/72826 + +------------------------ +"##, + }, + Lint { + label: "structural_match", + description: r##"# `structural_match` + +The tracking issue for this feature is: [#31434] + +[#31434]: https://github.com/rust-lang/rust/issues/31434 + +------------------------ +"##, + }, + Lint { + label: "sync_unsafe_cell", + description: r##"# `sync_unsafe_cell` + +The tracking issue for this feature is: [#95439] + +[#95439]: https://github.com/rust-lang/rust/issues/95439 + +------------------------ +"##, + }, + Lint { + label: "target_feature_11", + description: r##"# `target_feature_11` + +The tracking issue for this feature is: [#69098] + +[#69098]: https://github.com/rust-lang/rust/issues/69098 + +------------------------ +"##, + }, + Lint { + label: "tbm_target_feature", + description: r##"# `tbm_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "tcp_linger", + description: r##"# `tcp_linger` + +The tracking issue for this feature is: [#88494] + +[#88494]: https://github.com/rust-lang/rust/issues/88494 + +------------------------ +"##, + }, + Lint { + label: "tcp_quickack", + description: r##"# `tcp_quickack` + +The tracking issue for this feature is: [#96256] + +[#96256]: https://github.com/rust-lang/rust/issues/96256 + +------------------------ +"##, + }, + Lint { + label: "tcplistener_into_incoming", + description: r##"# `tcplistener_into_incoming` + +The tracking issue for this feature is: [#88339] + +[#88339]: https://github.com/rust-lang/rust/issues/88339 + +------------------------ +"##, + }, + Lint { label: "test", description: r##"# `test` @@ -4025,6 +8529,57 @@ even when using either of the above. "##, }, Lint { + label: "test_2018_feature", + description: r##"# `test_2018_feature` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "test_unstable_lint", + description: r##"# `test_unstable_lint` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "thin_box", + description: r##"# `thin_box` + +The tracking issue for this feature is: [#92791] + +[#92791]: https://github.com/rust-lang/rust/issues/92791 + +------------------------ +"##, + }, + Lint { + label: "thread_id_value", + description: r##"# `thread_id_value` + +The tracking issue for this feature is: [#67939] + +[#67939]: https://github.com/rust-lang/rust/issues/67939 + +------------------------ +"##, + }, + Lint { + label: "thread_local", + description: r##"# `thread_local` + +The tracking issue for this feature is: [#29594] + +[#29594]: https://github.com/rust-lang/rust/issues/29594 + +------------------------ +"##, + }, + Lint { label: "thread_local_internals", description: r##"# `thread_local_internals` @@ -4034,6 +8589,28 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "thread_sleep_until", + description: r##"# `thread_sleep_until` + +The tracking issue for this feature is: [#113752] + +[#113752]: https://github.com/rust-lang/rust/issues/113752 + +------------------------ +"##, + }, + Lint { + label: "thread_spawn_unchecked", + description: r##"# `thread_spawn_unchecked` + +The tracking issue for this feature is: [#55132] + +[#55132]: https://github.com/rust-lang/rust/issues/55132 + +------------------------ +"##, + }, + Lint { label: "trace_macros", description: r##"# `trace_macros` @@ -4077,6 +8654,17 @@ note: trace_macro "##, }, Lint { + label: "track_path", + description: r##"# `track_path` + +The tracking issue for this feature is: [#99515] + +[#99515]: https://github.com/rust-lang/rust/issues/99515 + +------------------------ +"##, + }, + Lint { label: "trait_alias", description: r##"# `trait_alias` @@ -4146,6 +8734,28 @@ let foo: &dyn Foo = bar; "##, }, Lint { + label: "transmutability", + description: r##"# `transmutability` + +The tracking issue for this feature is: [#99571] + +[#99571]: https://github.com/rust-lang/rust/issues/99571 + +------------------------ +"##, + }, + Lint { + label: "transmute_generic_consts", + description: r##"# `transmute_generic_consts` + +The tracking issue for this feature is: [#109929] + +[#109929]: https://github.com/rust-lang/rust/issues/109929 + +------------------------ +"##, + }, + Lint { label: "transparent_unions", description: r##"# `transparent_unions` @@ -4233,6 +8843,59 @@ their application of these optimizations. "##, }, Lint { + label: "trivial_bounds", + description: r##"# `trivial_bounds` + +The tracking issue for this feature is: [#48214] + +[#48214]: https://github.com/rust-lang/rust/issues/48214 + +------------------------ +"##, + }, + Lint { + label: "trusted_len", + description: r##"# `trusted_len` + +The tracking issue for this feature is: [#37572] + +[#37572]: https://github.com/rust-lang/rust/issues/37572 + +------------------------ +"##, + }, + Lint { + label: "trusted_len_next_unchecked", + description: r##"# `trusted_len_next_unchecked` + +The tracking issue for this feature is: [#37572] + +[#37572]: https://github.com/rust-lang/rust/issues/37572 + +------------------------ +"##, + }, + Lint { + label: "trusted_random_access", + description: r##"# `trusted_random_access` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "trusted_step", + description: r##"# `trusted_step` + +The tracking issue for this feature is: [#85731] + +[#85731]: https://github.com/rust-lang/rust/issues/85731 + +------------------------ +"##, + }, + Lint { label: "try_blocks", description: r##"# `try_blocks` @@ -4267,6 +8930,92 @@ assert!(result.is_err()); "##, }, Lint { + label: "try_find", + description: r##"# `try_find` + +The tracking issue for this feature is: [#63178] + +[#63178]: https://github.com/rust-lang/rust/issues/63178 + +------------------------ +"##, + }, + Lint { + label: "try_reserve_kind", + description: r##"# `try_reserve_kind` + +The tracking issue for this feature is: [#48043] + +[#48043]: https://github.com/rust-lang/rust/issues/48043 + +------------------------ +"##, + }, + Lint { + label: "try_trait_v2", + description: r##"# `try_trait_v2` + +The tracking issue for this feature is: [#84277] + +[#84277]: https://github.com/rust-lang/rust/issues/84277 + +------------------------ +"##, + }, + Lint { + label: "try_trait_v2_residual", + description: r##"# `try_trait_v2_residual` + +The tracking issue for this feature is: [#91285] + +[#91285]: https://github.com/rust-lang/rust/issues/91285 + +------------------------ +"##, + }, + Lint { + label: "try_trait_v2_yeet", + description: r##"# `try_trait_v2_yeet` + +The tracking issue for this feature is: [#96374] + +[#96374]: https://github.com/rust-lang/rust/issues/96374 + +------------------------ +"##, + }, + Lint { + label: "tuple_trait", + description: r##"# `tuple_trait` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "type_alias_impl_trait", + description: r##"# `type_alias_impl_trait` + +The tracking issue for this feature is: [#63063] + +[#63063]: https://github.com/rust-lang/rust/issues/63063 + +------------------------ +"##, + }, + Lint { + label: "type_ascription", + description: r##"# `type_ascription` + +The tracking issue for this feature is: [#23416] + +[#23416]: https://github.com/rust-lang/rust/issues/23416 + +------------------------ +"##, + }, + Lint { label: "type_changing_struct_update", description: r##"# `type_changing_struct_update` @@ -4304,6 +9053,39 @@ fn main () { "##, }, Lint { + label: "type_name_of_val", + description: r##"# `type_name_of_val` + +The tracking issue for this feature is: [#66359] + +[#66359]: https://github.com/rust-lang/rust/issues/66359 + +------------------------ +"##, + }, + Lint { + label: "type_privacy_lints", + description: r##"# `type_privacy_lints` + +The tracking issue for this feature is: [#48054] + +[#48054]: https://github.com/rust-lang/rust/issues/48054 + +------------------------ +"##, + }, + Lint { + label: "uefi_std", + description: r##"# `uefi_std` + +The tracking issue for this feature is: [#100499] + +[#100499]: https://github.com/rust-lang/rust/issues/100499 + +------------------------ +"##, + }, + Lint { label: "unboxed_closures", description: r##"# `unboxed_closures` @@ -4319,7 +9101,7 @@ The `unboxed_closures` feature allows you to write functions using the `"rust-ca required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have exactly one (non self) argument, a tuple representing the argument list. -[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html +[`Fn*`]: ../../std/ops/trait.Fn.html ```rust #![feature(unboxed_closures)] @@ -4333,6 +9115,200 @@ fn main() {} "##, }, Lint { + label: "unchecked_math", + description: r##"# `unchecked_math` + +The tracking issue for this feature is: [#85122] + +[#85122]: https://github.com/rust-lang/rust/issues/85122 + +------------------------ +"##, + }, + Lint { + label: "unicode_internals", + description: r##"# `unicode_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "unique_rc_arc", + description: r##"# `unique_rc_arc` + +The tracking issue for this feature is: [#112566] + +[#112566]: https://github.com/rust-lang/rust/issues/112566 + +------------------------ +"##, + }, + Lint { + label: "unix_file_vectored_at", + description: r##"# `unix_file_vectored_at` + +The tracking issue for this feature is: [#89517] + +[#89517]: https://github.com/rust-lang/rust/issues/89517 + +------------------------ +"##, + }, + Lint { + label: "unix_set_mark", + description: r##"# `unix_set_mark` + +The tracking issue for this feature is: [#96467] + +[#96467]: https://github.com/rust-lang/rust/issues/96467 + +------------------------ +"##, + }, + Lint { + label: "unix_sigpipe", + description: r##"# `unix_sigpipe` + +The tracking issue for this feature is: [#97889] + +[#97889]: https://github.com/rust-lang/rust/issues/97889 + +--- + +The `#[unix_sigpipe = "..."]` attribute on `fn main()` can be used to specify how libstd shall setup `SIGPIPE` on Unix platforms before invoking `fn main()`. This attribute is ignored on non-Unix targets. There are three variants: +* `#[unix_sigpipe = "inherit"]` +* `#[unix_sigpipe = "sig_dfl"]` +* `#[unix_sigpipe = "sig_ign"]` + +## `#[unix_sigpipe = "inherit"]` + +Leave `SIGPIPE` untouched before entering `fn main()`. Unless the parent process has changed the default `SIGPIPE` handler from `SIG_DFL` to something else, this will behave the same as `#[unix_sigpipe = "sig_dfl"]`. + +## `#[unix_sigpipe = "sig_dfl"]` + +Set the `SIGPIPE` handler to `SIG_DFL`. This will result in your program getting killed if it tries to write to a closed pipe. This is normally what you want if your program produces textual output. + +### Example + +```rust,no_run +#![feature(unix_sigpipe)] +#[unix_sigpipe = "sig_dfl"] +fn main() { loop { println!("hello world"); } } +``` + +```bash +% ./main | head -n 1 +hello world +``` + +## `#[unix_sigpipe = "sig_ign"]` + +Set the `SIGPIPE` handler to `SIG_IGN` before invoking `fn main()`. This will result in `ErrorKind::BrokenPipe` errors if you program tries to write to a closed pipe. This is normally what you want if you for example write socket servers, socket clients, or pipe peers. + +This is what libstd has done by default since 2014. (However, see the note on child processes below.) + +### Example + +```rust,no_run +#![feature(unix_sigpipe)] +#[unix_sigpipe = "sig_ign"] +fn main() { loop { println!("hello world"); } } +``` + +```bash +% ./main | head -n 1 +hello world +thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', library/std/src/io/stdio.rs:1016:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +``` + +### Note on child processes + +When spawning child processes, the legacy Rust behavior if `#[unix_sigpipe]` is not specified is to +reset `SIGPIPE` to `SIG_DFL`. + +If `#[unix_sigpipe = "..."]` is specified, no matter what its value is, the signal disposition of +`SIGPIPE` is no longer reset. This means that the child inherits the parent's `SIGPIPE` behavior. +"##, + }, + Lint { + label: "unix_socket_ancillary_data", + description: r##"# `unix_socket_ancillary_data` + +The tracking issue for this feature is: [#76915] + +[#76915]: https://github.com/rust-lang/rust/issues/76915 + +------------------------ +"##, + }, + Lint { + label: "unix_socket_peek", + description: r##"# `unix_socket_peek` + +The tracking issue for this feature is: [#76923] + +[#76923]: https://github.com/rust-lang/rust/issues/76923 + +------------------------ +"##, + }, + Lint { + label: "unnamed_fields", + description: r##"# `unnamed_fields` + +The tracking issue for this feature is: [#49804] + +[#49804]: https://github.com/rust-lang/rust/issues/49804 + +------------------------ +"##, + }, + Lint { + label: "unsafe_cell_from_mut", + description: r##"# `unsafe_cell_from_mut` + +The tracking issue for this feature is: [#111645] + +[#111645]: https://github.com/rust-lang/rust/issues/111645 + +------------------------ +"##, + }, + Lint { + label: "unsafe_pin_internals", + description: r##"# `unsafe_pin_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "unsize", + description: r##"# `unsize` + +The tracking issue for this feature is: [#18598] + +[#18598]: https://github.com/rust-lang/rust/issues/18598 + +------------------------ +"##, + }, + Lint { + label: "unsized_fn_params", + description: r##"# `unsized_fn_params` + +The tracking issue for this feature is: [#48055] + +[#48055]: https://github.com/rust-lang/rust/issues/48055 + +------------------------ +"##, + }, + Lint { label: "unsized_locals", description: r##"# `unsized_locals` @@ -4543,6 +9519,17 @@ fn main() { "##, }, Lint { + label: "unwrap_infallible", + description: r##"# `unwrap_infallible` + +The tracking issue for this feature is: [#61695] + +[#61695]: https://github.com/rust-lang/rust/issues/61695 + +------------------------ +"##, + }, + Lint { label: "update_panic_count", description: r##"# `update_panic_count` @@ -4552,6 +9539,149 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "used_with_arg", + description: r##"# `used_with_arg` + +The tracking issue for this feature is: [#93798] + +[#93798]: https://github.com/rust-lang/rust/issues/93798 + +------------------------ +"##, + }, + Lint { + label: "utf16_extra", + description: r##"# `utf16_extra` + +The tracking issue for this feature is: [#94919] + +[#94919]: https://github.com/rust-lang/rust/issues/94919 + +------------------------ +"##, + }, + Lint { + label: "utf16_extra_const", + description: r##"# `utf16_extra_const` + +The tracking issue for this feature is: [#94919] + +[#94919]: https://github.com/rust-lang/rust/issues/94919 + +------------------------ +"##, + }, + Lint { + label: "utf8_chunks", + description: r##"# `utf8_chunks` + +The tracking issue for this feature is: [#99543] + +[#99543]: https://github.com/rust-lang/rust/issues/99543 + +------------------------ +"##, + }, + Lint { + label: "variant_count", + description: r##"# `variant_count` + +The tracking issue for this feature is: [#73662] + +[#73662]: https://github.com/rust-lang/rust/issues/73662 + +------------------------ +"##, + }, + Lint { + label: "vec_into_raw_parts", + description: r##"# `vec_into_raw_parts` + +The tracking issue for this feature is: [#65816] + +[#65816]: https://github.com/rust-lang/rust/issues/65816 + +------------------------ +"##, + }, + Lint { + label: "vec_push_within_capacity", + description: r##"# `vec_push_within_capacity` + +The tracking issue for this feature is: [#100486] + +[#100486]: https://github.com/rust-lang/rust/issues/100486 + +------------------------ +"##, + }, + Lint { + label: "vec_split_at_spare", + description: r##"# `vec_split_at_spare` + +The tracking issue for this feature is: [#81944] + +[#81944]: https://github.com/rust-lang/rust/issues/81944 + +------------------------ +"##, + }, + Lint { + label: "waker_getters", + description: r##"# `waker_getters` + +The tracking issue for this feature is: [#87021] + +[#87021]: https://github.com/rust-lang/rust/issues/87021 + +------------------------ +"##, + }, + Lint { + label: "wasi_ext", + description: r##"# `wasi_ext` + +The tracking issue for this feature is: [#71213] + +[#71213]: https://github.com/rust-lang/rust/issues/71213 + +------------------------ +"##, + }, + Lint { + label: "wasm_abi", + description: r##"# `wasm_abi` + +The tracking issue for this feature is: [#83788] + +[#83788]: https://github.com/rust-lang/rust/issues/83788 + +------------------------ +"##, + }, + Lint { + label: "wasm_target_feature", + description: r##"# `wasm_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "windows_by_handle", + description: r##"# `windows_by_handle` + +The tracking issue for this feature is: [#63010] + +[#63010]: https://github.com/rust-lang/rust/issues/63010 + +------------------------ +"##, + }, + Lint { label: "windows_c", description: r##"# `windows_c` @@ -4579,6 +9709,59 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { + label: "windows_process_exit_code_from", + description: r##"# `windows_process_exit_code_from` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "windows_process_extensions_async_pipes", + description: r##"# `windows_process_extensions_async_pipes` + +The tracking issue for this feature is: [#98289] + +[#98289]: https://github.com/rust-lang/rust/issues/98289 + +------------------------ +"##, + }, + Lint { + label: "windows_process_extensions_force_quotes", + description: r##"# `windows_process_extensions_force_quotes` + +The tracking issue for this feature is: [#82227] + +[#82227]: https://github.com/rust-lang/rust/issues/82227 + +------------------------ +"##, + }, + Lint { + label: "windows_process_extensions_main_thread_handle", + description: r##"# `windows_process_extensions_main_thread_handle` + +The tracking issue for this feature is: [#96723] + +[#96723]: https://github.com/rust-lang/rust/issues/96723 + +------------------------ +"##, + }, + Lint { + label: "windows_process_extensions_raw_attribute", + description: r##"# `windows_process_extensions_raw_attribute` + +The tracking issue for this feature is: [#114854] + +[#114854]: https://github.com/rust-lang/rust/issues/114854 + +------------------------ +"##, + }, + Lint { label: "windows_stdio", description: r##"# `windows_stdio` @@ -4587,10 +9770,95 @@ This feature is internal to the Rust compiler and is not intended for general us ------------------------ "##, }, + Lint { + label: "with_negative_coherence", + description: r##"# `with_negative_coherence` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "wrapping_int_impl", + description: r##"# `wrapping_int_impl` + +The tracking issue for this feature is: [#32463] + +[#32463]: https://github.com/rust-lang/rust/issues/32463 + +------------------------ +"##, + }, + Lint { + label: "wrapping_next_power_of_two", + description: r##"# `wrapping_next_power_of_two` + +The tracking issue for this feature is: [#32463] + +[#32463]: https://github.com/rust-lang/rust/issues/32463 + +------------------------ +"##, + }, + Lint { + label: "write_all_vectored", + description: r##"# `write_all_vectored` + +The tracking issue for this feature is: [#70436] + +[#70436]: https://github.com/rust-lang/rust/issues/70436 + +------------------------ +"##, + }, + Lint { + label: "yeet_desugar_details", + description: r##"# `yeet_desugar_details` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "yeet_expr", + description: r##"# `yeet_expr` + +The tracking issue for this feature is: [#96373] + +[#96373]: https://github.com/rust-lang/rust/issues/96373 + +------------------------ + +The `yeet_expr` feature adds support for `do yeet` expressions, +which can be used to early-exit from a function or `try` block. + +These are highly experimental, thus the placeholder syntax. + +```rust,edition2021 +#![feature(yeet_expr)] + +fn foo() -> Result<String, i32> { + do yeet 4; +} +assert_eq!(foo(), Err(4)); + +fn bar() -> Option<String> { + do yeet; +} +assert_eq!(bar(), None); +``` +"##, + }, ]; pub const CLIPPY_LINTS: &[Lint] = &[ Lint { + label: "clippy::absolute_paths", + description: r##"Checks for usage of items through absolute paths, like `std::env::current_dir`."##, + }, + Lint { label: "clippy::absurd_extreme_comparisons", description: r##"Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a @@ -4598,6 +9866,33 @@ case that is always true or always false. Only integer and boolean types are checked."##, }, Lint { + label: "clippy::alloc_instead_of_core", + description: r##"Finds items imported through `alloc` when available through `core`."##, + }, + Lint { + label: "clippy::allow_attributes", + description: r##"Checks for usage of the `#[allow]` attribute and suggests replacing it with +the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + +The expect attribute is still unstable and requires the `lint_reasons` +on nightly. It can be enabled by adding `#![feature(lint_reasons)]` to +the crate root. + +This lint only warns outer attributes (`#[allow]`), as inner attributes +(`#![allow]`) are usually used to enable or disable lints on a global scale."##, + }, + Lint { + label: "clippy::allow_attributes_without_reason", + description: r##"Checks for attributes that allow lints without a reason. + +(This requires the `lint_reasons` feature)"##, + }, + Lint { + label: "clippy::almost_complete_range", + description: r##"Checks for ranges which almost include the entire range of letters from 'a' to 'z' +or digits from '0' to '9', but don't because they're a half open range."##, + }, + Lint { label: "clippy::almost_swapped", description: r##"Checks for `foo = bar; bar = foo` sequences."##, }, @@ -4611,22 +9906,50 @@ or respectively, suggesting to use the predefined constant."##, }, Lint { + label: "clippy::arc_with_non_send_sync", + description: r##". +This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`."##, + }, + Lint { + label: "clippy::arithmetic_side_effects", + description: r##"Checks any kind of arithmetic operation of any type. + +Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust +Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), +or can panic (`/`, `%`). + +Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant +environments, allowed types and non-constant operations that won't overflow are ignored."##, + }, + Lint { label: "clippy::as_conversions", description: r##"Checks for usage of `as` conversions. Note that this lint is specialized in linting *every single* use of `as` regardless of whether good alternatives exist or not. If you want more precise lints for `as`, please consider using these separate lints: -`unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`, +`unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`, `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. There is a good explanation the reason why this lint should work in this way and how it is useful [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122)."##, }, Lint { + label: "clippy::as_ptr_cast_mut", + description: r##"Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer"##, + }, + Lint { + label: "clippy::as_underscore", + description: r##"Checks for the usage of `as _` conversion using inferred type."##, + }, + Lint { label: "clippy::assertions_on_constants", description: r##"Checks for `assert!(true)` and `assert!(false)` calls."##, }, Lint { + label: "clippy::assertions_on_result_states", + description: r##"Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls."##, + }, + Lint { label: "clippy::assign_op_pattern", description: r##"Checks for `a = a op b` or `a = b commutative_op a` patterns."##, @@ -4641,14 +9964,17 @@ patterns."##, that can themselves be awaited."##, }, Lint { + label: "clippy::await_holding_invalid_type", + description: r##"Allows users to configure types which should not be held across `await` +suspension points."##, + }, + Lint { label: "clippy::await_holding_lock", - description: r##"Checks for calls to await while holding a -non-async-aware MutexGuard."##, + description: r##"Checks for calls to await while holding a non-async-aware MutexGuard."##, }, Lint { label: "clippy::await_holding_refcell_ref", - description: r##"Checks for calls to await while holding a -`RefCell` `Ref` or `RefMut`."##, + description: r##"Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`."##, }, Lint { label: "clippy::bad_bit_mask", @@ -4659,14 +9985,18 @@ The formula for detecting if an expression of the type `_ <bit_op> m {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following table: -|Comparison |Bit Op|Example |is always|Formula | -|------------|------|------------|---------|----------------------| -|`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` | -|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | -|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | -|`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` | -|`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` | -|`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |"##, +|Comparison |Bit Op|Example |is always|Formula | +|------------|------|-------------|---------|----------------------| +|`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` | +|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | +|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | +|`==` or `!=`| `\\|` |`x \\| 1 == 0`|`false` |`c \\| m != c` | +|`<` or `>=`| `\\|` |`x \\| 1 < 1` |`false` |`m >= c` | +|`<=` or `>` | `\\|` |`x \\| 1 > 0` |`true` |`m > c` |"##, + }, + Lint { + label: "clippy::big_endian_bytes", + description: r##"Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`."##, }, Lint { label: "clippy::bind_instead_of_map", @@ -4674,11 +10004,6 @@ table: `_.or_else(|x| Err(y))`."##, }, Lint { - label: "clippy::blacklisted_name", - description: r##"Checks for usage of blacklisted names for variables, such -as `foo`."##, - }, - Lint { label: "clippy::blanket_clippy_restriction_lints", description: r##"Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category."##, }, @@ -4698,21 +10023,38 @@ expression, statements or conditions that use closures with blocks."##, suggest using the variable directly."##, }, Lint { + label: "clippy::bool_to_int_with_if", + description: r##"Instead of using an if statement to convert a bool to an int, +this lint suggests using a `from()` function or an `as` coercion."##, + }, + Lint { + label: "clippy::borrow_as_ptr", + description: r##"Checks for the usage of `&expr as *const T` or +`&mut expr as *mut T`, and suggest using `ptr::addr_of` or +`ptr::addr_of_mut` instead."##, + }, + Lint { label: "clippy::borrow_deref_ref", description: r##"Checks for `&*(&T)`."## }, + Lint { label: "clippy::borrow_interior_mutable_const", description: r##"Checks if `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly."##, }, Lint { label: "clippy::borrowed_box", - description: r##"Checks for use of `&Box<T>` anywhere in the code. + description: r##"Checks for usage of `&Box<T>` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, }, Lint { label: "clippy::box_collection", - description: r##"Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code. + description: r##"Checks for usage of `Box<T>` where T is a collection such as Vec anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, }, Lint { + label: "clippy::box_default", + description: r##"checks for `Box::new(T::default())`, which is better written as +`Box::<T>::default()`."##, + }, + Lint { label: "clippy::boxed_local", description: r##"Checks for usage of `Box<T>` where an unboxed `T` would work fine."##, @@ -4727,6 +10069,11 @@ moved out of the blocks."##, description: r##"Warns if a generic shadows a built-in type."##, }, Lint { + label: "clippy::bytes_count_to_len", + description: r##"It checks for `str::bytes().count()` and suggests replacing it with +`str::len()`."##, + }, + Lint { label: "clippy::bytes_nth", description: r##"Checks for the use of `.bytes().nth()`."##, }, @@ -4741,22 +10088,41 @@ moved out of the blocks."##, and suggests to use a case-insensitive approach instead."##, }, Lint { + label: "clippy::cast_abs_to_unsigned", + description: r##"Checks for usage of the `abs()` method that cast the result to unsigned."##, + }, + Lint { + label: "clippy::cast_enum_constructor", + description: r##"Checks for casts from an enum tuple constructor to an integer."##, + }, + Lint { + label: "clippy::cast_enum_truncation", + description: r##"Checks for casts from an enum type to an integral type which will definitely truncate the +value."##, + }, + Lint { label: "clippy::cast_lossless", description: r##"Checks for casts between numerical types that may be replaced by safe conversion functions."##, }, Lint { + label: "clippy::cast_nan_to_int", + description: r##"Checks for a known NaN float being cast to an integer"##, + }, + Lint { label: "clippy::cast_possible_truncation", description: r##"Checks for casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by -default."##, +default. It suggests user either explicitly ignore the lint, +or use `try_from()` and handle the truncation, default, or panic explicitly."##, }, Lint { label: "clippy::cast_possible_wrap", description: r##"Checks for casts from an unsigned type to a signed type of -the same size. Performing such a cast is a 'no-op' for the compiler, -i.e., nothing is changed at the bit level, and the binary representation of -the value is reinterpreted. This can cause wrapping if the value is too big +the same size, or possibly smaller due to target dependent integers. +Performing such a cast is a 'no-op' for the compiler, i.e., nothing is +changed at the bit level, and the binary representation of the value is +reinterpreted. This can cause wrapping if the value is too big for the target signed type. However, the cast works as defined, so this lint is `Allow` by default."##, }, @@ -4776,10 +10142,6 @@ or any 64-bit integer to `f64`."##, from a less-strictly-aligned pointer to a more-strictly-aligned pointer"##, }, Lint { - label: "clippy::cast_ref_to_mut", - description: r##"Checks for casts of `&T` to `&mut T` anywhere in the code."##, - }, - Lint { label: "clippy::cast_sign_loss", description: r##"Checks for casts from a signed to an unsigned numerical type. In this case, negative values wrap around to large positive values, @@ -4787,6 +10149,14 @@ which can be quite surprising in practice. However, as the cast works as defined, this lint is `Allow` by default."##, }, Lint { + label: "clippy::cast_slice_different_sizes", + description: r##"Checks for `as` casts between raw pointers to slices with differently sized elements."##, + }, + Lint { + label: "clippy::cast_slice_from_raw_parts", + description: r##"Checks for a raw slice being cast to a slice pointer"##, + }, + Lint { label: "clippy::char_lit_as_u8", description: r##"Checks for expressions where a character literal is cast to `u8` and suggests using a byte literal instead."##, @@ -4806,8 +10176,8 @@ if it starts with a given char."##, description: r##"Checks for explicit bounds checking when casting."##, }, Lint { - label: "clippy::clone_double_ref", - description: r##"Checks for usage of `.clone()` on an `&&T`."##, + label: "clippy::clear_with_drain", + description: r##"Checks for usage of `.drain(..)` for the sole purpose of clearing a container."##, }, Lint { label: "clippy::clone_on_copy", @@ -4821,10 +10191,9 @@ function syntax instead (e.g., `Rc::clone(foo)`)."##, }, Lint { label: "clippy::cloned_instead_of_copied", - description: r##"Checks for usages of `cloned()` on an `Iterator` or `Option` where + description: r##"Checks for usage of `cloned()` on an `Iterator` or `Option` where `copied()` could be used instead."##, }, - Lint { label: "clippy::cmp_nan", description: r##"Checks for comparisons to NaN."## }, Lint { label: "clippy::cmp_null", description: r##"This lint checks for equality comparisons with `ptr::null`"##, @@ -4857,6 +10226,15 @@ Note that this lint is not intended to find _all_ cases where nested match patte cases where merging would most likely make the code more readable."##, }, Lint { + label: "clippy::collapsible_str_replace", + description: r##"Checks for consecutive calls to `str::replace` (2 or more) +that can be collapsed into a single call."##, + }, + Lint { + label: "clippy::collection_is_never_read", + description: r##"Checks for collections that are never queried."##, + }, + Lint { label: "clippy::comparison_chain", description: r##"Checks comparison chains written with `if` that can be rewritten with `match` and `cmp`."##, @@ -4872,6 +10250,10 @@ and suggests using `.is_empty()` where applicable."##, `Iterator`."##, }, Lint { + label: "clippy::crate_in_macro_def", + description: r##"Checks for usage of `crate` as opposed to `$crate` in a macro definition."##, + }, + Lint { label: "clippy::create_dir", description: r##"Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead."##, }, @@ -4879,7 +10261,10 @@ and suggests using `.is_empty()` where applicable."##, label: "clippy::crosspointer_transmute", description: r##"Checks for transmutes between a type `T` and `*T`."##, }, - Lint { label: "clippy::dbg_macro", description: r##"Checks for usage of dbg!() macro."## }, + Lint { + label: "clippy::dbg_macro", + description: r##"Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro."##, + }, Lint { label: "clippy::debug_assert_with_mut_call", description: r##"Checks for function/method calls with a mutable @@ -4895,6 +10280,15 @@ parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros." mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.)."##, }, Lint { + label: "clippy::default_constructed_unit_structs", + description: r##"Checks for construction on unit struct using `default`."##, + }, + Lint { + label: "clippy::default_instead_of_iter_empty", + description: r##"It checks for `std::iter::Empty::default()` and suggests replacing it with +`std::iter::empty()`."##, + }, + Lint { label: "clippy::default_numeric_fallback", description: r##"Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference. @@ -4910,6 +10304,10 @@ See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-in description: r##"Checks for literal calls to `Default::default()`."##, }, Lint { + label: "clippy::default_union_representation", + description: r##"Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute)."##, + }, + Lint { label: "clippy::deprecated_cfg_attr", description: r##"Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it with `#[rustfmt::skip]`."##, @@ -4924,22 +10322,46 @@ field that is not a valid semantic version."##, description: r##"Checks for usage of `*&` and `*&mut` in expressions."##, }, Lint { + label: "clippy::deref_by_slicing", + description: r##"Checks for slicing expressions which are equivalent to dereferencing the +value."##, + }, + Lint { label: "clippy::derivable_impls", description: r##"Detects manual `std::default::Default` implementations that are identical to a derived implementation."##, }, Lint { - label: "clippy::derive_hash_xor_eq", - description: r##"Checks for deriving `Hash` but implementing `PartialEq` -explicitly or vice versa."##, + label: "clippy::derive_ord_xor_partial_ord", + description: r##"Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` +or `PartialOrd` implementation."##, }, Lint { - label: "clippy::derive_ord_xor_partial_ord", - description: r##"Checks for deriving `Ord` but implementing `PartialOrd` -explicitly or vice versa."##, + label: "clippy::derive_partial_eq_without_eq", + description: r##"Checks for types that derive `PartialEq` and could implement `Eq`."##, + }, + Lint { + label: "clippy::derived_hash_with_manual_eq", + description: r##"Lints against manual `PartialEq` implementations for types with a derived `Hash` +implementation."##, + }, + Lint { + label: "clippy::disallowed_macros", + description: r##"Denies the configured macros in clippy.toml + +Note: Even though this lint is warn-by-default, it will only trigger if +macros are defined in the clippy.toml file."##, }, Lint { label: "clippy::disallowed_methods", - description: r##"Denies the configured methods and functions in clippy.toml"##, + description: r##"Denies the configured methods and functions in clippy.toml + +Note: Even though this lint is warn-by-default, it will only trigger if +methods are defined in the clippy.toml file."##, + }, + Lint { + label: "clippy::disallowed_names", + description: r##"Checks for usage of disallowed names for variables, such +as `foo`."##, }, Lint { label: "clippy::disallowed_script_idents", @@ -4958,7 +10380,10 @@ See also: [`non_ascii_idents`]. }, Lint { label: "clippy::disallowed_types", - description: r##"Denies the configured types in clippy.toml."##, + description: r##"Denies the configured types in clippy.toml. + +Note: Even though this lint is warn-by-default, it will only trigger if +types are defined in the clippy.toml file."##, }, Lint { label: "clippy::diverging_sub_expression", @@ -4966,6 +10391,11 @@ See also: [`non_ascii_idents`]. statements."##, }, Lint { + label: "clippy::doc_link_with_quotes", + description: r##"Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) +outside of code blocks"##, + }, + Lint { label: "clippy::doc_markdown", description: r##"Checks for the presence of `_`, `::` or camel-case words outside ticks in documentation."##, @@ -4989,14 +10419,19 @@ marked as `#[must_use]`."##, description: r##"Checks for unnecessary double parentheses."##, }, Lint { - label: "clippy::drop_copy", - description: r##"Checks for calls to `std::mem::drop` with a value -that derives the Copy trait"##, + label: "clippy::drain_collect", + description: r##"Checks for calls to `.drain()` that clear the collection, immediately followed by a call to `.collect()`. + +> Collection in this context refers to any type with a `drain` method: +> `Vec`, `VecDeque`, `BinaryHeap`, `HashSet`,`HashMap`, `String`"##, }, Lint { - label: "clippy::drop_ref", - description: r##"Checks for calls to `std::mem::drop` with a reference -instead of an owned value."##, + label: "clippy::drop_non_drop", + description: r##"Checks for calls to `std::mem::drop` with a value that does not implement `Drop`."##, + }, + Lint { + label: "clippy::duplicate_mod", + description: r##"Checks for files that are included as modules multiple times."##, }, Lint { label: "clippy::duplicate_underscore_argument", @@ -5014,6 +10449,10 @@ from other `Duration` methods."##, but without a final `else` branch."##, }, Lint { + label: "clippy::empty_drop", + description: r##"Checks for empty `Drop` implementations."##, + }, + Lint { label: "clippy::empty_enum", description: r##"Checks for `enum`s with no variants. @@ -5022,11 +10461,19 @@ nightly-only experimental API. Therefore, this lint is only triggered if the `never_type` is enabled."##, }, Lint { + label: "clippy::empty_line_after_doc_comments", + description: r##"Checks for empty lines after documenation comments."##, + }, + Lint { label: "clippy::empty_line_after_outer_attr", description: r##"Checks for empty lines after outer attributes"##, }, Lint { label: "clippy::empty_loop", description: r##"Checks for empty `loop` expressions."## }, Lint { + label: "clippy::empty_structs_with_brackets", + description: r##"Finds structs without fields (a so-called empty struct) that are declared with brackets."##, + }, + Lint { label: "clippy::enum_clike_unportable_variant", description: r##"Checks for C-like enumerations that are `repr(isize/usize)` and have values that don't fit into an `i32`."##, @@ -5052,10 +10499,18 @@ bitwise, difference and division binary operators (`==`, `>`, etc., `&&`, description: r##"Checks for erasing operations, e.g., `x * 0`."##, }, Lint { - label: "clippy::eval_order_dependence", - description: r##"Checks for a read and a write to the same variable where -whether the read occurs before or after the write depends on the evaluation -order of sub-expressions."##, + label: "clippy::err_expect", + description: r##"Checks for `.err().expect()` calls on the `Result` type."##, + }, + Lint { + label: "clippy::error_impl_error", + description: r##"Checks for types named `Error` that implement `Error`."##, + }, + Lint { + label: "clippy::excessive_nesting", + description: r##"Checks for blocks which are nested beyond a certain threshold. + +Note: Even though this lint is warn-by-default, it will only trigger if a maximum nesting level is defined in the clippy.toml file."##, }, Lint { label: "clippy::excessive_precision", @@ -5072,8 +10527,7 @@ than that supported by the underlying type."##, }, Lint { label: "clippy::exit", - description: r##"`exit()` terminates the program and doesn't provide a -stack trace."##, + description: r##"Detects calls to the `exit()` function which terminates the program."##, }, Lint { label: "clippy::expect_fun_call", @@ -5082,7 +10536,7 @@ etc., and suggests to use `unwrap_or_else` instead"##, }, Lint { label: "clippy::expect_used", - description: r##"Checks for `.expect()` calls on `Option`s and `Result`s."##, + description: r##"Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s."##, }, Lint { label: "clippy::expl_impl_clone_on_copy", @@ -5090,6 +10544,10 @@ etc., and suggests to use `unwrap_or_else` instead"##, types."##, }, Lint { + label: "clippy::explicit_auto_deref", + description: r##"Checks for dereferencing expressions which would be covered by auto-deref."##, + }, + Lint { label: "clippy::explicit_counter_loop", description: r##"Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`."##, @@ -5127,6 +10585,10 @@ replaced with `(e)print!()` / `(e)println!()`"##, anywhere else."##, }, Lint { + label: "clippy::extra_unused_type_parameters", + description: r##"Checks for type parameters in generics that are never used anywhere else."##, + }, + Lint { label: "clippy::fallible_impl_from", description: r##"Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`"##, }, @@ -5144,6 +10606,10 @@ with Default::default()."##, description: r##"Nothing. This lint has been deprecated."##, }, Lint { + label: "clippy::filter_map_bool_then", + description: r##"Checks for usage of `bool::then` in `Iterator::filter_map`."##, + }, + Lint { label: "clippy::filter_map_identity", description: r##"Checks for usage of `filter_map(|x| x)`."##, }, @@ -5162,7 +10628,7 @@ with Default::default()."##, }, Lint { label: "clippy::flat_map_option", - description: r##"Checks for usages of `Iterator::flat_map()` where `filter_map()` could be + description: r##"Checks for usage of `Iterator::flat_map()` where `filter_map()` could be used instead."##, }, Lint { label: "clippy::float_arithmetic", description: r##"Checks for float arithmetic."## }, @@ -5211,18 +10677,12 @@ store address."##, ignoring either the keys or values."##, }, Lint { - label: "clippy::for_loops_over_fallibles", - description: r##"Checks for `for` loops over `Option` or `Result` values."##, + label: "clippy::forget_non_drop", + description: r##"Checks for calls to `std::mem::forget` with a value that does not implement `Drop`."##, }, Lint { - label: "clippy::forget_copy", - description: r##"Checks for calls to `std::mem::forget` with a value that -derives the Copy trait"##, - }, - Lint { - label: "clippy::forget_ref", - description: r##"Checks for calls to `std::mem::forget` with a reference -instead of an owned value."##, + label: "clippy::format_collect", + description: r##"Checks for usage of `.map(|_| format!(..)).collect::<String>()`."##, }, Lint { label: "clippy::format_in_format_args", @@ -5231,6 +10691,15 @@ formatting such as `format!` itself, `write!` or `println!`. Suggests inlining the `format!` call."##, }, Lint { + label: "clippy::format_push_string", + description: r##"Detects cases where the result of a `format!` call is +appended to an existing `String`."##, + }, + Lint { + label: "clippy::four_forward_slashes", + description: r##"Checks for outer doc comments written with 4 forward slashes (`////`)."##, + }, + Lint { label: "clippy::from_iter_instead_of_collect", description: r##"Checks for `from_iter()` function calls on types that implement the `FromIterator` trait."##, @@ -5240,6 +10709,10 @@ trait."##, description: r##"Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead."##, }, Lint { + label: "clippy::from_raw_with_void_ptr", + description: r##"Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)`"##, + }, + Lint { label: "clippy::from_str_radix_10", description: r##"Checks for function invocations of the form `primitive::from_str_radix(s, 10)`"##, }, @@ -5251,16 +10724,25 @@ used by library authors (public and internal) that target an audience where multithreaded executors are likely to be used for running these Futures."##, }, Lint { + label: "clippy::get_first", + description: r##"Checks for usage of `x.get(0)` instead of +`x.first()`."##, + }, + Lint { label: "clippy::get_last_with_len", - description: r##"Checks for using `x.get(x.len() - 1)` instead of + description: r##"Checks for usage of `x.get(x.len() - 1)` instead of `x.last()`."##, }, Lint { label: "clippy::get_unwrap", - description: r##"Checks for use of `.get().unwrap()` (or + description: r##"Checks for usage of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`"##, }, Lint { + label: "clippy::host_endian_bytes", + description: r##"Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`."##, + }, + Lint { label: "clippy::identity_op", description: r##"Checks for identity operations, e.g., `x + 0`."##, }, @@ -5285,13 +10767,21 @@ and the *else* part."##, }, Lint { label: "clippy::if_then_some_else_none", - description: r##"Checks for if-else that could be written to `bool::then`."##, + description: r##"Checks for if-else that could be written using either `bool::then` or `bool::then_some`."##, }, Lint { label: "clippy::ifs_same_cond", description: r##"Checks for consecutive `if`s with the same condition."##, }, Lint { + label: "clippy::ignored_unit_patterns", + description: r##"Checks for usage of `_` in patterns of type `()`."##, + }, + Lint { + label: "clippy::impl_trait_in_params", + description: r##"Lints when `impl Trait` is being used in a function's parameters."##, + }, + Lint { label: "clippy::implicit_clone", description: r##"Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer."##, }, @@ -5306,10 +10796,25 @@ algorithm (`SipHash`)."##, description: r##"Checks for missing return statements at the end of a block."##, }, Lint { + label: "clippy::implicit_saturating_add", + description: r##"Checks for implicit saturating addition."##, + }, + Lint { label: "clippy::implicit_saturating_sub", description: r##"Checks for implicit saturating subtraction."##, }, Lint { + label: "clippy::implied_bounds_in_impls", + description: r##"Looks for bounds in `impl Trait` in return position that are implied by other bounds. +This can happen when a trait is specified that another trait already has as a supertrait +(e.g. `fn() -> impl Deref + DerefMut<Target = i32>` has an unnecessary `Deref` bound, +because `Deref` is a supertrait of `DerefMut`)"##, + }, + Lint { + label: "clippy::impossible_comparisons", + description: r##"Checks for double comparisons that can never succeed"##, + }, + Lint { label: "clippy::imprecise_flops", description: r##"Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy @@ -5343,10 +10848,10 @@ lint on constant `usize` indexing on arrays because that is handled by rustc's ` without changing the outcome. The basic structure can be seen in the following table: -|Comparison| Bit Op |Example |equals | -|----------|---------|-----------|-------| -|`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`| -|`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|"##, +|Comparison| Bit Op |Example |equals | +|----------|----------|------------|-------| +|`>` / `<=`|`\\|` / `^`|`x \\| 2 > 3`|`x > 3`| +|`<` / `>=`|`\\|` / `^`|`x ^ 1 < 4` |`x < 4`|"##, }, Lint { label: "clippy::inefficient_to_string", @@ -5371,6 +10876,12 @@ or tuple struct where a `let` will suffice."##, description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait."##, }, Lint { + label: "clippy::init_numbered_fields", + description: r##"Checks for tuple structs initialized with field syntax. +It will however not lint if a base initializer is present. +The lint will also ignore code in macros."##, + }, + Lint { label: "clippy::inline_always", description: r##"Checks for items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics."##, @@ -5395,16 +10906,6 @@ unless the annotated function is empty or simply panics."##, label: "clippy::int_plus_one", description: r##"Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block"##, }, - Lint { - label: "clippy::integer_arithmetic", - description: r##"Checks for integer arithmetic operations which could overflow or panic. - -Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable -of overflowing according to the [Rust -Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), -or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is -attempted."##, - }, Lint { label: "clippy::integer_division", description: r##"Checks for division of integers"## }, Lint { label: "clippy::into_iter_on_ref", @@ -5412,6 +10913,12 @@ attempted."##, or `iter_mut`."##, }, Lint { + label: "clippy::into_iter_without_iter", + description: r##"This is the opposite of the `iter_without_into_iter` lint. +It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method +on the type or on any of the types in its `Deref` chain."##, + }, + Lint { label: "clippy::invalid_null_ptr_usage", description: r##"This lint checks for invalid usages of `ptr::null`."##, }, @@ -5432,10 +10939,20 @@ necessary. Only integer types are checked."##, description: r##"Checks for invisible Unicode characters in the code."##, }, Lint { + label: "clippy::is_digit_ascii_radix", + description: r##"Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that +can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or +[`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit)."##, + }, + Lint { label: "clippy::items_after_statements", description: r##"Checks for items declared after some statement in a block."##, }, Lint { + label: "clippy::items_after_test_module", + description: r##"Triggers if an item is declared after the testing module marked with `#[cfg(test)]`."##, + }, + Lint { label: "clippy::iter_cloned_collect", description: r##"Checks for the use of `.cloned().collect()` on slice to create a `Vec`."##, @@ -5444,6 +10961,11 @@ create a `Vec`."##, label: "clippy::iter_count", description: r##"Checks for the use of `.iter().count()`."##, }, + Lint { + label: "clippy::iter_kv_map", + description: r##"Checks for iterating a map (`HashMap` or `BTreeMap`) and +ignoring either the keys or values."##, + }, Lint { label: "clippy::iter_next_loop", description: r##"Checks for loops on `x.next()`."## }, Lint { label: "clippy::iter_next_slice", @@ -5455,7 +10977,7 @@ create a `Vec`."##, }, Lint { label: "clippy::iter_nth", - description: r##"Checks for use of `.iter().nth()` (and the related + description: r##"Checks for usage of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with *O*(1) element access."##, }, Lint { @@ -5463,8 +10985,37 @@ create a `Vec`."##, description: r##"Checks for the use of `iter.nth(0)`."##, }, Lint { + label: "clippy::iter_on_empty_collections", + description: r##"Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections"##, + }, + Lint { + label: "clippy::iter_on_single_items", + description: r##"Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item"##, + }, + Lint { + label: "clippy::iter_out_of_bounds", + description: r##"Looks for iterator combinator calls such as `.take(x)` or `.skip(x)` +where `x` is greater than the amount of items that an iterator will produce."##, + }, + Lint { + label: "clippy::iter_overeager_cloned", + description: r##"Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed."##, + }, + Lint { label: "clippy::iter_skip_next", - description: r##"Checks for use of `.skip(x).next()` on iterators."##, + description: r##"Checks for usage of `.skip(x).next()` on iterators."##, + }, + Lint { + label: "clippy::iter_skip_zero", + description: r##"Checks for usage of `.skip(0)` on iterators."##, + }, + Lint { + label: "clippy::iter_with_drain", + description: r##"Checks for usage of `.drain(..)` on `Vec` and `VecDeque` for iteration."##, + }, + Lint { + label: "clippy::iter_without_into_iter", + description: r##"Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation."##, }, Lint { label: "clippy::iterator_step_by_zero", @@ -5492,10 +11043,29 @@ are too large."##, `enum`s."##, }, Lint { + label: "clippy::large_futures", + description: r##"It checks for the size of a `Future` created by `async fn` or `async {}`."##, + }, + Lint { + label: "clippy::large_include_file", + description: r##"Checks for the inclusion of large files via `include_bytes!()` +and `include_str!()`"##, + }, + Lint { label: "clippy::large_stack_arrays", description: r##"Checks for local arrays that may be too large."##, }, Lint { + label: "clippy::large_stack_frames", + description: r##"Checks for functions that use a lot of stack space. + +This often happens when constructing a large type, such as an array with a lot of elements, +or constructing *many* smaller-but-still-large structs, or copying around a lot of large types. + +This lint is a more general version of [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/#large_stack_arrays) +that is intended to look at functions as a whole instead of only individual array expressions inside of a function."##, + }, + Lint { label: "clippy::large_types_passed_by_value", description: r##"Checks for functions taking arguments by value, where the argument type is `Copy` and large enough to be worth considering @@ -5519,29 +11089,42 @@ just to compare to zero, and suggests using `.is_empty()` where applicable."##, returned."##, }, Lint { - label: "clippy::let_underscore_drop", - description: r##"Checks for `let _ = <expr>` -where expr has a type that implements `Drop`"##, + label: "clippy::let_underscore_future", + description: r##"Checks for `let _ = <expr>` where the resulting type of expr implements `Future`"##, }, Lint { label: "clippy::let_underscore_lock", - description: r##"Checks for `let _ = sync_lock`. -This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`."##, + description: r##"Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in +`parking_lot`. For `std` locks see the `rustc` lint +[`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)"##, }, Lint { label: "clippy::let_underscore_must_use", description: r##"Checks for `let _ = <expr>` where expr is `#[must_use]`"##, }, + Lint { + label: "clippy::let_underscore_untyped", + description: r##"Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one, +or remove the `let` keyword altogether."##, + }, Lint { label: "clippy::let_unit_value", description: r##"Checks for binding a unit value."## }, Lint { + label: "clippy::let_with_type_underscore", + description: r##"Detects when a variable is declared with an explicit type of `_`."##, + }, + Lint { + label: "clippy::lines_filter_map_ok", + description: r##"Checks for usage of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)` +when `lines` has type `std::io::Lines`."##, + }, + Lint { label: "clippy::linkedlist", description: r##"Checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`)."##, }, Lint { - label: "clippy::logic_bug", - description: r##"Checks for boolean expressions that contain terminals that -can be eliminated."##, + label: "clippy::little_endian_bytes", + description: r##"Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`."##, }, Lint { label: "clippy::lossy_float_literal", @@ -5565,23 +11148,74 @@ cannot be represented as the underlying type without loss."##, description: r##"It checks for manual implementations of `async` functions."##, }, Lint { + label: "clippy::manual_bits", + description: r##"Checks for usage of `std::mem::size_of::<T>() * 8` when +`T::BITS` is available."##, + }, + Lint { + label: "clippy::manual_clamp", + description: r##"Identifies good opportunities for a clamp function from std or core, and suggests using it."##, + }, + Lint { + label: "clippy::manual_filter", + description: r##"Checks for usage of `match` which could be implemented using `filter`"##, + }, + Lint { label: "clippy::manual_filter_map", description: r##"Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`."##, }, Lint { + label: "clippy::manual_find", + description: r##"Checks for manual implementations of Iterator::find"##, + }, + Lint { label: "clippy::manual_find_map", description: r##"Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`."##, }, Lint { label: "clippy::manual_flatten", - description: r##"Check for unnecessary `if let` usage in a for loop + description: r##"Checks for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used."##, }, Lint { + label: "clippy::manual_hash_one", + description: r##"Checks for cases where [`BuildHasher::hash_one`] can be used. + +[`BuildHasher::hash_one`]: https://doc.rust-lang.org/std/hash/trait.BuildHasher.html#method.hash_one"##, + }, + Lint { + label: "clippy::manual_instant_elapsed", + description: r##"Lints subtraction between `Instant::now()` and another `Instant`."##, + }, + Lint { + label: "clippy::manual_is_ascii_check", + description: r##"Suggests to use dedicated built-in methods, +`is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range"##, + }, + Lint { + label: "clippy::manual_is_finite", + description: r##"Checks for manual `is_finite` reimplementations +(i.e., `x != <float>::INFINITY && x != <float>::NEG_INFINITY`)."##, + }, + Lint { + label: "clippy::manual_is_infinite", + description: r##"Checks for manual `is_infinite` reimplementations +(i.e., `x == <float>::INFINITY || x == <float>::NEG_INFINITY`)."##, + }, + Lint { + label: "clippy::manual_let_else", + description: r##"Warn of cases where `let...else` could be used"##, + }, + Lint { + label: "clippy::manual_main_separator_str", + description: r##"Checks for references on `std::path::MAIN_SEPARATOR.to_string()` used +to build a `&str`."##, + }, + Lint { label: "clippy::manual_map", - description: r##"Checks for usages of `match` which could be implemented using `map`"##, + description: r##"Checks for usage of `match` which could be implemented using `map`"##, }, Lint { label: "clippy::manual_memcpy", @@ -5589,6 +11223,10 @@ where only the `Some` or `Ok` variant of the iterator element is used."##, slices that could be optimized by having a memcpy."##, }, Lint { + label: "clippy::manual_next_back", + description: r##"Checks for `.rev().next()` on a `DoubleEndedIterator`"##, + }, + Lint { label: "clippy::manual_non_exhaustive", description: r##"Checks for manual implementations of the non-exhaustive pattern."##, }, @@ -5602,28 +11240,66 @@ slices that could be optimized by having a memcpy."##, be more readably expressed as `(3..8).contains(x)`."##, }, Lint { + label: "clippy::manual_range_patterns", + description: r##"Looks for combined OR patterns that are all contained in a specific range, +e.g. `6 | 4 | 5 | 9 | 7 | 8` can be rewritten as `4..=9`."##, + }, + Lint { + label: "clippy::manual_rem_euclid", + description: r##"Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation +of `x.rem_euclid(4)`."##, + }, + Lint { + label: "clippy::manual_retain", + description: r##"Checks for code to be replaced by `.retain()`."##, + }, + Lint { label: "clippy::manual_saturating_arithmetic", description: r##"Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`."##, }, Lint { + label: "clippy::manual_slice_size_calculation", + description: r##"When `a` is `&[T]`, detect `a.len() * size_of::<T>()` and suggest `size_of_val(a)` +instead."##, + }, + Lint { label: "clippy::manual_split_once", - description: r##"Checks for usages of `str::splitn(2, _)`"##, + description: r##"Checks for usage of `str::splitn(2, _)`"##, }, Lint { label: "clippy::manual_str_repeat", description: r##"Checks for manual implementations of `str::repeat`"##, }, Lint { + label: "clippy::manual_string_new", + description: r##"Checks for usage of `` to create a `String`, such as `.to_string()`, `.to_owned()`, +`String::from()` and others."##, + }, + Lint { label: "clippy::manual_strip", description: r##"Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using the pattern's length."##, }, - Lint { label: "clippy::manual_swap", description: r##"Checks for manual swapping."## }, + Lint { + label: "clippy::manual_swap", + description: r##"Checks for manual swapping. + +Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable."##, + }, + Lint { + label: "clippy::manual_try_fold", + description: r##"Checks for usage of `Iterator::fold` with a type that implements `Try`."##, + }, Lint { label: "clippy::manual_unwrap_or", description: r##"Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`."##, }, Lint { + label: "clippy::manual_while_let_some", + description: r##"Looks for loops that check for emptiness of a `Vec` in the condition and pop an element +in the body as a separate operation."##, + }, + Lint { label: "clippy::many_single_char_names", description: r##"Checks for too many variables whose name consists of a single character."##, @@ -5640,7 +11316,7 @@ and suggests `cloned()` or `copied()` instead"##, }, Lint { label: "clippy::map_entry", - description: r##"Checks for uses of `contains_key` + `insert` on `HashMap` + description: r##"Checks for usage of `contains_key` + `insert` on `HashMap` or `BTreeMap`."##, }, Lint { @@ -5695,7 +11371,10 @@ instead. It also checks for `if let &foo = bar` blocks."##, }, Lint { label: "clippy::match_same_arms", - description: r##"Checks for `match` with identical arm bodies."##, + description: r##"Checks for `match` with identical arm bodies. + +Note: Does not lint on wildcards if the `non_exhaustive_omitted_patterns_lint` feature is +enabled and disallowed."##, }, Lint { label: "clippy::match_single_binding", @@ -5719,9 +11398,14 @@ and take drastic actions like `panic!`."##, description: r##"Checks for iteration that may be infinite."##, }, Lint { + label: "clippy::maybe_misused_cfg", + description: r##"Checks for `#[cfg(features = ...)]` and suggests to replace it with +`#[cfg(feature = ...)]`."##, + }, + Lint { label: "clippy::mem_forget", description: r##"Checks for usage of `std::mem::forget(t)` where `t` is -`Drop`."##, +`Drop` or has a field that implements `Drop`."##, }, Lint { label: "clippy::mem_replace_option_with_none", @@ -5739,6 +11423,13 @@ and take drastic actions like `panic!`."##, and `mem::replace(&mut _, mem::zeroed())`."##, }, Lint { + label: "clippy::min_ident_chars", + description: r##"Checks for idents which comprise of a single letter. + +Note: This lint can be very noisy when enabled; it may be desirable to only enable it +temporarily."##, + }, + Lint { label: "clippy::min_max", description: r##"Checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant."##, @@ -5752,17 +11443,37 @@ used to clamp values, but switched so that the result is constant."##, description: r##"Checks for cfg attributes having operating systems used in target family position."##, }, Lint { + label: "clippy::mismatching_type_param_order", + description: r##"Checks for type parameters which are positioned inconsistently between +a type definition and impl block. Specifically, a parameter in an impl +block which has the same name as a parameter in the type def, but is in +a different place."##, + }, + Lint { + label: "clippy::misnamed_getters", + description: r##"Checks for getter methods that return a field that doesn't correspond +to the name of the method, when there is a field's whose name matches that of the method."##, + }, + Lint { label: "clippy::misrefactored_assign_op", description: r##"Checks for `a op= a op b` or `a op= b op a` patterns."##, }, Lint { + label: "clippy::missing_assert_message", + description: r##"Checks assertions without a custom panic message."##, + }, + Lint { + label: "clippy::missing_asserts_for_indexing", + description: r##"Checks for repeated slice indexing without asserting beforehand that the length +is greater than the largest index used to index into the slice."##, + }, + Lint { label: "clippy::missing_const_for_fn", description: r##"Suggests the use of `const` in functions and methods where possible."##, }, Lint { label: "clippy::missing_docs_in_private_items", - description: r##"Warns if there is missing doc for any documentable item -(public or private)."##, + description: r##"Warns if there is missing doc for any private documentable item"##, }, Lint { label: "clippy::missing_enforced_import_renames", @@ -5775,6 +11486,10 @@ in the `enforce-import-renames` config option."##, return a `Result` type and warns if there is no `# Errors` section."##, }, Lint { + label: "clippy::missing_fields_in_debug", + description: r##"Checks for manual [`core::fmt::Debug`](https://doc.rust-lang.org/core/fmt/trait.Debug.html) implementations that do not use all fields."##, + }, + Lint { label: "clippy::missing_inline_in_public_items", description: r##"It lints if an exported function, method, trait method with default impl, or trait method impl is not `#[inline]`."##, @@ -5789,6 +11504,17 @@ may panic and warns if there is no `# Panics` section."##, description: r##"Checks for the doc comments of publicly visible unsafe functions and warns if there is no `# Safety` section."##, }, + Lint { label: "clippy::missing_spin_loop", description: r##"Checks for empty spin loops"## }, + Lint { + label: "clippy::missing_trait_methods", + description: r##"Checks if a provided method is used implicitly by a trait +implementation. A usage example would be a wrapper where every method +should perform some operation before delegating to the inner type's +implementation. + +This lint should typically be enabled on a specific trait `impl` item +rather than globally."##, + }, Lint { label: "clippy::mistyped_literal_suffixes", description: r##"Warns for mistyped suffix in literals"##, @@ -5799,8 +11525,14 @@ unsafe functions and warns if there is no `# Safety` section."##, digits."##, }, Lint { + label: "clippy::mixed_read_write_in_expression", + description: r##"Checks for a read and a write to the same variable where +whether the read occurs before or after the write depends on the evaluation +order of sub-expressions."##, + }, + Lint { label: "clippy::mod_module_files", - description: r##"Checks that module layout uses only self named module files, bans mod.rs files."##, + description: r##"Checks that module layout uses only self named module files, bans `mod.rs` files."##, }, Lint { label: "clippy::module_inception", @@ -5818,6 +11550,7 @@ containing module's name."##, description: r##"Checks for getting the remainder of a division by one or minus one."##, }, + Lint { label: "clippy::multi_assignments", description: r##"Checks for nested assignments."## }, Lint { label: "clippy::multiple_crate_versions", description: r##"Checks to see if multiple versions of a crate are being @@ -5828,6 +11561,10 @@ used."##, description: r##"Checks for multiple inherent implementations of a struct"##, }, Lint { + label: "clippy::multiple_unsafe_ops_per_block", + description: r##"Checks for `unsafe` blocks that contain more than one unsafe operation."##, + }, + Lint { label: "clippy::must_use_candidate", description: r##"Checks for public functions that have no `#[must_use]` attribute, but return something not already marked @@ -5840,8 +11577,12 @@ unit-returning functions and methods."##, }, Lint { label: "clippy::mut_from_ref", - description: r##"This lint checks for functions that take immutable -references and return mutable ones."##, + description: r##"This lint checks for functions that take immutable references and return +mutable ones. This will not trigger if no unsafe code exists as there +are multiple safe functions which will do this transformation + +To be on the conservative side, if there's at least one mutable +reference with the output lifetime, this lint will not trigger."##, }, Lint { label: "clippy::mut_mut", @@ -5861,11 +11602,11 @@ references and return mutable ones."##, }, Lint { label: "clippy::mutex_atomic", - description: r##"Checks for usages of `Mutex<X>` where an atomic will do."##, + description: r##"Checks for usage of `Mutex<X>` where an atomic will do."##, }, Lint { label: "clippy::mutex_integer", - description: r##"Checks for usages of `Mutex<X>` where `X` is an integral + description: r##"Checks for usage of `Mutex<X>` where `X` is an integral type."##, }, Lint { label: "clippy::naive_bytecount", description: r##"Checks for naive byte counts"## }, @@ -5876,7 +11617,7 @@ specify the `Self`-type explicitly"##, }, Lint { label: "clippy::needless_bitwise_bool", - description: r##"Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using + description: r##"Checks for usage of bitwise and/or operators between booleans, where performance may be improved by using a lazy and."##, }, Lint { @@ -5885,16 +11626,27 @@ a lazy and."##, false }` (or vice versa) and suggests using the condition directly."##, }, Lint { + label: "clippy::needless_bool_assign", + description: r##"Checks for expressions of the form `if c { x = true } else { x = false }` +(or vice versa) and suggest assigning the variable directly from the +condition."##, + }, + Lint { label: "clippy::needless_borrow", description: r##"Checks for address of operations (`&`) that are going to be dereferenced immediately by the compiler."##, }, Lint { label: "clippy::needless_borrowed_reference", - description: r##"Checks for bindings that destructure a reference and borrow the inner + description: r##"Checks for bindings that needlessly destructure a reference and borrow the inner value with `&ref`."##, }, Lint { + label: "clippy::needless_borrows_for_generic_args", + description: r##"Checks for borrow operations (`&`) that used as a generic argument to a +function when the borrowed value could be used."##, + }, + Lint { label: "clippy::needless_collect", description: r##"Checks for functions collecting an iterator when collect is not needed."##, @@ -5910,12 +11662,17 @@ rearrangement of code can make the code easier to understand."##, label: "clippy::needless_doctest_main", description: r##"Checks for `fn main() { .. }` in doctests"##, }, + Lint { label: "clippy::needless_else", description: r##"Checks for empty `else` branches."## }, Lint { label: "clippy::needless_for_each", description: r##"Checks for usage of `for_each` that would be more simply written as a `for` loop."##, }, Lint { + label: "clippy::needless_if", + description: r##"Checks for empty `if` branches with no else branch."##, + }, + Lint { label: "clippy::needless_late_init", description: r##"Checks for late initializations that can be replaced by a `let` statement with an initializer."##, @@ -5926,17 +11683,42 @@ with an initializer."##, relying on lifetime elision."##, }, Lint { + label: "clippy::needless_match", + description: r##"Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result` +when function signatures are the same."##, + }, + Lint { label: "clippy::needless_option_as_deref", - description: r##"Checks for no-op uses of Option::{as_deref,as_deref_mut}, + description: r##"Checks for no-op uses of `Option::{as_deref, as_deref_mut}`, for example, `Option<&T>::as_deref()` returns the same type."##, }, Lint { + label: "clippy::needless_option_take", + description: r##"Checks for calling `take` function after `as_ref`."##, + }, + Lint { + label: "clippy::needless_parens_on_range_literals", + description: r##"The lint checks for parenthesis on literals in range statements that are +superfluous."##, + }, + Lint { + label: "clippy::needless_pass_by_ref_mut", + description: r##"Check if a `&mut` function argument is actually used mutably. + +Be careful if the function is publicly reexported as it would break compatibility with +users of this function."##, + }, + Lint { label: "clippy::needless_pass_by_value", description: r##"Checks for functions taking arguments by value, but not consuming them in its body."##, }, Lint { + label: "clippy::needless_pub_self", + description: r##"Checks for usage of `pub(self)` and `pub(in self)`."##, + }, + Lint { label: "clippy::needless_question_mark", description: r##"Suggests alternatives for useless applications of `?` in terminating expressions"##, }, @@ -5946,12 +11728,24 @@ body."##, collection just to get the values by index."##, }, Lint { + label: "clippy::needless_raw_string_hashes", + description: r##"Checks for raw string literals with an unnecessary amount of hashes around them."##, + }, + Lint { + label: "clippy::needless_raw_strings", + description: r##"Checks for raw string literals where a string literal can be used instead."##, + }, + Lint { label: "clippy::needless_return", description: r##"Checks for return statements at the end of a block."##, }, Lint { + label: "clippy::needless_return_with_question_mark", + description: r##"Checks for return statements on `Err` paired with the `?` operator."##, + }, + Lint { label: "clippy::needless_splitn", - description: r##"Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same."##, + description: r##"Checks for usage of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same."##, }, Lint { label: "clippy::needless_update", @@ -5985,7 +11779,7 @@ This lint is not applied to structs marked with }, Lint { label: "clippy::new_without_default", - description: r##"Checks for types with a `fn new() -> Self` method and no + description: r##"Checks for public types with a `pub fn new() -> Self` method and no implementation of [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)."##, }, @@ -5994,14 +11788,34 @@ implementation of description: r##"Checks for statements which have no effect."##, }, Lint { + label: "clippy::no_effect_replace", + description: r##"Checks for `replace` statements which have no effect."##, + }, + Lint { label: "clippy::no_effect_underscore_binding", description: r##"Checks for binding to underscore prefixed variable without side-effects."##, }, Lint { + label: "clippy::no_mangle_with_rust_abi", + description: r##"Checks for Rust ABI functions with the `#[no_mangle]` attribute."##, + }, + Lint { label: "clippy::non_ascii_literal", description: r##"Checks for non-ASCII characters in string and char literals."##, }, Lint { + label: "clippy::non_canonical_clone_impl", + description: r##"Checks for non-canonical implementations of `Clone` when `Copy` is already implemented."##, + }, + Lint { + label: "clippy::non_canonical_partial_ord_impl", + description: r##"Checks for non-canonical implementations of `PartialOrd` when `Ord` is already implemented."##, + }, + Lint { + label: "clippy::non_minimal_cfg", + description: r##"Checks for `any` and `all` combinators in `cfg` with only one condition."##, + }, + Lint { label: "clippy::non_octal_unix_permissions", description: r##"Checks for non-octal values used to set Unix file permissions."##, }, @@ -6033,12 +11847,20 @@ that make no sense."##, arguments but are not marked `unsafe`."##, }, Lint { + label: "clippy::obfuscated_if_else", + description: r##"Checks for usage of `.then_some(..).unwrap_or(..)`"##, + }, + Lint { label: "clippy::octal_escapes", description: r##"Checks for `\\0` escapes in string and byte literals that look like octal character escapes in C."##, }, Lint { label: "clippy::ok_expect", description: r##"Checks for usage of `ok().expect(..)`."## }, Lint { + label: "clippy::only_used_in_recursion", + description: r##"Checks for arguments that are only used in recursion with no side-effects."##, + }, + Lint { label: "clippy::op_ref", description: r##"Checks for arguments to `==` which have their address taken to satisfy a bound @@ -6046,7 +11868,7 @@ and suggests to dereference the other argument instead"##, }, Lint { label: "clippy::option_as_ref_deref", - description: r##"Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str)."##, + description: r##"Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str)."##, }, Lint { label: "clippy::option_env_unwrap", @@ -6059,7 +11881,8 @@ suggests usage of the `env!` macro."##, }, Lint { label: "clippy::option_if_let_else", - description: r##"Lints usage of `if let Some(v) = ... { y } else { x }` which is more + description: r##"Lints usage of `if let Some(v) = ... { y } else { x }` and +`match .. { Some(v) => y, None/_ => x }` which are more idiomatically done with `Option::map_or` (if the else bit is a pure expression) or `Option::map_or_else` (if the else bit is an impure expression)."##, @@ -6075,14 +11898,19 @@ or closure that returns the unit type `()`."##, }, Lint { label: "clippy::option_option", - description: r##"Checks for use of `Option<Option<_>>` in function signatures and type + description: r##"Checks for usage of `Option<Option<_>>` in function signatures and type definitions"##, }, Lint { label: "clippy::or_fun_call", description: r##"Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, -etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or -`unwrap_or_default` instead."##, +`.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`, +`.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()` +etc. instead."##, + }, + Lint { + label: "clippy::or_then_unwrap", + description: r##"Checks for `.or(…).unwrap()` calls to Options and Results."##, }, Lint { label: "clippy::out_of_bounds_indexing", @@ -6093,25 +11921,49 @@ index."##, label: "clippy::overflow_check_conditional", description: r##"Detects classic underflow/overflow checks."##, }, + Lint { + label: "clippy::overly_complex_bool_expr", + description: r##"Checks for boolean expressions that contain terminals that +can be eliminated."##, + }, Lint { label: "clippy::panic", description: r##"Checks for usage of `panic!`."## }, Lint { label: "clippy::panic_in_result_fn", - description: r##"Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result."##, + description: r##"Checks for usage of `panic!` or assertions in a function of type result."##, }, Lint { label: "clippy::panicking_unwrap", description: r##"Checks for calls of `unwrap[_err]()` that will always fail."##, }, Lint { + label: "clippy::partial_pub_fields", + description: r##"Checks whether partial fields of a struct are public. + +Either make all fields of a type public, or make none of them public"##, + }, + Lint { label: "clippy::partialeq_ne_impl", description: r##"Checks for manual re-implementations of `PartialEq::ne`."##, }, Lint { + label: "clippy::partialeq_to_none", + description: r##"Checks for binary comparisons to a literal `Option::None`."##, + }, + Lint { label: "clippy::path_buf_push_overwrite", description: r##"* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) calls on `PathBuf` that can cause overwrites."##, }, Lint { + label: "clippy::path_ends_with_ext", + description: r##"Looks for calls to `Path::ends_with` calls where the argument looks like a file extension. + +By default, Clippy has a short list of known filenames that start with a dot +but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default. +The `allowed-dotfiles` configuration can be used to allow additional +file extensions that Clippy should not lint."##, + }, + Lint { label: "clippy::pattern_type_mismatch", description: r##"Checks for patterns that aren't exact representations of the types they are applied to. @@ -6133,6 +11985,10 @@ this lint can still be used to highlight areas of interest and ensure a good und of ownership semantics."##, }, Lint { + label: "clippy::permissions_set_readonly_false", + description: r##"Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`."##, + }, + Lint { label: "clippy::possible_missing_comma", description: r##"Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines."##, @@ -6148,6 +12004,11 @@ numeric literal) followed by a method call"##, }, Lint { + label: "clippy::print_in_format_impl", + description: r##"Checks for usage of `println`, `print`, `eprintln` or `eprint` in an +implementation of a formatting trait."##, + }, + Lint { label: "clippy::print_literal", description: r##"This lint warns about the use of literals as `print!`/`println!` args."##, }, @@ -6173,16 +12034,20 @@ print a newline."##, }, Lint { label: "clippy::ptr_arg", - description: r##"This lint checks for function arguments of type `&String` -or `&Vec` unless the references are mutable. It will also suggest you -replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()` -calls."##, + description: r##"This lint checks for function arguments of type `&String`, `&Vec`, +`&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls +with the appropriate `.to_owned()`/`to_string()` calls."##, }, Lint { label: "clippy::ptr_as_ptr", description: r##"Checks for `as` casts between raw pointers without changing its mutability, namely `*const T` to `*const U` and `*mut T` to `*mut U`."##, }, + Lint { + label: "clippy::ptr_cast_constness", + description: r##"Checks for `as` casts between raw pointers which change its constness, namely `*const T` to +`*mut T` and `*mut T` to `*const T`."##, + }, Lint { label: "clippy::ptr_eq", description: r##"Use `std::ptr::eq` when applicable"## }, Lint { label: "clippy::ptr_offset_with_cast", @@ -6193,11 +12058,27 @@ namely `*const T` to `*const U` and `*mut T` to `*mut U`."##, label: "clippy::pub_enum_variant_names", description: r##"Nothing. This lint has been deprecated."##, }, + Lint { label: "clippy::pub_use", description: r##"Restricts the usage of `pub use ...`"## }, + Lint { + label: "clippy::pub_with_shorthand", + description: r##"Checks for usage of `pub(<loc>)` with `in`."##, + }, + Lint { + label: "clippy::pub_without_shorthand", + description: r##"Checks for usage of `pub(<loc>)` without `in`. + +Note: As you cannot write a module's path in `pub(<loc>)`, this will only trigger on +`pub(super)` and the like."##, + }, Lint { label: "clippy::question_mark", description: r##"Checks for expressions that could be replaced by the question mark operator."##, }, Lint { + label: "clippy::question_mark_used", + description: r##"Checks for expressions that use the question mark operator and rejects them."##, + }, + Lint { label: "clippy::range_minus_one", description: r##"Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., `x..=(y-1)`."##, @@ -6220,10 +12101,49 @@ upper bound, e.g., `x..(y+1)`."##, label: "clippy::rc_buffer", description: r##"Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`."##, }, + Lint { + label: "clippy::rc_clone_in_vec_init", + description: r##"Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`) +in `vec![elem; len]`"##, + }, Lint { label: "clippy::rc_mutex", description: r##"Checks for `Rc<Mutex<T>>`."## }, Lint { + label: "clippy::read_line_without_trim", + description: r##"Looks for calls to [`Stdin::read_line`] to read a line from the standard input +into a string, then later attempting to parse this string into a type without first trimming it, which will +always fail because the string has a trailing newline in it."##, + }, + Lint { + label: "clippy::read_zero_byte_vec", + description: r##"This lint catches reads into a zero-length `Vec`. +Especially in the case of a call to `with_capacity`, this lint warns that read +gets the number of bytes from the `Vec`'s length, not its capacity."##, + }, + Lint { + label: "clippy::readonly_write_lock", + description: r##"Looks for calls to `RwLock::write` where the lock is only used for reading."##, + }, + Lint { + label: "clippy::recursive_format_impl", + description: r##"Checks for format trait implementations (e.g. `Display`) with a recursive call to itself +which uses `self` as a parameter. +This is typically done indirectly with the `write!` macro or with `to_string()`."##, + }, + Lint { label: "clippy::redundant_allocation", - description: r##"Checks for use of redundant allocations anywhere in the code."##, + description: r##"Checks for usage of redundant allocations anywhere in the code."##, + }, + Lint { + label: "clippy::redundant_as_str", + description: r##"Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself."##, + }, + Lint { + label: "clippy::redundant_async_block", + description: r##"Checks for `async` block that only returns `await` on a future."##, + }, + Lint { + label: "clippy::redundant_at_rest_pattern", + description: r##"Checks for `[all @ ..]` patterns."##, }, Lint { label: "clippy::redundant_clone", @@ -6247,6 +12167,10 @@ are defined."##, argument and can be replaced by referencing the method directly."##, }, Lint { + label: "clippy::redundant_comparisons", + description: r##"Checks for ineffective double comparisons against constants."##, + }, + Lint { label: "clippy::redundant_else", description: r##"Checks for `else` blocks that can be removed without changing semantics."##, }, @@ -6260,6 +12184,14 @@ argument and can be replaced by referencing the method directly."##, could be used."##, }, Lint { + label: "clippy::redundant_guards", + description: r##"Checks for unnecessary guards in match expressions."##, + }, + Lint { + label: "clippy::redundant_locals", + description: r##"Checks for redundant redefinitions of local bindings."##, + }, + Lint { label: "clippy::redundant_pattern", description: r##"Checks for patterns in the form `name @ _`."##, }, @@ -6283,19 +12215,22 @@ do not change the type."##, description: r##"Checks for constants and statics with an explicit `'static` lifetime."##, }, Lint { - label: "clippy::ref_binding_to_reference", - description: r##"Checks for `ref` bindings which create a reference to a reference."##, + label: "clippy::redundant_type_annotations", + description: r##"Warns about needless / redundant type annotations."##, }, Lint { - label: "clippy::ref_in_deref", - description: r##"Checks for references in expressions that use -auto dereference."##, + label: "clippy::ref_binding_to_reference", + description: r##"Checks for `ref` bindings which create a reference to a reference."##, }, Lint { label: "clippy::ref_option_ref", description: r##"Checks for usage of `&Option<&T>`."##, }, Lint { + label: "clippy::ref_patterns", + description: r##"Checks for usages of the `ref` keyword."##, + }, + Lint { label: "clippy::regex_macro", description: r##"Nothing. This lint has been deprecated."##, }, @@ -6314,10 +12249,19 @@ they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://gi description: r##"Nothing. This lint has been deprecated."##, }, Lint { + label: "clippy::reserve_after_initialization", + description: r##"Informs the user about a more concise way to create a vector with a known capacity."##, + }, + Lint { label: "clippy::rest_pat_in_fully_bound_structs", description: r##"Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched."##, }, Lint { + label: "clippy::result_large_err", + description: r##"Checks for functions that return `Result` with an unusually large +`Err`-variant."##, + }, + Lint { label: "clippy::result_map_or_into_option", description: r##"Checks for usage of `_.map_or(None, Some)`."##, }, @@ -6339,7 +12283,7 @@ implements `std::error::Error`."##, Lint { label: "clippy::reversed_empty_ranges", description: r##"Checks for range expressions `x..y` where both `x` and `y` -are constant and `x` is greater or equal to `y`."##, +are constant and `x` is greater to `y`. Also triggers if `x` is equal to `y` when they are conditions to a `for` loop."##, }, Lint { label: "clippy::same_functions_in_if_condition", @@ -6361,6 +12305,16 @@ one from a trait, another not from trait."##, `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`."##, }, Lint { + label: "clippy::seek_from_current", + description: r##"Checks an argument of `seek` method of `Seek` trait +and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead."##, + }, + Lint { + label: "clippy::seek_to_start_instead_of_rewind", + description: r##"Checks for jumps to the start of a stream that implements `Seek` +and uses the `seek` method providing `Start` as parameter."##, + }, + Lint { label: "clippy::self_assignment", description: r##"Checks for explicit self-assignments."##, }, @@ -6370,7 +12324,7 @@ one from a trait, another not from trait."##, }, Lint { label: "clippy::self_named_module_files", - description: r##"Checks that module layout uses only mod.rs files."##, + description: r##"Checks that module layout uses only `mod.rs` files."##, }, Lint { label: "clippy::semicolon_if_nothing_returned", @@ -6378,6 +12332,15 @@ one from a trait, another not from trait."##, `()` but is not followed by a semicolon."##, }, Lint { + label: "clippy::semicolon_inside_block", + description: r##"Suggests moving the semicolon after a block to the inside of the block, after its last +expression."##, + }, + Lint { + label: "clippy::semicolon_outside_block", + description: r##"Suggests moving the semicolon from a block's final expression outside of the block."##, + }, + Lint { label: "clippy::separated_literal_suffix", description: r##"Warns if literal suffixes are separated by an underscore. To enforce separated literal suffix style, @@ -6421,8 +12384,31 @@ post](http://llogiq.github.io/2015/07/30/traits.html) for further information) instead of an inherent implementation."##, }, Lint { + label: "clippy::should_panic_without_expect", + description: r##"Checks for `#[should_panic]` attributes without specifying the expected panic message."##, + }, + Lint { + label: "clippy::significant_drop_in_scrutinee", + description: r##"Checks for temporaries returned from function calls in a match scrutinee that have the +`clippy::has_significant_drop` attribute."##, + }, + Lint { + label: "clippy::significant_drop_tightening", + description: r##"Searches for elements marked with `#[clippy::has_significant_drop]` that could be early +dropped but are in fact dropped at the end of their scopes. In other words, enforces the +tightening of their possible lifetimes."##, + }, + Lint { label: "clippy::similar_names", - description: r##"Checks for names that are very similar and thus confusing."##, + description: r##"Checks for names that are very similar and thus confusing. + +Note: this lint looks for similar names throughout each +scope. To allow it, you need to allow it on the scope +level, not on the name that is reported."##, + }, + Lint { + label: "clippy::single_call_fn", + description: r##"Checks for functions that are only used once. Does not lint tests."##, }, Lint { label: "clippy::single_char_add_str", @@ -6430,6 +12416,11 @@ information) instead of an inherent implementation."##, where `push`/`insert` with a `char` would work fine."##, }, Lint { + label: "clippy::single_char_lifetime_names", + description: r##"Checks for lifetimes with names which are one character +long."##, + }, + Lint { label: "clippy::single_char_pattern", description: r##"Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(x)`."##, @@ -6445,7 +12436,12 @@ where `push`/`insert` with a `char` would work fine."##, Lint { label: "clippy::single_match", description: r##"Checks for matches with a single arm where an `if let` -will usually suffice."##, +will usually suffice. + +This intentionally does not lint if there are comments +inside of the other arm, so as to allow the user to document +why having another explicit pattern with an empty body is necessary, +or because the comments need to be preserved for other reasons."##, }, Lint { label: "clippy::single_match_else", @@ -6453,12 +12449,21 @@ will usually suffice."##, usually suffice."##, }, Lint { + label: "clippy::single_range_in_vec_init", + description: r##"Checks for `Vec` or array initializations that contain only one range."##, + }, + Lint { label: "clippy::size_of_in_element_count", description: r##"Detects expressions where `size_of::<T>` or `size_of_val::<T>` is used as a count of elements of type `T`"##, }, Lint { + label: "clippy::size_of_ref", + description: r##"Checks for calls to `std::mem::size_of_val()` where the argument is +a reference to a reference."##, + }, + Lint { label: "clippy::skip_while_next", description: r##"Checks for usage of `_.skip_while(condition).next()`."##, }, @@ -6469,10 +12474,18 @@ count of elements of type `T`"##, Lint { label: "clippy::stable_sort_primitive", description: r##"When sorting primitive values (integers, bools, chars, as well -as arrays, slices, and tuples of such items), it is better to +as arrays, slices, and tuples of such items), it is typically better to use an unstable sort than a stable sort."##, }, Lint { + label: "clippy::std_instead_of_alloc", + description: r##"Finds items imported through `std` when available through `alloc`."##, + }, + Lint { + label: "clippy::std_instead_of_core", + description: r##"Finds items imported through `std` when available through `core`."##, + }, + Lint { label: "clippy::str_to_string", description: r##"This lint checks for `.to_string()` method calls on values of type `&str`."##, }, @@ -6502,6 +12515,10 @@ match."##, that contain only ASCII characters."##, }, Lint { + label: "clippy::string_lit_chars_any", + description: r##"Checks for `<string_lit>.chars().any(|i| i == c)`."##, + }, + Lint { label: "clippy::string_slice", description: r##"Checks for slice operations on strings"##, }, @@ -6532,10 +12549,19 @@ subtracting elements in an Add impl."##, }, Lint { label: "clippy::suspicious_assignment_formatting", - description: r##"Checks for use of the nonexistent `=*`, `=!` and `=-` + description: r##"Checks for usage of the non-existent `=*`, `=!` and `=-` operators."##, }, Lint { + label: "clippy::suspicious_command_arg_space", + description: r##"Checks for `Command::arg()` invocations that look like they +should be multiple arguments instead, such as `arg(-t ext2)`."##, + }, + Lint { + label: "clippy::suspicious_doc_comments", + description: r##"Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`"##, + }, + Lint { label: "clippy::suspicious_else_formatting", description: r##"Checks for formatting of `else`. It lints if the `else` is followed immediately by a newline or the `else` seems to be missing."##, @@ -6562,12 +12588,24 @@ of binary operators nearby."##, related functions with either zero or one splits."##, }, Lint { + label: "clippy::suspicious_to_owned", + description: r##"Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`."##, + }, + Lint { label: "clippy::suspicious_unary_op_formatting", description: r##"Checks the formatting of a unary operator on the right hand side of a binary operator. It lints if there is no space between the binary and unary operators, but there is a space between the unary and its operand."##, }, Lint { + label: "clippy::suspicious_xor_used_as_pow", + description: r##"Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal."##, + }, + Lint { + label: "clippy::swap_ptr_to_ref", + description: r##"Checks for calls to `core::mem::swap` where either parameter is derived from a pointer"##, + }, + Lint { label: "clippy::tabs_in_doc_comments", description: r##"Checks doc comments for usage of tab characters."##, }, @@ -6577,12 +12615,13 @@ but there is a space between the unary and its operand."##, assign a value in it."##, }, Lint { - label: "clippy::to_digit_is_some", - description: r##"Checks for `.to_digit(..).is_some()` on `char`s."##, + label: "clippy::tests_outside_test_module", + description: r##"Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module +(marked with `#[cfg(test)]`)."##, }, Lint { - label: "clippy::to_string_in_display", - description: r##"Checks for uses of `to_string()` in `Display` traits."##, + label: "clippy::to_digit_is_some", + description: r##"Checks for `.to_digit(..).is_some()` on `char`s."##, }, Lint { label: "clippy::to_string_in_format_args", @@ -6610,7 +12649,7 @@ in a macro that does formatting."##, }, Lint { label: "clippy::trait_duplication_in_bounds", - description: r##"Checks for cases where generics are being used and multiple + description: r##"Checks for cases where generics or trait objects are being used and multiple syntax specifications for trait bounds are used simultaneously."##, }, Lint { @@ -6634,6 +12673,15 @@ syntax specifications for trait bounds are used simultaneously."##, description: r##"Checks for transmutes from an integer to a float."##, }, Lint { + label: "clippy::transmute_int_to_non_zero", + description: r##"Checks for transmutes from integers to `NonZero*` types, and suggests their `new_unchecked` +method instead."##, + }, + Lint { + label: "clippy::transmute_null_to_fn", + description: r##"Checks for null function pointer creation through transmute."##, + }, + Lint { label: "clippy::transmute_num_to_bytes", description: r##"Checks for transmutes from a number to an array of `u8`"##, }, @@ -6647,6 +12695,11 @@ from a reference to a reference."##, description: r##"Checks for transmutes from a pointer to a reference."##, }, Lint { + label: "clippy::transmute_undefined_repr", + description: r##"Checks for transmutes between types which do not have a representation defined relative to +each other."##, + }, + Lint { label: "clippy::transmutes_expressible_as_ptr_casts", description: r##"Checks for transmutes that could be a pointer cast."##, }, @@ -6655,6 +12708,10 @@ from a reference to a reference."##, description: r##"Checks for transmute calls which would receive a null pointer."##, }, Lint { + label: "clippy::trim_split_whitespace", + description: r##"Warns about calling `str::trim` (or variants) before `str::split_whitespace`."##, + }, + Lint { label: "clippy::trivial_regex", description: r##"Checks for trivial [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`)."##, @@ -6665,25 +12722,51 @@ creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`)."##, the argument type is `Copy` and small enough to be more efficient to always pass by value."##, }, - Lint { label: "clippy::try_err", description: r##"Checks for usages of `Err(x)?`."## }, + Lint { label: "clippy::try_err", description: r##"Checks for usage of `Err(x)?`."## }, + Lint { + label: "clippy::tuple_array_conversions", + description: r##"Checks for tuple<=>array conversions that are not done with `.into()`."##, + }, Lint { label: "clippy::type_complexity", description: r##"Checks for types used in structs, parameters and `let` declarations above a certain complexity threshold."##, }, Lint { + label: "clippy::type_id_on_box", + description: r##"Looks for calls to `<Box<dyn Any> as Any>::type_id`."##, + }, + Lint { label: "clippy::type_repetition_in_bounds", description: r##"This lint warns about unnecessary type repetitions in trait bounds"##, }, Lint { - label: "clippy::undocumented_unsafe_blocks", - description: r##"Checks for `unsafe` blocks without a `// Safety: ` comment -explaining why the unsafe operations performed inside -the block are safe."##, + label: "clippy::unchecked_duration_subtraction", + description: r##"Lints subtraction between an [`Instant`] and a [`Duration`]."##, }, Lint { - label: "clippy::undropped_manually_drops", - description: r##"Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`."##, + label: "clippy::undocumented_unsafe_blocks", + description: r##"Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment +explaining why the unsafe operations performed inside +the block are safe. + +Note the comment must appear on the line(s) preceding the unsafe block +with nothing appearing in between. The following is ok: +```rust +foo( + // SAFETY: + // This is a valid safety comment + unsafe { *x } +) +``` +But neither of these are: +```rust +// SAFETY: +// This is not a valid safety comment +foo( + /* SAFETY: Neither is this */ unsafe { *x }, +); +```"##, }, Lint { label: "clippy::unicode_not_nfc", @@ -6706,6 +12789,11 @@ This is commonly caused by calling `set_len()` right after allocating or reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`."##, }, Lint { + label: "clippy::uninlined_format_args", + description: r##"Detect when a variable is not inlined in a format string, +and suggests to inline it."##, + }, + Lint { label: "clippy::unit_arg", description: r##"Checks for passing a unit value as an argument to a function without using a unit literal (`()`)."##, @@ -6723,23 +12811,40 @@ Fn(...) -> Ord where the implemented closure returns the unit type. The lint also suggests to remove the semi-colon at the end of the statement if present."##, }, Lint { + label: "clippy::unnecessary_box_returns", + description: r##"Checks for a return type containing a `Box<T>` where `T` implements `Sized` + +The lint ignores `Box<T>` where `T` is larger than `unnecessary_box_size`, +as returning a large `T` directly may be detrimental to performance."##, + }, + Lint { label: "clippy::unnecessary_cast", - description: r##"Checks for casts to the same type, casts of int literals to integer types -and casts of float literals to float types."##, + description: r##"Checks for casts to the same type, casts of int literals to integer types, casts of float +literals to float types and casts between raw pointers without changing type or constness."##, }, Lint { label: "clippy::unnecessary_filter_map", - description: r##"Checks for `filter_map` calls which could be replaced by `filter` or `map`. + description: r##"Checks for `filter_map` calls that could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the filter or map operations and suggests the appropriate option."##, }, Lint { + label: "clippy::unnecessary_find_map", + description: r##"Checks for `find_map` calls that could be replaced by `find` or `map`. More +specifically it checks if the closure provided is only performing one of the +find or map operations and suggests the appropriate option."##, + }, + Lint { label: "clippy::unnecessary_fold", - description: r##"Checks for using `fold` when a more succinct alternative exists. + description: r##"Checks for usage of `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`, `sum` or `product`."##, }, Lint { + label: "clippy::unnecessary_join", + description: r##"Checks for usage of `.collect::<Vec<String>>().join()` on iterators."##, + }, + Lint { label: "clippy::unnecessary_lazy_evaluations", description: r##"As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`. @@ -6750,7 +12855,16 @@ simpler code: - `and_then` to `and` - `or_else` to `or` - `get_or_insert_with` to `get_or_insert` - - `ok_or_else` to `ok_or`"##, + - `ok_or_else` to `ok_or` + - `then` to `then_some` (for msrv >= 1.62.0)"##, + }, + Lint { + label: "clippy::unnecessary_literal_unwrap", + description: r##"Checks for `.unwrap()` related calls on `Result`s and `Option`s that are constructed."##, + }, + Lint { + label: "clippy::unnecessary_map_on_constructor", + description: r##"Suggest removing the use of a may (or map_err) method when an Option or Result is being construted."##, }, Lint { label: "clippy::unnecessary_mut_passed", @@ -6763,15 +12877,33 @@ requires an immutable reference."##, sub-expression."##, }, Lint { + label: "clippy::unnecessary_owned_empty_strings", + description: r##"Detects cases of owned empty strings being passed as an argument to a function expecting `&str`"##, + }, + Lint { + label: "clippy::unnecessary_safety_comment", + description: r##"Checks for `// SAFETY: ` comments on safe code."##, + }, + Lint { + label: "clippy::unnecessary_safety_doc", + description: r##"Checks for the doc comments of publicly visible +safe functions and traits and warns if there is a `# Safety` section."##, + }, + Lint { label: "clippy::unnecessary_self_imports", description: r##"Checks for imports ending in `::{self}`."##, }, Lint { label: "clippy::unnecessary_sort_by", - description: r##"Detects uses of `Vec::sort_by` passing in a closure + description: r##"Checks for usage of `Vec::sort_by` passing in a closure which compares the two arguments, either directly or indirectly."##, }, Lint { + label: "clippy::unnecessary_struct_initialization", + description: r##"Checks for initialization of a `struct` by copying a base without setting +any field."##, + }, + Lint { label: "clippy::unnecessary_to_owned", description: r##"Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) and other `to_owned`-like functions."##, @@ -6855,10 +12987,24 @@ types have different ABI, size or alignment."##, description: r##"Nothing. This lint has been deprecated."##, }, Lint { + label: "clippy::unused_format_specs", + description: r##"Detects [formatting parameters] that have no effect on the output of +`format!()`, `println!()` or similar macros."##, + }, + Lint { label: "clippy::unused_io_amount", description: r##"Checks for unused written/read amount."##, }, Lint { + label: "clippy::unused_peekable", + description: r##"Checks for the creation of a `peekable` iterator that is never `.peek()`ed"##, + }, + Lint { + label: "clippy::unused_rounding", + description: r##"Detects cases where a whole-number literal float is being rounded, using +the `floor`, `ceil`, or `round` methods."##, + }, + Lint { label: "clippy::unused_self", description: r##"Checks methods that contain a `self` argument but don't use it"##, }, @@ -6876,13 +13022,17 @@ by nibble or byte."##, description: r##"Checks for functions of type `Result` that contain `expect()` or `unwrap()`"##, }, Lint { - label: "clippy::unwrap_or_else_default", - description: r##"Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and -`Result` values."##, + label: "clippy::unwrap_or_default", + description: r##"Checks for usages of the following functions with an argument that constructs a default value +(e.g., `Default::default` or `String::new`): +- `unwrap_or` +- `unwrap_or_else` +- `or_insert` +- `or_insert_with`"##, }, Lint { label: "clippy::unwrap_used", - description: r##"Checks for `.unwrap()` calls on `Option`s and on `Result`s."##, + description: r##"Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s."##, }, Lint { label: "clippy::upper_case_acronyms", @@ -6890,7 +13040,7 @@ by nibble or byte."##, }, Lint { label: "clippy::use_debug", - description: r##"Checks for use of `Debug` formatting. The purpose of this + description: r##"Checks for usage of `Debug` formatting. The purpose of this lint is to catch debugging remnants."##, }, Lint { @@ -6913,10 +13063,17 @@ types before and after the call are the same."##, description: r##"Checks for `extern crate` and `use` items annotated with lint attributes. -This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, -`#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and -`#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on -`extern crate` items with a `#[macro_use]` attribute."##, +This lint permits lint attributes for lints emitted on the items themself. +For `use` items these lints are: +* deprecated +* unreachable_pub +* unused_imports +* clippy::enum_glob_use +* clippy::macro_use_imports +* clippy::wildcard_imports + +For `extern crate` items these lints are: +* `unused_imports` on items with `#[macro_use]`"##, }, Lint { label: "clippy::useless_conversion", @@ -6940,17 +13097,24 @@ and transmutes that could be a cast."##, }, Lint { label: "clippy::useless_vec", - description: r##"Checks for usage of `&vec![..]` when using `&[..]` would + description: r##"Checks for usage of `vec![..]` when using `[..]` would be possible."##, }, Lint { label: "clippy::vec_box", - description: r##"Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code. + description: r##"Checks for usage of `Vec<Box<T>>` where T: Sized anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, }, Lint { label: "clippy::vec_init_then_push", - description: r##"Checks for calls to `push` immediately after creating a new `Vec`."##, + description: r##"Checks for calls to `push` immediately after creating a new `Vec`. + +If the `Vec` is created using `with_capacity` this will only lint if the capacity is a +constant and the number of pushes is greater than or equal to the initial capacity. + +If the `Vec` is extended after the initial sequence of pushes and it was default initialized +then this will only lint after there were at least four pushes. This number may change in +the future."##, }, Lint { label: "clippy::vec_resize_to_zero", @@ -6963,7 +13127,7 @@ to `trailing_zeros`"##, }, Lint { label: "clippy::verbose_file_reads", - description: r##"Checks for use of File::read_to_end and File::read_to_string."##, + description: r##"Checks for usage of File::read_to_end and File::read_to_string."##, }, Lint { label: "clippy::vtable_address_comparisons", @@ -7020,18 +13184,19 @@ print a newline."##, }, Lint { label: "clippy::wrong_self_convention", - description: r##"Checks for methods with certain name prefixes and which -doesn't match how self is taken. The actual rules are: - -|Prefix |Postfix |`self` taken | `self` type | -|-------|------------|-----------------------|--------------| -|`as_` | none |`&self` or `&mut self` | any | -|`from_`| none | none | any | -|`into_`| none |`self` | any | -|`is_` | none |`&self` or none | any | -|`to_` | `_mut` |`&mut self` | any | -|`to_` | not `_mut` |`self` | `Copy` | -|`to_` | not `_mut` |`&self` | not `Copy` | + description: r##"Checks for methods with certain name prefixes or suffixes, and which +do not adhere to standard conventions regarding how `self` is taken. +The actual rules are: + +|Prefix |Postfix |`self` taken | `self` type | +|-------|------------|-------------------------------|--------------| +|`as_` | none |`&self` or `&mut self` | any | +|`from_`| none | none | any | +|`into_`| none |`self` | any | +|`is_` | none |`&mut self` or `&self` or none | any | +|`to_` | `_mut` |`&mut self` | any | +|`to_` | not `_mut` |`self` | `Copy` | +|`to_` | not `_mut` |`&self` | not `Copy` | Note: Clippy doesn't trigger methods with `to_` prefix in: - Traits definition. @@ -7086,15 +13251,18 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::complexity", - description: r##"lint group for: clippy::bind_instead_of_map, clippy::bool_comparison, clippy::borrowed_box, clippy::char_lit_as_u8, clippy::clone_on_copy, clippy::crosspointer_transmute, clippy::deprecated_cfg_attr, clippy::deref_addrof, clippy::derivable_impls, clippy::diverging_sub_expression, clippy::double_comparisons, clippy::double_parens, clippy::duration_subsec, clippy::explicit_counter_loop, clippy::explicit_write, clippy::extra_unused_lifetimes, clippy::filter_map_identity, clippy::filter_next, clippy::flat_map_identity, clippy::get_last_with_len, clippy::identity_op, clippy::inspect_for_each, clippy::int_plus_one, clippy::iter_count, clippy::manual_filter_map, clippy::manual_find_map, clippy::manual_flatten, clippy::manual_split_once, clippy::manual_strip, clippy::manual_swap, clippy::manual_unwrap_or, clippy::map_flatten, clippy::map_identity, clippy::match_as_ref, clippy::match_single_binding, clippy::needless_arbitrary_self_type, clippy::needless_bool, clippy::needless_borrowed_reference, clippy::needless_lifetimes, clippy::needless_option_as_deref, clippy::needless_question_mark, clippy::needless_splitn, clippy::needless_update, clippy::neg_cmp_op_on_partial_ord, clippy::no_effect, clippy::nonminimal_bool, clippy::option_as_ref_deref, clippy::option_filter_map, clippy::option_map_unit_fn, clippy::overflow_check_conditional, clippy::partialeq_ne_impl, clippy::precedence, clippy::ptr_offset_with_cast, clippy::range_zip_with_len, clippy::redundant_closure_call, clippy::redundant_slicing, clippy::ref_in_deref, clippy::repeat_once, clippy::result_map_unit_fn, clippy::search_is_some, clippy::short_circuit_statement, clippy::single_element_loop, clippy::skip_while_next, clippy::string_from_utf8_as_bytes, clippy::strlen_on_c_strings, clippy::temporary_assignment, clippy::too_many_arguments, clippy::transmute_bytes_to_str, clippy::transmute_float_to_int, clippy::transmute_int_to_bool, clippy::transmute_int_to_char, clippy::transmute_int_to_float, clippy::transmute_num_to_bytes, clippy::transmute_ptr_to_ref, clippy::transmutes_expressible_as_ptr_casts, clippy::type_complexity, clippy::unit_arg, clippy::unnecessary_cast, clippy::unnecessary_filter_map, clippy::unnecessary_operation, clippy::unnecessary_sort_by, clippy::unnecessary_unwrap, clippy::unneeded_wildcard_pattern, clippy::useless_asref, clippy::useless_conversion, clippy::useless_format, clippy::vec_box, clippy::while_let_loop, clippy::wildcard_in_or_patterns, clippy::zero_divided_by_zero, clippy::zero_prefixed_literal"##, + description: r##"lint group for: clippy::bind_instead_of_map, clippy::bool_comparison, clippy::borrow_deref_ref, clippy::borrowed_box, clippy::bytes_count_to_len, clippy::char_lit_as_u8, clippy::clone_on_copy, clippy::crosspointer_transmute, clippy::default_constructed_unit_structs, clippy::deprecated_cfg_attr, clippy::deref_addrof, clippy::derivable_impls, clippy::diverging_sub_expression, clippy::double_comparisons, clippy::double_parens, clippy::duration_subsec, clippy::excessive_nesting, clippy::explicit_auto_deref, clippy::explicit_counter_loop, clippy::explicit_write, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, clippy::filter_map_identity, clippy::filter_next, clippy::flat_map_identity, clippy::get_last_with_len, clippy::identity_op, clippy::inspect_for_each, clippy::int_plus_one, clippy::iter_count, clippy::iter_kv_map, clippy::let_with_type_underscore, clippy::manual_filter, clippy::manual_filter_map, clippy::manual_find, clippy::manual_find_map, clippy::manual_flatten, clippy::manual_hash_one, clippy::manual_main_separator_str, clippy::manual_range_patterns, clippy::manual_rem_euclid, clippy::manual_slice_size_calculation, clippy::manual_split_once, clippy::manual_strip, clippy::manual_swap, clippy::manual_unwrap_or, clippy::map_flatten, clippy::map_identity, clippy::match_as_ref, clippy::match_single_binding, clippy::needless_arbitrary_self_type, clippy::needless_bool, clippy::needless_bool_assign, clippy::needless_borrowed_reference, clippy::needless_if, clippy::needless_lifetimes, clippy::needless_match, clippy::needless_option_as_deref, clippy::needless_option_take, clippy::needless_question_mark, clippy::needless_splitn, clippy::needless_update, clippy::neg_cmp_op_on_partial_ord, clippy::no_effect, clippy::nonminimal_bool, clippy::only_used_in_recursion, clippy::option_as_ref_deref, clippy::option_filter_map, clippy::option_map_unit_fn, clippy::or_then_unwrap, clippy::overflow_check_conditional, clippy::partialeq_ne_impl, clippy::precedence, clippy::ptr_offset_with_cast, clippy::range_zip_with_len, clippy::redundant_as_str, clippy::redundant_async_block, clippy::redundant_at_rest_pattern, clippy::redundant_closure_call, clippy::redundant_guards, clippy::redundant_slicing, clippy::repeat_once, clippy::reserve_after_initialization, clippy::result_map_unit_fn, clippy::search_is_some, clippy::seek_from_current, clippy::seek_to_start_instead_of_rewind, clippy::short_circuit_statement, clippy::single_element_loop, clippy::skip_while_next, clippy::string_from_utf8_as_bytes, clippy::strlen_on_c_strings, clippy::temporary_assignment, clippy::too_many_arguments, clippy::transmute_bytes_to_str, clippy::transmute_float_to_int, clippy::transmute_int_to_bool, clippy::transmute_int_to_char, clippy::transmute_int_to_float, clippy::transmute_int_to_non_zero, clippy::transmute_num_to_bytes, clippy::transmute_ptr_to_ref, clippy::transmutes_expressible_as_ptr_casts, clippy::type_complexity, clippy::unit_arg, clippy::unnecessary_cast, clippy::unnecessary_filter_map, clippy::unnecessary_find_map, clippy::unnecessary_literal_unwrap, clippy::unnecessary_map_on_constructor, clippy::unnecessary_operation, clippy::unnecessary_sort_by, clippy::unnecessary_unwrap, clippy::unneeded_wildcard_pattern, clippy::unused_format_specs, clippy::useless_asref, clippy::useless_conversion, clippy::useless_format, clippy::useless_transmute, clippy::vec_box, clippy::while_let_loop, clippy::wildcard_in_or_patterns, clippy::zero_divided_by_zero, clippy::zero_prefixed_literal"##, }, children: &[ "clippy::bind_instead_of_map", "clippy::bool_comparison", + "clippy::borrow_deref_ref", "clippy::borrowed_box", + "clippy::bytes_count_to_len", "clippy::char_lit_as_u8", "clippy::clone_on_copy", "clippy::crosspointer_transmute", + "clippy::default_constructed_unit_structs", "clippy::deprecated_cfg_attr", "clippy::deref_addrof", "clippy::derivable_impls", @@ -7102,9 +13270,12 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::double_comparisons", "clippy::double_parens", "clippy::duration_subsec", + "clippy::excessive_nesting", + "clippy::explicit_auto_deref", "clippy::explicit_counter_loop", "clippy::explicit_write", "clippy::extra_unused_lifetimes", + "clippy::extra_unused_type_parameters", "clippy::filter_map_identity", "clippy::filter_next", "clippy::flat_map_identity", @@ -7113,9 +13284,18 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::inspect_for_each", "clippy::int_plus_one", "clippy::iter_count", + "clippy::iter_kv_map", + "clippy::let_with_type_underscore", + "clippy::manual_filter", "clippy::manual_filter_map", + "clippy::manual_find", "clippy::manual_find_map", "clippy::manual_flatten", + "clippy::manual_hash_one", + "clippy::manual_main_separator_str", + "clippy::manual_range_patterns", + "clippy::manual_rem_euclid", + "clippy::manual_slice_size_calculation", "clippy::manual_split_once", "clippy::manual_strip", "clippy::manual_swap", @@ -7126,29 +13306,41 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::match_single_binding", "clippy::needless_arbitrary_self_type", "clippy::needless_bool", + "clippy::needless_bool_assign", "clippy::needless_borrowed_reference", + "clippy::needless_if", "clippy::needless_lifetimes", + "clippy::needless_match", "clippy::needless_option_as_deref", + "clippy::needless_option_take", "clippy::needless_question_mark", "clippy::needless_splitn", "clippy::needless_update", "clippy::neg_cmp_op_on_partial_ord", "clippy::no_effect", "clippy::nonminimal_bool", + "clippy::only_used_in_recursion", "clippy::option_as_ref_deref", "clippy::option_filter_map", "clippy::option_map_unit_fn", + "clippy::or_then_unwrap", "clippy::overflow_check_conditional", "clippy::partialeq_ne_impl", "clippy::precedence", "clippy::ptr_offset_with_cast", "clippy::range_zip_with_len", + "clippy::redundant_as_str", + "clippy::redundant_async_block", + "clippy::redundant_at_rest_pattern", "clippy::redundant_closure_call", + "clippy::redundant_guards", "clippy::redundant_slicing", - "clippy::ref_in_deref", "clippy::repeat_once", + "clippy::reserve_after_initialization", "clippy::result_map_unit_fn", "clippy::search_is_some", + "clippy::seek_from_current", + "clippy::seek_to_start_instead_of_rewind", "clippy::short_circuit_statement", "clippy::single_element_loop", "clippy::skip_while_next", @@ -7161,6 +13353,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::transmute_int_to_bool", "clippy::transmute_int_to_char", "clippy::transmute_int_to_float", + "clippy::transmute_int_to_non_zero", "clippy::transmute_num_to_bytes", "clippy::transmute_ptr_to_ref", "clippy::transmutes_expressible_as_ptr_casts", @@ -7168,13 +13361,18 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::unit_arg", "clippy::unnecessary_cast", "clippy::unnecessary_filter_map", + "clippy::unnecessary_find_map", + "clippy::unnecessary_literal_unwrap", + "clippy::unnecessary_map_on_constructor", "clippy::unnecessary_operation", "clippy::unnecessary_sort_by", "clippy::unnecessary_unwrap", "clippy::unneeded_wildcard_pattern", + "clippy::unused_format_specs", "clippy::useless_asref", "clippy::useless_conversion", "clippy::useless_format", + "clippy::useless_transmute", "clippy::vec_box", "clippy::while_let_loop", "clippy::wildcard_in_or_patterns", @@ -7185,7 +13383,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::correctness", - description: r##"lint group for: clippy::absurd_extreme_comparisons, clippy::almost_swapped, clippy::approx_constant, clippy::async_yields_async, clippy::bad_bit_mask, clippy::cast_ref_to_mut, clippy::clone_double_ref, clippy::cmp_nan, clippy::deprecated_semver, clippy::derive_hash_xor_eq, clippy::derive_ord_xor_partial_ord, clippy::drop_copy, clippy::drop_ref, clippy::enum_clike_unportable_variant, clippy::eq_op, clippy::erasing_op, clippy::fn_address_comparisons, clippy::forget_copy, clippy::forget_ref, clippy::if_let_mutex, clippy::if_same_then_else, clippy::ifs_same_cond, clippy::ineffective_bit_mask, clippy::infinite_iter, clippy::inherent_to_string_shadow_display, clippy::inline_fn_without_body, clippy::invalid_null_ptr_usage, clippy::invalid_regex, clippy::invisible_characters, clippy::iter_next_loop, clippy::iterator_step_by_zero, clippy::let_underscore_lock, clippy::logic_bug, clippy::match_str_case_mismatch, clippy::mem_replace_with_uninit, clippy::min_max, clippy::mismatched_target_os, clippy::mistyped_literal_suffixes, clippy::modulo_one, clippy::mut_from_ref, clippy::never_loop, clippy::non_octal_unix_permissions, clippy::nonsensical_open_options, clippy::not_unsafe_ptr_arg_deref, clippy::option_env_unwrap, clippy::out_of_bounds_indexing, clippy::panicking_unwrap, clippy::possible_missing_comma, clippy::reversed_empty_ranges, clippy::self_assignment, clippy::serde_api_misuse, clippy::size_of_in_element_count, clippy::suspicious_splitn, clippy::to_string_in_display, clippy::transmuting_null, clippy::undropped_manually_drops, clippy::uninit_assumed_init, clippy::uninit_vec, clippy::unit_cmp, clippy::unit_hash, clippy::unit_return_expecting_ord, clippy::unsound_collection_transmute, clippy::unused_io_amount, clippy::useless_attribute, clippy::vec_resize_to_zero, clippy::vtable_address_comparisons, clippy::while_immutable_condition, clippy::wrong_transmute, clippy::zst_offset"##, + description: r##"lint group for: clippy::absurd_extreme_comparisons, clippy::almost_swapped, clippy::approx_constant, clippy::async_yields_async, clippy::bad_bit_mask, clippy::cast_slice_different_sizes, clippy::deprecated_semver, clippy::derive_ord_xor_partial_ord, clippy::derived_hash_with_manual_eq, clippy::enum_clike_unportable_variant, clippy::eq_op, clippy::erasing_op, clippy::fn_address_comparisons, clippy::if_let_mutex, clippy::if_same_then_else, clippy::ifs_same_cond, clippy::impossible_comparisons, clippy::ineffective_bit_mask, clippy::infinite_iter, clippy::inherent_to_string_shadow_display, clippy::inline_fn_without_body, clippy::invalid_null_ptr_usage, clippy::invalid_regex, clippy::invisible_characters, clippy::iter_next_loop, clippy::iter_skip_zero, clippy::iterator_step_by_zero, clippy::let_underscore_lock, clippy::match_str_case_mismatch, clippy::mem_replace_with_uninit, clippy::min_max, clippy::mismatched_target_os, clippy::mistyped_literal_suffixes, clippy::modulo_one, clippy::mut_from_ref, clippy::never_loop, clippy::non_octal_unix_permissions, clippy::nonsensical_open_options, clippy::not_unsafe_ptr_arg_deref, clippy::option_env_unwrap, clippy::out_of_bounds_indexing, clippy::overly_complex_bool_expr, clippy::panicking_unwrap, clippy::possible_missing_comma, clippy::read_line_without_trim, clippy::read_zero_byte_vec, clippy::recursive_format_impl, clippy::redundant_comparisons, clippy::redundant_locals, clippy::reversed_empty_ranges, clippy::self_assignment, clippy::serde_api_misuse, clippy::size_of_in_element_count, clippy::suspicious_splitn, clippy::transmute_null_to_fn, clippy::transmuting_null, clippy::uninit_assumed_init, clippy::uninit_vec, clippy::unit_cmp, clippy::unit_hash, clippy::unit_return_expecting_ord, clippy::unsound_collection_transmute, clippy::unused_io_amount, clippy::useless_attribute, clippy::vec_resize_to_zero, clippy::vtable_address_comparisons, clippy::while_immutable_condition, clippy::wrong_transmute, clippy::zst_offset"##, }, children: &[ "clippy::absurd_extreme_comparisons", @@ -7193,23 +13391,18 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::approx_constant", "clippy::async_yields_async", "clippy::bad_bit_mask", - "clippy::cast_ref_to_mut", - "clippy::clone_double_ref", - "clippy::cmp_nan", + "clippy::cast_slice_different_sizes", "clippy::deprecated_semver", - "clippy::derive_hash_xor_eq", "clippy::derive_ord_xor_partial_ord", - "clippy::drop_copy", - "clippy::drop_ref", + "clippy::derived_hash_with_manual_eq", "clippy::enum_clike_unportable_variant", "clippy::eq_op", "clippy::erasing_op", "clippy::fn_address_comparisons", - "clippy::forget_copy", - "clippy::forget_ref", "clippy::if_let_mutex", "clippy::if_same_then_else", "clippy::ifs_same_cond", + "clippy::impossible_comparisons", "clippy::ineffective_bit_mask", "clippy::infinite_iter", "clippy::inherent_to_string_shadow_display", @@ -7218,9 +13411,9 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::invalid_regex", "clippy::invisible_characters", "clippy::iter_next_loop", + "clippy::iter_skip_zero", "clippy::iterator_step_by_zero", "clippy::let_underscore_lock", - "clippy::logic_bug", "clippy::match_str_case_mismatch", "clippy::mem_replace_with_uninit", "clippy::min_max", @@ -7234,16 +13427,21 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::not_unsafe_ptr_arg_deref", "clippy::option_env_unwrap", "clippy::out_of_bounds_indexing", + "clippy::overly_complex_bool_expr", "clippy::panicking_unwrap", "clippy::possible_missing_comma", + "clippy::read_line_without_trim", + "clippy::read_zero_byte_vec", + "clippy::recursive_format_impl", + "clippy::redundant_comparisons", + "clippy::redundant_locals", "clippy::reversed_empty_ranges", "clippy::self_assignment", "clippy::serde_api_misuse", "clippy::size_of_in_element_count", "clippy::suspicious_splitn", - "clippy::to_string_in_display", + "clippy::transmute_null_to_fn", "clippy::transmuting_null", - "clippy::undropped_manually_drops", "clippy::uninit_assumed_init", "clippy::uninit_vec", "clippy::unit_cmp", @@ -7286,45 +13484,66 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::nursery", - description: r##"lint group for: clippy::branches_sharing_code, clippy::cognitive_complexity, clippy::debug_assert_with_mut_call, clippy::disallowed_methods, clippy::disallowed_types, clippy::empty_line_after_outer_attr, clippy::equatable_if_let, clippy::fallible_impl_from, clippy::future_not_send, clippy::imprecise_flops, clippy::index_refutable_slice, clippy::missing_const_for_fn, clippy::mutex_integer, clippy::non_send_fields_in_send_ty, clippy::nonstandard_macro_braces, clippy::option_if_let_else, clippy::path_buf_push_overwrite, clippy::redundant_pub_crate, clippy::string_lit_as_bytes, clippy::suboptimal_flops, clippy::suspicious_operation_groupings, clippy::trailing_empty_array, clippy::trivial_regex, clippy::use_self, clippy::useless_let_if_seq, clippy::useless_transmute"##, + description: r##"lint group for: clippy::as_ptr_cast_mut, clippy::branches_sharing_code, clippy::clear_with_drain, clippy::cognitive_complexity, clippy::collection_is_never_read, clippy::debug_assert_with_mut_call, clippy::derive_partial_eq_without_eq, clippy::empty_line_after_doc_comments, clippy::empty_line_after_outer_attr, clippy::equatable_if_let, clippy::fallible_impl_from, clippy::future_not_send, clippy::implied_bounds_in_impls, clippy::imprecise_flops, clippy::iter_on_empty_collections, clippy::iter_on_single_items, clippy::iter_with_drain, clippy::large_stack_frames, clippy::manual_clamp, clippy::missing_const_for_fn, clippy::mutex_integer, clippy::needless_collect, clippy::needless_pass_by_ref_mut, clippy::non_send_fields_in_send_ty, clippy::nonstandard_macro_braces, clippy::option_if_let_else, clippy::or_fun_call, clippy::path_buf_push_overwrite, clippy::readonly_write_lock, clippy::redundant_clone, clippy::redundant_pub_crate, clippy::significant_drop_in_scrutinee, clippy::significant_drop_tightening, clippy::string_lit_as_bytes, clippy::suboptimal_flops, clippy::suspicious_operation_groupings, clippy::trailing_empty_array, clippy::trait_duplication_in_bounds, clippy::transmute_undefined_repr, clippy::trivial_regex, clippy::tuple_array_conversions, clippy::type_repetition_in_bounds, clippy::unnecessary_struct_initialization, clippy::unused_peekable, clippy::unused_rounding, clippy::use_self, clippy::useless_let_if_seq"##, }, children: &[ + "clippy::as_ptr_cast_mut", "clippy::branches_sharing_code", + "clippy::clear_with_drain", "clippy::cognitive_complexity", + "clippy::collection_is_never_read", "clippy::debug_assert_with_mut_call", - "clippy::disallowed_methods", - "clippy::disallowed_types", + "clippy::derive_partial_eq_without_eq", + "clippy::empty_line_after_doc_comments", "clippy::empty_line_after_outer_attr", "clippy::equatable_if_let", "clippy::fallible_impl_from", "clippy::future_not_send", + "clippy::implied_bounds_in_impls", "clippy::imprecise_flops", - "clippy::index_refutable_slice", + "clippy::iter_on_empty_collections", + "clippy::iter_on_single_items", + "clippy::iter_with_drain", + "clippy::large_stack_frames", + "clippy::manual_clamp", "clippy::missing_const_for_fn", "clippy::mutex_integer", + "clippy::needless_collect", + "clippy::needless_pass_by_ref_mut", "clippy::non_send_fields_in_send_ty", "clippy::nonstandard_macro_braces", "clippy::option_if_let_else", + "clippy::or_fun_call", "clippy::path_buf_push_overwrite", + "clippy::readonly_write_lock", + "clippy::redundant_clone", "clippy::redundant_pub_crate", + "clippy::significant_drop_in_scrutinee", + "clippy::significant_drop_tightening", "clippy::string_lit_as_bytes", "clippy::suboptimal_flops", "clippy::suspicious_operation_groupings", "clippy::trailing_empty_array", + "clippy::trait_duplication_in_bounds", + "clippy::transmute_undefined_repr", "clippy::trivial_regex", + "clippy::tuple_array_conversions", + "clippy::type_repetition_in_bounds", + "clippy::unnecessary_struct_initialization", + "clippy::unused_peekable", + "clippy::unused_rounding", "clippy::use_self", "clippy::useless_let_if_seq", - "clippy::useless_transmute", ], }, LintGroup { lint: Lint { label: "clippy::pedantic", - description: r##"lint group for: clippy::await_holding_lock, clippy::await_holding_refcell_ref, clippy::case_sensitive_file_extension_comparisons, clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_ptr_alignment, clippy::cast_sign_loss, clippy::checked_conversions, clippy::cloned_instead_of_copied, clippy::copy_iterator, clippy::default_trait_access, clippy::doc_markdown, clippy::empty_enum, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_deref_methods, clippy::explicit_into_iter_loop, clippy::explicit_iter_loop, clippy::filter_map_next, clippy::flat_map_option, clippy::float_cmp, clippy::fn_params_excessive_bools, clippy::from_iter_instead_of_collect, clippy::if_not_else, clippy::implicit_clone, clippy::implicit_hasher, clippy::implicit_saturating_sub, clippy::inconsistent_struct_constructor, clippy::inefficient_to_string, clippy::inline_always, clippy::invalid_upcast_comparisons, clippy::items_after_statements, clippy::iter_not_returning_iterator, clippy::large_digit_groups, clippy::large_stack_arrays, clippy::large_types_passed_by_value, clippy::let_underscore_drop, clippy::let_unit_value, clippy::linkedlist, clippy::macro_use_imports, clippy::manual_assert, clippy::manual_ok_or, clippy::many_single_char_names, clippy::map_unwrap_or, clippy::match_bool, clippy::match_on_vec_items, clippy::match_same_arms, clippy::match_wild_err_arm, clippy::match_wildcard_for_single_variants, clippy::maybe_infinite_iter, clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::module_name_repetitions, clippy::must_use_candidate, clippy::mut_mut, clippy::naive_bytecount, clippy::needless_bitwise_bool, clippy::needless_continue, clippy::needless_for_each, clippy::needless_pass_by_value, clippy::no_effect_underscore_binding, clippy::option_option, clippy::ptr_as_ptr, clippy::range_minus_one, clippy::range_plus_one, clippy::redundant_closure_for_method_calls, clippy::redundant_else, clippy::ref_binding_to_reference, clippy::ref_option_ref, clippy::same_functions_in_if_condition, clippy::semicolon_if_nothing_returned, clippy::similar_names, clippy::single_match_else, clippy::string_add_assign, clippy::struct_excessive_bools, clippy::too_many_lines, clippy::trait_duplication_in_bounds, clippy::transmute_ptr_to_ptr, clippy::trivially_copy_pass_by_ref, clippy::type_repetition_in_bounds, clippy::unicode_not_nfc, clippy::unnecessary_wraps, clippy::unnested_or_patterns, clippy::unreadable_literal, clippy::unsafe_derive_deserialize, clippy::unused_async, clippy::unused_self, clippy::used_underscore_binding, clippy::verbose_bit_mask, clippy::wildcard_imports, clippy::zero_sized_map_values"##, + description: r##"lint group for: clippy::bool_to_int_with_if, clippy::borrow_as_ptr, clippy::case_sensitive_file_extension_comparisons, clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_ptr_alignment, clippy::cast_sign_loss, clippy::checked_conversions, clippy::cloned_instead_of_copied, clippy::copy_iterator, clippy::default_trait_access, clippy::doc_link_with_quotes, clippy::doc_markdown, clippy::empty_enum, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_deref_methods, clippy::explicit_into_iter_loop, clippy::explicit_iter_loop, clippy::filter_map_next, clippy::flat_map_option, clippy::float_cmp, clippy::fn_params_excessive_bools, clippy::from_iter_instead_of_collect, clippy::if_not_else, clippy::ignored_unit_patterns, clippy::implicit_clone, clippy::implicit_hasher, clippy::inconsistent_struct_constructor, clippy::index_refutable_slice, clippy::inefficient_to_string, clippy::inline_always, clippy::into_iter_without_iter, clippy::invalid_upcast_comparisons, clippy::items_after_statements, clippy::iter_not_returning_iterator, clippy::iter_without_into_iter, clippy::large_digit_groups, clippy::large_futures, clippy::large_stack_arrays, clippy::large_types_passed_by_value, clippy::linkedlist, clippy::macro_use_imports, clippy::manual_assert, clippy::manual_instant_elapsed, clippy::manual_let_else, clippy::manual_ok_or, clippy::manual_string_new, clippy::many_single_char_names, clippy::map_unwrap_or, clippy::match_bool, clippy::match_on_vec_items, clippy::match_same_arms, clippy::match_wild_err_arm, clippy::match_wildcard_for_single_variants, clippy::maybe_infinite_iter, clippy::mismatching_type_param_order, clippy::missing_errors_doc, clippy::missing_fields_in_debug, clippy::missing_panics_doc, clippy::module_name_repetitions, clippy::must_use_candidate, clippy::mut_mut, clippy::naive_bytecount, clippy::needless_bitwise_bool, clippy::needless_continue, clippy::needless_for_each, clippy::needless_pass_by_value, clippy::needless_raw_string_hashes, clippy::no_effect_underscore_binding, clippy::no_mangle_with_rust_abi, clippy::option_option, clippy::ptr_as_ptr, clippy::ptr_cast_constness, clippy::range_minus_one, clippy::range_plus_one, clippy::redundant_closure_for_method_calls, clippy::redundant_else, clippy::ref_binding_to_reference, clippy::ref_option_ref, clippy::return_self_not_must_use, clippy::same_functions_in_if_condition, clippy::semicolon_if_nothing_returned, clippy::should_panic_without_expect, clippy::similar_names, clippy::single_match_else, clippy::stable_sort_primitive, clippy::string_add_assign, clippy::struct_excessive_bools, clippy::too_many_lines, clippy::transmute_ptr_to_ptr, clippy::trivially_copy_pass_by_ref, clippy::unchecked_duration_subtraction, clippy::unicode_not_nfc, clippy::uninlined_format_args, clippy::unnecessary_box_returns, clippy::unnecessary_join, clippy::unnecessary_wraps, clippy::unnested_or_patterns, clippy::unreadable_literal, clippy::unsafe_derive_deserialize, clippy::unused_async, clippy::unused_self, clippy::used_underscore_binding, clippy::verbose_bit_mask, clippy::wildcard_imports, clippy::zero_sized_map_values"##, }, children: &[ - "clippy::await_holding_lock", - "clippy::await_holding_refcell_ref", + "clippy::bool_to_int_with_if", + "clippy::borrow_as_ptr", "clippy::case_sensitive_file_extension_comparisons", "clippy::cast_lossless", "clippy::cast_possible_truncation", @@ -7336,6 +13555,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::cloned_instead_of_copied", "clippy::copy_iterator", "clippy::default_trait_access", + "clippy::doc_link_with_quotes", "clippy::doc_markdown", "clippy::empty_enum", "clippy::enum_glob_use", @@ -7349,24 +13569,29 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::fn_params_excessive_bools", "clippy::from_iter_instead_of_collect", "clippy::if_not_else", + "clippy::ignored_unit_patterns", "clippy::implicit_clone", "clippy::implicit_hasher", - "clippy::implicit_saturating_sub", "clippy::inconsistent_struct_constructor", + "clippy::index_refutable_slice", "clippy::inefficient_to_string", "clippy::inline_always", + "clippy::into_iter_without_iter", "clippy::invalid_upcast_comparisons", "clippy::items_after_statements", "clippy::iter_not_returning_iterator", + "clippy::iter_without_into_iter", "clippy::large_digit_groups", + "clippy::large_futures", "clippy::large_stack_arrays", "clippy::large_types_passed_by_value", - "clippy::let_underscore_drop", - "clippy::let_unit_value", "clippy::linkedlist", "clippy::macro_use_imports", "clippy::manual_assert", + "clippy::manual_instant_elapsed", + "clippy::manual_let_else", "clippy::manual_ok_or", + "clippy::manual_string_new", "clippy::many_single_char_names", "clippy::map_unwrap_or", "clippy::match_bool", @@ -7375,7 +13600,9 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::match_wild_err_arm", "clippy::match_wildcard_for_single_variants", "clippy::maybe_infinite_iter", + "clippy::mismatching_type_param_order", "clippy::missing_errors_doc", + "clippy::missing_fields_in_debug", "clippy::missing_panics_doc", "clippy::module_name_repetitions", "clippy::must_use_candidate", @@ -7385,27 +13612,35 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::needless_continue", "clippy::needless_for_each", "clippy::needless_pass_by_value", + "clippy::needless_raw_string_hashes", "clippy::no_effect_underscore_binding", + "clippy::no_mangle_with_rust_abi", "clippy::option_option", "clippy::ptr_as_ptr", + "clippy::ptr_cast_constness", "clippy::range_minus_one", "clippy::range_plus_one", "clippy::redundant_closure_for_method_calls", "clippy::redundant_else", "clippy::ref_binding_to_reference", "clippy::ref_option_ref", + "clippy::return_self_not_must_use", "clippy::same_functions_in_if_condition", "clippy::semicolon_if_nothing_returned", + "clippy::should_panic_without_expect", "clippy::similar_names", "clippy::single_match_else", + "clippy::stable_sort_primitive", "clippy::string_add_assign", "clippy::struct_excessive_bools", "clippy::too_many_lines", - "clippy::trait_duplication_in_bounds", "clippy::transmute_ptr_to_ptr", "clippy::trivially_copy_pass_by_ref", - "clippy::type_repetition_in_bounds", + "clippy::unchecked_duration_subtraction", "clippy::unicode_not_nfc", + "clippy::uninlined_format_args", + "clippy::unnecessary_box_returns", + "clippy::unnecessary_join", "clippy::unnecessary_wraps", "clippy::unnested_or_patterns", "clippy::unreadable_literal", @@ -7421,29 +13656,33 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::perf", - description: r##"lint group for: clippy::box_collection, clippy::boxed_local, clippy::cmp_owned, clippy::expect_fun_call, clippy::extend_with_drain, clippy::format_in_format_args, clippy::iter_nth, clippy::large_const_arrays, clippy::large_enum_variant, clippy::manual_memcpy, clippy::manual_str_repeat, clippy::map_entry, clippy::mutex_atomic, clippy::needless_collect, clippy::or_fun_call, clippy::redundant_allocation, clippy::redundant_clone, clippy::single_char_pattern, clippy::slow_vector_initialization, clippy::stable_sort_primitive, clippy::to_string_in_format_args, clippy::unnecessary_to_owned, clippy::useless_vec, clippy::vec_init_then_push"##, + description: r##"lint group for: clippy::box_collection, clippy::box_default, clippy::boxed_local, clippy::cmp_owned, clippy::collapsible_str_replace, clippy::drain_collect, clippy::expect_fun_call, clippy::extend_with_drain, clippy::format_collect, clippy::format_in_format_args, clippy::iter_nth, clippy::iter_overeager_cloned, clippy::large_const_arrays, clippy::large_enum_variant, clippy::manual_memcpy, clippy::manual_retain, clippy::manual_str_repeat, clippy::manual_try_fold, clippy::map_entry, clippy::missing_spin_loop, clippy::redundant_allocation, clippy::result_large_err, clippy::single_char_pattern, clippy::slow_vector_initialization, clippy::to_string_in_format_args, clippy::unnecessary_to_owned, clippy::useless_vec, clippy::vec_init_then_push"##, }, children: &[ "clippy::box_collection", + "clippy::box_default", "clippy::boxed_local", "clippy::cmp_owned", + "clippy::collapsible_str_replace", + "clippy::drain_collect", "clippy::expect_fun_call", "clippy::extend_with_drain", + "clippy::format_collect", "clippy::format_in_format_args", "clippy::iter_nth", + "clippy::iter_overeager_cloned", "clippy::large_const_arrays", "clippy::large_enum_variant", "clippy::manual_memcpy", + "clippy::manual_retain", "clippy::manual_str_repeat", + "clippy::manual_try_fold", "clippy::map_entry", - "clippy::mutex_atomic", - "clippy::needless_collect", - "clippy::or_fun_call", + "clippy::missing_spin_loop", "clippy::redundant_allocation", - "clippy::redundant_clone", + "clippy::result_large_err", "clippy::single_char_pattern", "clippy::slow_vector_initialization", - "clippy::stable_sort_primitive", "clippy::to_string_in_format_args", "clippy::unnecessary_to_owned", "clippy::useless_vec", @@ -7453,17 +13692,30 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::restriction", - description: r##"lint group for: clippy::as_conversions, clippy::clone_on_ref_ptr, clippy::create_dir, clippy::dbg_macro, clippy::decimal_literal_representation, clippy::default_numeric_fallback, clippy::disallowed_script_idents, clippy::else_if_without_else, clippy::exhaustive_enums, clippy::exhaustive_structs, clippy::exit, clippy::expect_used, clippy::filetype_is_file, clippy::float_arithmetic, clippy::float_cmp_const, clippy::fn_to_numeric_cast_any, clippy::get_unwrap, clippy::if_then_some_else_none, clippy::implicit_return, clippy::indexing_slicing, clippy::inline_asm_x86_att_syntax, clippy::inline_asm_x86_intel_syntax, clippy::integer_arithmetic, clippy::integer_division, clippy::let_underscore_must_use, clippy::lossy_float_literal, clippy::map_err_ignore, clippy::mem_forget, clippy::missing_docs_in_private_items, clippy::missing_enforced_import_renames, clippy::missing_inline_in_public_items, clippy::mod_module_files, clippy::modulo_arithmetic, clippy::multiple_inherent_impl, clippy::non_ascii_literal, clippy::panic, clippy::panic_in_result_fn, clippy::pattern_type_mismatch, clippy::print_stderr, clippy::print_stdout, clippy::rc_buffer, clippy::rc_mutex, clippy::rest_pat_in_fully_bound_structs, clippy::same_name_method, clippy::self_named_module_files, clippy::separated_literal_suffix, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::str_to_string, clippy::string_add, clippy::string_slice, clippy::string_to_string, clippy::todo, clippy::undocumented_unsafe_blocks, clippy::unimplemented, clippy::unnecessary_self_imports, clippy::unneeded_field_pattern, clippy::unreachable, clippy::unseparated_literal_suffix, clippy::unwrap_in_result, clippy::unwrap_used, clippy::use_debug, clippy::verbose_file_reads, clippy::wildcard_enum_match_arm"##, + description: r##"lint group for: clippy::absolute_paths, clippy::alloc_instead_of_core, clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::arithmetic_side_effects, clippy::as_conversions, clippy::as_underscore, clippy::assertions_on_result_states, clippy::big_endian_bytes, clippy::clone_on_ref_ptr, clippy::create_dir, clippy::dbg_macro, clippy::decimal_literal_representation, clippy::default_numeric_fallback, clippy::default_union_representation, clippy::deref_by_slicing, clippy::disallowed_script_idents, clippy::else_if_without_else, clippy::empty_drop, clippy::empty_structs_with_brackets, clippy::error_impl_error, clippy::exhaustive_enums, clippy::exhaustive_structs, clippy::exit, clippy::expect_used, clippy::filetype_is_file, clippy::float_arithmetic, clippy::float_cmp_const, clippy::fn_to_numeric_cast_any, clippy::format_push_string, clippy::get_unwrap, clippy::host_endian_bytes, clippy::if_then_some_else_none, clippy::impl_trait_in_params, clippy::implicit_return, clippy::indexing_slicing, clippy::inline_asm_x86_att_syntax, clippy::inline_asm_x86_intel_syntax, clippy::integer_division, clippy::large_include_file, clippy::let_underscore_must_use, clippy::let_underscore_untyped, clippy::little_endian_bytes, clippy::lossy_float_literal, clippy::map_err_ignore, clippy::mem_forget, clippy::min_ident_chars, clippy::missing_assert_message, clippy::missing_asserts_for_indexing, clippy::missing_docs_in_private_items, clippy::missing_enforced_import_renames, clippy::missing_inline_in_public_items, clippy::missing_trait_methods, clippy::mixed_read_write_in_expression, clippy::mod_module_files, clippy::modulo_arithmetic, clippy::multiple_inherent_impl, clippy::multiple_unsafe_ops_per_block, clippy::mutex_atomic, clippy::needless_raw_strings, clippy::non_ascii_literal, clippy::panic, clippy::panic_in_result_fn, clippy::partial_pub_fields, clippy::pattern_type_mismatch, clippy::print_stderr, clippy::print_stdout, clippy::pub_use, clippy::pub_with_shorthand, clippy::pub_without_shorthand, clippy::question_mark_used, clippy::rc_buffer, clippy::rc_mutex, clippy::redundant_type_annotations, clippy::ref_patterns, clippy::rest_pat_in_fully_bound_structs, clippy::same_name_method, clippy::self_named_module_files, clippy::semicolon_inside_block, clippy::semicolon_outside_block, clippy::separated_literal_suffix, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::single_call_fn, clippy::single_char_lifetime_names, clippy::std_instead_of_alloc, clippy::std_instead_of_core, clippy::str_to_string, clippy::string_add, clippy::string_lit_chars_any, clippy::string_slice, clippy::string_to_string, clippy::suspicious_xor_used_as_pow, clippy::tests_outside_test_module, clippy::todo, clippy::try_err, clippy::undocumented_unsafe_blocks, clippy::unimplemented, clippy::unnecessary_safety_comment, clippy::unnecessary_safety_doc, clippy::unnecessary_self_imports, clippy::unneeded_field_pattern, clippy::unreachable, clippy::unseparated_literal_suffix, clippy::unwrap_in_result, clippy::unwrap_used, clippy::use_debug, clippy::verbose_file_reads, clippy::wildcard_enum_match_arm"##, }, children: &[ + "clippy::absolute_paths", + "clippy::alloc_instead_of_core", + "clippy::allow_attributes", + "clippy::allow_attributes_without_reason", + "clippy::arithmetic_side_effects", "clippy::as_conversions", + "clippy::as_underscore", + "clippy::assertions_on_result_states", + "clippy::big_endian_bytes", "clippy::clone_on_ref_ptr", "clippy::create_dir", "clippy::dbg_macro", "clippy::decimal_literal_representation", "clippy::default_numeric_fallback", + "clippy::default_union_representation", + "clippy::deref_by_slicing", "clippy::disallowed_script_idents", "clippy::else_if_without_else", + "clippy::empty_drop", + "clippy::empty_structs_with_brackets", + "clippy::error_impl_error", "clippy::exhaustive_enums", "clippy::exhaustive_structs", "clippy::exit", @@ -7472,46 +13724,78 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::float_arithmetic", "clippy::float_cmp_const", "clippy::fn_to_numeric_cast_any", + "clippy::format_push_string", "clippy::get_unwrap", + "clippy::host_endian_bytes", "clippy::if_then_some_else_none", + "clippy::impl_trait_in_params", "clippy::implicit_return", "clippy::indexing_slicing", "clippy::inline_asm_x86_att_syntax", "clippy::inline_asm_x86_intel_syntax", - "clippy::integer_arithmetic", "clippy::integer_division", + "clippy::large_include_file", "clippy::let_underscore_must_use", + "clippy::let_underscore_untyped", + "clippy::little_endian_bytes", "clippy::lossy_float_literal", "clippy::map_err_ignore", "clippy::mem_forget", + "clippy::min_ident_chars", + "clippy::missing_assert_message", + "clippy::missing_asserts_for_indexing", "clippy::missing_docs_in_private_items", "clippy::missing_enforced_import_renames", "clippy::missing_inline_in_public_items", + "clippy::missing_trait_methods", + "clippy::mixed_read_write_in_expression", "clippy::mod_module_files", "clippy::modulo_arithmetic", "clippy::multiple_inherent_impl", + "clippy::multiple_unsafe_ops_per_block", + "clippy::mutex_atomic", + "clippy::needless_raw_strings", "clippy::non_ascii_literal", "clippy::panic", "clippy::panic_in_result_fn", + "clippy::partial_pub_fields", "clippy::pattern_type_mismatch", "clippy::print_stderr", "clippy::print_stdout", + "clippy::pub_use", + "clippy::pub_with_shorthand", + "clippy::pub_without_shorthand", + "clippy::question_mark_used", "clippy::rc_buffer", "clippy::rc_mutex", + "clippy::redundant_type_annotations", + "clippy::ref_patterns", "clippy::rest_pat_in_fully_bound_structs", "clippy::same_name_method", "clippy::self_named_module_files", + "clippy::semicolon_inside_block", + "clippy::semicolon_outside_block", "clippy::separated_literal_suffix", "clippy::shadow_reuse", "clippy::shadow_same", "clippy::shadow_unrelated", + "clippy::single_call_fn", + "clippy::single_char_lifetime_names", + "clippy::std_instead_of_alloc", + "clippy::std_instead_of_core", "clippy::str_to_string", "clippy::string_add", + "clippy::string_lit_chars_any", "clippy::string_slice", "clippy::string_to_string", + "clippy::suspicious_xor_used_as_pow", + "clippy::tests_outside_test_module", "clippy::todo", + "clippy::try_err", "clippy::undocumented_unsafe_blocks", "clippy::unimplemented", + "clippy::unnecessary_safety_comment", + "clippy::unnecessary_safety_doc", "clippy::unnecessary_self_imports", "clippy::unneeded_field_pattern", "clippy::unreachable", @@ -7526,12 +13810,11 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::style", - description: r##"lint group for: clippy::assertions_on_constants, clippy::assign_op_pattern, clippy::blacklisted_name, clippy::blocks_in_if_conditions, clippy::bool_assert_comparison, clippy::borrow_interior_mutable_const, clippy::builtin_type_shadow, clippy::bytes_nth, clippy::chars_last_cmp, clippy::chars_next_cmp, clippy::cmp_null, clippy::collapsible_else_if, clippy::collapsible_if, clippy::collapsible_match, clippy::comparison_chain, clippy::comparison_to_empty, clippy::declare_interior_mutable_const, clippy::double_must_use, clippy::double_neg, clippy::duplicate_underscore_argument, clippy::enum_variant_names, clippy::excessive_precision, clippy::field_reassign_with_default, clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation, clippy::for_kv_map, clippy::from_over_into, clippy::from_str_radix_10, clippy::inconsistent_digit_grouping, clippy::infallible_destructuring_match, clippy::inherent_to_string, clippy::into_iter_on_ref, clippy::iter_cloned_collect, clippy::iter_next_slice, clippy::iter_nth_zero, clippy::iter_skip_next, clippy::just_underscores_and_digits, clippy::len_without_is_empty, clippy::len_zero, clippy::let_and_return, clippy::main_recursion, clippy::manual_async_fn, clippy::manual_map, clippy::manual_non_exhaustive, clippy::manual_range_contains, clippy::manual_saturating_arithmetic, clippy::map_clone, clippy::map_collect_result_unit, clippy::match_like_matches_macro, clippy::match_overlapping_arm, clippy::match_ref_pats, clippy::match_result_ok, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default, clippy::missing_safety_doc, clippy::mixed_case_hex_literals, clippy::module_inception, clippy::must_use_unit, clippy::mut_mutex_lock, clippy::needless_borrow, clippy::needless_doctest_main, clippy::needless_late_init, clippy::needless_range_loop, clippy::needless_return, clippy::neg_multiply, clippy::new_ret_no_self, clippy::new_without_default, clippy::ok_expect, clippy::op_ref, clippy::option_map_or_none, clippy::print_literal, clippy::print_with_newline, clippy::println_empty_string, clippy::ptr_arg, clippy::ptr_eq, clippy::question_mark, clippy::redundant_closure, clippy::redundant_field_names, clippy::redundant_pattern, clippy::redundant_pattern_matching, clippy::redundant_static_lifetimes, clippy::result_map_or_into_option, clippy::result_unit_err, clippy::same_item_push, clippy::self_named_constructors, clippy::should_implement_trait, clippy::single_char_add_str, clippy::single_component_path_imports, clippy::single_match, clippy::string_extend_chars, clippy::tabs_in_doc_comments, clippy::to_digit_is_some, clippy::toplevel_ref_arg, clippy::try_err, clippy::unnecessary_fold, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_mut_passed, clippy::unsafe_removed_from_name, clippy::unused_unit, clippy::unusual_byte_groupings, clippy::unwrap_or_else_default, clippy::upper_case_acronyms, clippy::while_let_on_iterator, clippy::write_literal, clippy::write_with_newline, clippy::writeln_empty_string, clippy::wrong_self_convention, clippy::zero_ptr"##, + description: r##"lint group for: clippy::assertions_on_constants, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::bool_assert_comparison, clippy::borrow_interior_mutable_const, clippy::builtin_type_shadow, clippy::bytes_nth, clippy::chars_last_cmp, clippy::chars_next_cmp, clippy::cmp_null, clippy::collapsible_else_if, clippy::collapsible_if, clippy::collapsible_match, clippy::comparison_chain, clippy::comparison_to_empty, clippy::declare_interior_mutable_const, clippy::default_instead_of_iter_empty, clippy::disallowed_macros, clippy::disallowed_methods, clippy::disallowed_names, clippy::disallowed_types, clippy::double_must_use, clippy::double_neg, clippy::duplicate_underscore_argument, clippy::enum_variant_names, clippy::err_expect, clippy::excessive_precision, clippy::field_reassign_with_default, clippy::filter_map_bool_then, clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation, clippy::for_kv_map, clippy::from_over_into, clippy::from_str_radix_10, clippy::get_first, clippy::implicit_saturating_add, clippy::implicit_saturating_sub, clippy::inconsistent_digit_grouping, clippy::infallible_destructuring_match, clippy::inherent_to_string, clippy::init_numbered_fields, clippy::into_iter_on_ref, clippy::is_digit_ascii_radix, clippy::items_after_test_module, clippy::iter_cloned_collect, clippy::iter_next_slice, clippy::iter_nth_zero, clippy::iter_skip_next, clippy::just_underscores_and_digits, clippy::len_without_is_empty, clippy::len_zero, clippy::let_and_return, clippy::let_unit_value, clippy::main_recursion, clippy::manual_async_fn, clippy::manual_bits, clippy::manual_is_ascii_check, clippy::manual_is_finite, clippy::manual_is_infinite, clippy::manual_map, clippy::manual_next_back, clippy::manual_non_exhaustive, clippy::manual_range_contains, clippy::manual_saturating_arithmetic, clippy::manual_while_let_some, clippy::map_clone, clippy::map_collect_result_unit, clippy::match_like_matches_macro, clippy::match_overlapping_arm, clippy::match_ref_pats, clippy::match_result_ok, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default, clippy::missing_safety_doc, clippy::mixed_case_hex_literals, clippy::module_inception, clippy::must_use_unit, clippy::mut_mutex_lock, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::needless_doctest_main, clippy::needless_else, clippy::needless_late_init, clippy::needless_parens_on_range_literals, clippy::needless_pub_self, clippy::needless_range_loop, clippy::needless_return, clippy::needless_return_with_question_mark, clippy::neg_multiply, clippy::new_ret_no_self, clippy::new_without_default, clippy::non_minimal_cfg, clippy::obfuscated_if_else, clippy::ok_expect, clippy::op_ref, clippy::option_map_or_none, clippy::partialeq_to_none, clippy::print_literal, clippy::print_with_newline, clippy::println_empty_string, clippy::ptr_arg, clippy::ptr_eq, clippy::question_mark, clippy::redundant_closure, clippy::redundant_field_names, clippy::redundant_pattern, clippy::redundant_pattern_matching, clippy::redundant_static_lifetimes, clippy::result_map_or_into_option, clippy::result_unit_err, clippy::same_item_push, clippy::self_named_constructors, clippy::should_implement_trait, clippy::single_char_add_str, clippy::single_component_path_imports, clippy::single_match, clippy::string_extend_chars, clippy::tabs_in_doc_comments, clippy::to_digit_is_some, clippy::toplevel_ref_arg, clippy::trim_split_whitespace, clippy::unnecessary_fold, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_mut_passed, clippy::unnecessary_owned_empty_strings, clippy::unsafe_removed_from_name, clippy::unused_unit, clippy::unusual_byte_groupings, clippy::unwrap_or_default, clippy::upper_case_acronyms, clippy::while_let_on_iterator, clippy::write_literal, clippy::write_with_newline, clippy::writeln_empty_string, clippy::wrong_self_convention, clippy::zero_ptr"##, }, children: &[ "clippy::assertions_on_constants", "clippy::assign_op_pattern", - "clippy::blacklisted_name", "clippy::blocks_in_if_conditions", "clippy::bool_assert_comparison", "clippy::borrow_interior_mutable_const", @@ -7546,21 +13829,34 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::comparison_chain", "clippy::comparison_to_empty", "clippy::declare_interior_mutable_const", + "clippy::default_instead_of_iter_empty", + "clippy::disallowed_macros", + "clippy::disallowed_methods", + "clippy::disallowed_names", + "clippy::disallowed_types", "clippy::double_must_use", "clippy::double_neg", "clippy::duplicate_underscore_argument", "clippy::enum_variant_names", + "clippy::err_expect", "clippy::excessive_precision", "clippy::field_reassign_with_default", + "clippy::filter_map_bool_then", "clippy::fn_to_numeric_cast", "clippy::fn_to_numeric_cast_with_truncation", "clippy::for_kv_map", "clippy::from_over_into", "clippy::from_str_radix_10", + "clippy::get_first", + "clippy::implicit_saturating_add", + "clippy::implicit_saturating_sub", "clippy::inconsistent_digit_grouping", "clippy::infallible_destructuring_match", "clippy::inherent_to_string", + "clippy::init_numbered_fields", "clippy::into_iter_on_ref", + "clippy::is_digit_ascii_radix", + "clippy::items_after_test_module", "clippy::iter_cloned_collect", "clippy::iter_next_slice", "clippy::iter_nth_zero", @@ -7569,12 +13865,19 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::len_without_is_empty", "clippy::len_zero", "clippy::let_and_return", + "clippy::let_unit_value", "clippy::main_recursion", "clippy::manual_async_fn", + "clippy::manual_bits", + "clippy::manual_is_ascii_check", + "clippy::manual_is_finite", + "clippy::manual_is_infinite", "clippy::manual_map", + "clippy::manual_next_back", "clippy::manual_non_exhaustive", "clippy::manual_range_contains", "clippy::manual_saturating_arithmetic", + "clippy::manual_while_let_some", "clippy::map_clone", "clippy::map_collect_result_unit", "clippy::match_like_matches_macro", @@ -7589,16 +13892,24 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::must_use_unit", "clippy::mut_mutex_lock", "clippy::needless_borrow", + "clippy::needless_borrows_for_generic_args", "clippy::needless_doctest_main", + "clippy::needless_else", "clippy::needless_late_init", + "clippy::needless_parens_on_range_literals", + "clippy::needless_pub_self", "clippy::needless_range_loop", "clippy::needless_return", + "clippy::needless_return_with_question_mark", "clippy::neg_multiply", "clippy::new_ret_no_self", "clippy::new_without_default", + "clippy::non_minimal_cfg", + "clippy::obfuscated_if_else", "clippy::ok_expect", "clippy::op_ref", "clippy::option_map_or_none", + "clippy::partialeq_to_none", "clippy::print_literal", "clippy::print_with_newline", "clippy::println_empty_string", @@ -7622,14 +13933,15 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::tabs_in_doc_comments", "clippy::to_digit_is_some", "clippy::toplevel_ref_arg", - "clippy::try_err", + "clippy::trim_split_whitespace", "clippy::unnecessary_fold", "clippy::unnecessary_lazy_evaluations", "clippy::unnecessary_mut_passed", + "clippy::unnecessary_owned_empty_strings", "clippy::unsafe_removed_from_name", "clippy::unused_unit", "clippy::unusual_byte_groupings", - "clippy::unwrap_or_else_default", + "clippy::unwrap_or_default", "clippy::upper_case_acronyms", "clippy::while_let_on_iterator", "clippy::write_literal", @@ -7642,25 +13954,58 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::suspicious", - description: r##"lint group for: clippy::blanket_clippy_restriction_lints, clippy::empty_loop, clippy::eval_order_dependence, clippy::float_equality_without_abs, clippy::for_loops_over_fallibles, clippy::misrefactored_assign_op, clippy::mut_range_bound, clippy::mutable_key_type, clippy::octal_escapes, clippy::return_self_not_must_use, clippy::suspicious_arithmetic_impl, clippy::suspicious_assignment_formatting, clippy::suspicious_else_formatting, clippy::suspicious_map, clippy::suspicious_op_assign_impl, clippy::suspicious_unary_op_formatting"##, + description: r##"lint group for: clippy::almost_complete_range, clippy::arc_with_non_send_sync, clippy::await_holding_invalid_type, clippy::await_holding_lock, clippy::await_holding_refcell_ref, clippy::blanket_clippy_restriction_lints, clippy::cast_abs_to_unsigned, clippy::cast_enum_constructor, clippy::cast_enum_truncation, clippy::cast_nan_to_int, clippy::cast_slice_from_raw_parts, clippy::crate_in_macro_def, clippy::drop_non_drop, clippy::duplicate_mod, clippy::empty_loop, clippy::float_equality_without_abs, clippy::forget_non_drop, clippy::four_forward_slashes, clippy::from_raw_with_void_ptr, clippy::iter_out_of_bounds, clippy::let_underscore_future, clippy::lines_filter_map_ok, clippy::maybe_misused_cfg, clippy::misnamed_getters, clippy::misrefactored_assign_op, clippy::multi_assignments, clippy::mut_range_bound, clippy::mutable_key_type, clippy::no_effect_replace, clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, clippy::octal_escapes, clippy::path_ends_with_ext, clippy::permissions_set_readonly_false, clippy::print_in_format_impl, clippy::rc_clone_in_vec_init, clippy::single_range_in_vec_init, clippy::size_of_ref, clippy::suspicious_arithmetic_impl, clippy::suspicious_assignment_formatting, clippy::suspicious_command_arg_space, clippy::suspicious_doc_comments, clippy::suspicious_else_formatting, clippy::suspicious_map, clippy::suspicious_op_assign_impl, clippy::suspicious_to_owned, clippy::suspicious_unary_op_formatting, clippy::swap_ptr_to_ref, clippy::type_id_on_box"##, }, children: &[ + "clippy::almost_complete_range", + "clippy::arc_with_non_send_sync", + "clippy::await_holding_invalid_type", + "clippy::await_holding_lock", + "clippy::await_holding_refcell_ref", "clippy::blanket_clippy_restriction_lints", + "clippy::cast_abs_to_unsigned", + "clippy::cast_enum_constructor", + "clippy::cast_enum_truncation", + "clippy::cast_nan_to_int", + "clippy::cast_slice_from_raw_parts", + "clippy::crate_in_macro_def", + "clippy::drop_non_drop", + "clippy::duplicate_mod", "clippy::empty_loop", - "clippy::eval_order_dependence", "clippy::float_equality_without_abs", - "clippy::for_loops_over_fallibles", + "clippy::forget_non_drop", + "clippy::four_forward_slashes", + "clippy::from_raw_with_void_ptr", + "clippy::iter_out_of_bounds", + "clippy::let_underscore_future", + "clippy::lines_filter_map_ok", + "clippy::maybe_misused_cfg", + "clippy::misnamed_getters", "clippy::misrefactored_assign_op", + "clippy::multi_assignments", "clippy::mut_range_bound", "clippy::mutable_key_type", + "clippy::no_effect_replace", + "clippy::non_canonical_clone_impl", + "clippy::non_canonical_partial_ord_impl", "clippy::octal_escapes", - "clippy::return_self_not_must_use", + "clippy::path_ends_with_ext", + "clippy::permissions_set_readonly_false", + "clippy::print_in_format_impl", + "clippy::rc_clone_in_vec_init", + "clippy::single_range_in_vec_init", + "clippy::size_of_ref", "clippy::suspicious_arithmetic_impl", "clippy::suspicious_assignment_formatting", + "clippy::suspicious_command_arg_space", + "clippy::suspicious_doc_comments", "clippy::suspicious_else_formatting", "clippy::suspicious_map", "clippy::suspicious_op_assign_impl", + "clippy::suspicious_to_owned", "clippy::suspicious_unary_op_formatting", + "clippy::swap_ptr_to_ref", + "clippy::type_id_on_box", ], }, ]; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index e475c5cd66b..04263d15d0a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -68,22 +68,29 @@ pub struct FirstSegmentUnresolved { pub enum NameToImport { /// Requires items with names that exactly match the given string, bool indicates case-sensitivity. Exact(String, bool), - /// Requires items with names that case-insensitively contain all letters from the string, + /// Requires items with names that match the given string by prefix, bool indicates case-sensitivity. + Prefix(String, bool), + /// Requires items with names contain all letters from the string, /// in the same order, but not necessary adjacent. - Fuzzy(String), + Fuzzy(String, bool), } impl NameToImport { pub fn exact_case_sensitive(s: String) -> NameToImport { NameToImport::Exact(s, true) } -} -impl NameToImport { + pub fn fuzzy(s: String) -> NameToImport { + // unless all chars are lowercase, we do a case sensitive search + let case_sensitive = s.chars().any(|c| c.is_uppercase()); + NameToImport::Fuzzy(s, case_sensitive) + } + pub fn text(&self) -> &str { match self { - NameToImport::Exact(text, _) => text.as_str(), - NameToImport::Fuzzy(text) => text.as_str(), + NameToImport::Prefix(text, _) + | NameToImport::Exact(text, _) + | NameToImport::Fuzzy(text, _) => text.as_str(), } } } @@ -165,7 +172,7 @@ impl ImportAssets { Some(Self { import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { receiver_ty, - assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name), + assoc_item_name: NameToImport::fuzzy(fuzzy_method_name), }), module_with_candidate: module_with_method_call, candidate_node, @@ -213,9 +220,10 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec<LocatedImport> { let _p = profile::span("import_assets::search_for_imports"); - self.search_for(sema, Some(prefix_kind), prefer_no_std) + self.search_for(sema, Some(prefix_kind), prefer_no_std, prefer_prelude) } /// This may return non-absolute paths if a part of the returned path is already imported into scope. @@ -223,17 +231,36 @@ impl ImportAssets { &self, sema: &Semantics<'_, RootDatabase>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec<LocatedImport> { let _p = profile::span("import_assets::search_for_relative_paths"); - self.search_for(sema, None, prefer_no_std) + self.search_for(sema, None, prefer_no_std, prefer_prelude) + } + + /// Requires imports to by prefix instead of fuzzily. + pub fn path_fuzzy_name_to_prefix(&mut self) { + if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) = + &mut self.import_candidate + { + let (name, case_sensitive) = match to_import { + NameToImport::Fuzzy(name, case_sensitive) => { + (std::mem::take(name), *case_sensitive) + } + _ => return, + }; + *to_import = NameToImport::Prefix(name, case_sensitive); + } } - pub fn path_fuzzy_name_to_exact(&mut self, case_sensitive: bool) { + /// Requires imports to match exactly instead of fuzzily. + pub fn path_fuzzy_name_to_exact(&mut self) { if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) = &mut self.import_candidate { - let name = match to_import { - NameToImport::Fuzzy(name) => std::mem::take(name), + let (name, case_sensitive) = match to_import { + NameToImport::Fuzzy(name, case_sensitive) => { + (std::mem::take(name), *case_sensitive) + } _ => return, }; *to_import = NameToImport::Exact(name, case_sensitive); @@ -245,6 +272,7 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec<LocatedImport> { let _p = profile::span("import_assets::search_for"); @@ -256,6 +284,7 @@ impl ImportAssets { &self.module_with_candidate, prefixed, prefer_no_std, + prefer_prelude, ) }; @@ -569,11 +598,18 @@ fn get_mod_path( module_with_candidate: &Module, prefixed: Option<PrefixKind>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option<ModPath> { if let Some(prefix_kind) = prefixed { - module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind, prefer_no_std) + module_with_candidate.find_use_path_prefixed( + db, + item_to_search, + prefix_kind, + prefer_no_std, + prefer_prelude, + ) } else { - module_with_candidate.find_use_path(db, item_to_search, prefer_no_std) + module_with_candidate.find_use_path(db, item_to_search, prefer_no_std, prefer_prelude) } } @@ -623,7 +659,7 @@ impl ImportCandidate { fuzzy_name: String, sema: &Semantics<'_, RootDatabase>, ) -> Option<Self> { - path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) + path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name)) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 9be1d366349..a0cfd3836dd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -9,7 +9,7 @@ use syntax::{ algo, ast::{ self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, - PathSegmentKind, + PathSegmentKind, UseTree, }, ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode, }; @@ -157,6 +157,29 @@ impl ImportScope { /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { + insert_use_with_alias_option(scope, path, cfg, None); +} + +pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { + let text: &str = "use foo as _"; + let parse = syntax::SourceFile::parse(text); + let node = parse + .tree() + .syntax() + .descendants() + .find_map(UseTree::cast) + .expect("Failed to make ast node `Rename`"); + let alias = node.rename(); + + insert_use_with_alias_option(scope, path, cfg, alias); +} + +fn insert_use_with_alias_option( + scope: &ImportScope, + path: ast::Path, + cfg: &InsertUseConfig, + alias: Option<ast::Rename>, +) { let _p = profile::span("insert_use"); let mut mb = match cfg.granularity { ImportGranularity::Crate => Some(MergeBehavior::Crate), @@ -176,7 +199,8 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { } let use_item = - make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); + make::use_(None, make::use_tree(path.clone(), None, alias, false)).clone_for_update(); + // merge into existing imports if possible if let Some(mb) = mb { let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index b92e367f7e1..01d2f1970c3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -993,6 +993,46 @@ use foo::bar::qux; ); } +#[test] +fn insert_with_renamed_import_simple_use() { + check_with_config( + "use self::foo::Foo", + r#" +use self::foo::Foo as _; +"#, + r#" +use self::foo::Foo; +"#, + &InsertUseConfig { + granularity: ImportGranularity::Crate, + prefix_kind: hir::PrefixKind::BySelf, + enforce_granularity: true, + group: true, + skip_glob_imports: true, + }, + ); +} + +#[test] +fn insert_with_renamed_import_complex_use() { + check_with_config( + "use self::foo::Foo;", + r#" +use self::foo::{self, Foo as _, Bar}; +"#, + r#" +use self::foo::{self, Foo, Bar}; +"#, + &InsertUseConfig { + granularity: ImportGranularity::Crate, + prefix_kind: hir::PrefixKind::BySelf, + enforce_granularity: true, + group: true, + skip_glob_imports: true, + }, + ); +} + fn check_with_config( path: &str, ra_fixture_before: &str, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 27b6321f3a7..ff84e9ffaee 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -78,6 +78,10 @@ fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehav { lhs.split_prefix(&lhs_prefix); rhs.split_prefix(&rhs_prefix); + } else { + ted::replace(lhs.syntax(), rhs.syntax()); + // we can safely return here, in this case `recursive_merge` doesn't do anything + return Some(()); } recursive_merge(lhs, rhs, merge) } @@ -123,6 +127,13 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) // so they need to be handled explicitly .or_else(|| tree.star_token().map(|_| false)) }; + + if lhs_t.rename().and_then(|x| x.underscore_token()).is_some() { + ted::replace(lhs_t.syntax(), rhs_t.syntax()); + *lhs_t = rhs_t; + continue; + } + match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) { (Some(true), None) => continue, (None, Some(true)) => { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs index 3f7a3ec2d0f..67ed44f08b7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs @@ -31,26 +31,34 @@ pub fn items_with_name<'a>( ) }); + let prefix = matches!(name, NameToImport::Prefix(..)); let (mut local_query, mut external_query) = match name { - NameToImport::Exact(exact_name, case_sensitive) => { + NameToImport::Prefix(exact_name, case_sensitive) + | NameToImport::Exact(exact_name, case_sensitive) => { let mut local_query = symbol_index::Query::new(exact_name.clone()); - local_query.exact(); - - let external_query = import_map::Query::new(exact_name); - - ( - local_query, - if case_sensitive { external_query.case_sensitive() } else { external_query }, - ) + let mut external_query = import_map::Query::new(exact_name); + if prefix { + local_query.prefix(); + external_query = external_query.prefix(); + } else { + local_query.exact(); + external_query = external_query.exact(); + } + if case_sensitive { + local_query.case_sensitive(); + external_query = external_query.case_sensitive(); + } + (local_query, external_query) } - NameToImport::Fuzzy(fuzzy_search_string) => { + NameToImport::Fuzzy(fuzzy_search_string, case_sensitive) => { let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone()); + local_query.fuzzy(); let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) .fuzzy() .assoc_search_mode(assoc_item_search); - if fuzzy_search_string.to_lowercase() != fuzzy_search_string { + if case_sensitive { local_query.case_sensitive(); external_query = external_query.case_sensitive(); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index fb75b5b4584..fa9339f30f2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -277,6 +277,7 @@ impl Ctx<'_> { self.source_scope.db.upcast(), hir::ModuleDef::Trait(trait_ref), false, + true, )?; match make::ty_path(mod_path_to_ast(&found_path)) { ast::Type::PathType(path_ty) => Some(path_ty), @@ -311,8 +312,12 @@ impl Ctx<'_> { } } - let found_path = - self.target_module.find_use_path(self.source_scope.db.upcast(), def, false)?; + let found_path = self.target_module.find_use_path( + self.source_scope.db.upcast(), + def, + false, + true, + )?; let res = mod_path_to_ast(&found_path).clone_for_update(); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) { if let Some(segment) = res.segment() { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 9c4f0ac8c9f..22438a203bd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -584,7 +584,7 @@ impl<'a> FindUsages<'a> { ) -> bool { match NameRefClass::classify(self.sema, name_ref) { Some(NameRefClass::Definition(Definition::SelfType(impl_))) - if impl_.self_ty(self.sema.db) == *self_ty => + if impl_.self_ty(self.sema.db).as_adt() == self_ty.as_adt() => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 39763479c65..c7188f1f794 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -140,10 +140,10 @@ impl SnippetEdit { .with_position() .map(|pos| { let (snippet, index) = match pos { - itertools::Position::First(it) | itertools::Position::Middle(it) => it, + (itertools::Position::First, it) | (itertools::Position::Middle, it) => it, // last/only snippet gets index 0 - itertools::Position::Last((snippet, _)) - | itertools::Position::Only((snippet, _)) => (snippet, 0), + (itertools::Position::Last, (snippet, _)) + | (itertools::Position::Only, (snippet, _)) => (snippet, 0), }; let range = match snippet { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index f699f999baf..3e89159c2c6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -43,13 +43,20 @@ use triomphe::Arc; use crate::RootDatabase; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum SearchMode { + Fuzzy, + Exact, + Prefix, +} + #[derive(Debug)] pub struct Query { query: String, lowercased: String, only_types: bool, libs: bool, - exact: bool, + mode: SearchMode, case_sensitive: bool, limit: usize, } @@ -62,7 +69,7 @@ impl Query { lowercased, only_types: false, libs: false, - exact: false, + mode: SearchMode::Fuzzy, case_sensitive: false, limit: usize::max_value(), } @@ -76,8 +83,16 @@ impl Query { self.libs = true; } + pub fn fuzzy(&mut self) { + self.mode = SearchMode::Fuzzy; + } + pub fn exact(&mut self) { - self.exact = true; + self.mode = SearchMode::Exact; + } + + pub fn prefix(&mut self) { + self.mode = SearchMode::Prefix; } pub fn case_sensitive(&mut self) { @@ -329,13 +344,23 @@ impl Query { { continue; } - if self.exact { - if symbol.name != self.query { - continue; + let skip = match self.mode { + SearchMode::Fuzzy => { + self.case_sensitive + && self.query.chars().any(|c| !symbol.name.contains(c)) } - } else if self.case_sensitive - && self.query.chars().any(|c| !symbol.name.contains(c)) - { + SearchMode::Exact => symbol.name != self.query, + SearchMode::Prefix if self.case_sensitive => { + !symbol.name.starts_with(&self.query) + } + SearchMode::Prefix => symbol + .name + .chars() + .zip(self.lowercased.chars()) + .all(|(n, q)| n.to_lowercase().next() == Some(q)), + }; + + if skip { continue; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/tests/sourcegen_lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/tests/sourcegen_lints.rs index c7d5f3613d4..c8cf87d3c20 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/tests/sourcegen_lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/tests/sourcegen_lints.rs @@ -1,4 +1,5 @@ -//! Generates descriptors structure for unstable feature from Unstable Book +//! Generates descriptor structures for unstable features from the unstable book +//! and lints from rustc, rustdoc, and clippy. use std::{borrow::Cow, fs, path::Path}; use itertools::Itertools; @@ -6,6 +7,8 @@ use stdx::format_to; use test_utils::project_root; use xshell::{cmd, Shell}; +const DESTINATION: &str = "crates/ide-db/src/generated/lints.rs"; + /// This clones rustc repo, and so is not worth to keep up-to-date. We update /// manually by un-ignoring the test from time to time. #[test] @@ -14,11 +17,21 @@ fn sourcegen_lint_completions() { let sh = &Shell::new().unwrap(); let rust_repo = project_root().join("./target/rust"); - if !rust_repo.exists() { + if rust_repo.exists() { + cmd!(sh, "git -C {rust_repo} pull --rebase").run().unwrap(); + } else { cmd!(sh, "git clone --depth=1 https://github.com/rust-lang/rust {rust_repo}") .run() .unwrap(); } + // need submodules for Cargo to parse the workspace correctly + cmd!( + sh, + "git -C {rust_repo} submodule update --init --recursive --depth=1 -- + compiler library src/tools" + ) + .run() + .unwrap(); let mut contents = String::from( r" @@ -27,17 +40,28 @@ pub struct Lint { pub label: &'static str, pub description: &'static str, } + pub struct LintGroup { pub lint: Lint, pub children: &'static [&'static str], } + ", ); generate_lint_descriptor(sh, &mut contents); contents.push('\n'); - generate_feature_descriptor(&mut contents, &rust_repo.join("src/doc/unstable-book/src")); + let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + let unstable_book = project_root().join("./target/unstable-book-gen"); + cmd!( + sh, + "{cargo} run --manifest-path {rust_repo}/src/tools/unstable-book-gen/Cargo.toml -- + {rust_repo}/library {rust_repo}/compiler {rust_repo}/src {unstable_book}" + ) + .run() + .unwrap(); + generate_feature_descriptor(&mut contents, &unstable_book.join("src")); contents.push('\n'); let lints_json = project_root().join("./target/clippy_lints.json"); @@ -51,41 +75,60 @@ pub struct LintGroup { let contents = sourcegen::add_preamble("sourcegen_lints", sourcegen::reformat(contents)); - let destination = project_root().join("crates/ide_db/src/generated/lints.rs"); + let destination = project_root().join(DESTINATION); sourcegen::ensure_file_contents(destination.as_path(), &contents); } +/// Parses the output of `rustdoc -Whelp` and prints `Lint` and `LintGroup` constants into `buf`. +/// +/// As of writing, the output of `rustc -Whelp` (not rustdoc) has the following format: +/// +/// ```text +/// Lint checks provided by rustc: +/// +/// name default meaning +/// ---- ------- ------- +/// +/// ... +/// +/// Lint groups provided by rustc: +/// +/// name sub-lints +/// ---- --------- +/// +/// ... +/// ``` +/// +/// `rustdoc -Whelp` (and any other custom `rustc` driver) adds another two +/// tables after the `rustc` ones, with a different title but the same format. fn generate_lint_descriptor(sh: &Shell, buf: &mut String) { - // FIXME: rustdoc currently requires an input file for -Whelp cc https://github.com/rust-lang/rust/pull/88831 - let file = project_root().join(file!()); - let stdout = cmd!(sh, "rustdoc -W help {file}").read().unwrap(); - let start_lints = stdout.find("---- ------- -------").unwrap(); - let start_lint_groups = stdout.find("---- ---------").unwrap(); - let start_lints_rustdoc = - stdout.find("Lint checks provided by plugins loaded by this crate:").unwrap(); - let start_lint_groups_rustdoc = - stdout.find("Lint groups provided by plugins loaded by this crate:").unwrap(); + let stdout = cmd!(sh, "rustdoc -Whelp").read().unwrap(); + let lints_pat = "---- ------- -------\n"; + let lint_groups_pat = "---- ---------\n"; + let lints = find_and_slice(&stdout, lints_pat); + let lint_groups = find_and_slice(lints, lint_groups_pat); + let lints_rustdoc = find_and_slice(lint_groups, lints_pat); + let lint_groups_rustdoc = find_and_slice(lints_rustdoc, lint_groups_pat); buf.push_str(r#"pub const DEFAULT_LINTS: &[Lint] = &["#); buf.push('\n'); - let lints = stdout[start_lints..].lines().skip(1).take_while(|l| !l.is_empty()).map(|line| { + let lints = lints.lines().take_while(|l| !l.is_empty()).map(|line| { let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap(); let (_default_level, description) = rest.trim().split_once(char::is_whitespace).unwrap(); (name.trim(), Cow::Borrowed(description.trim()), vec![]) }); - let lint_groups = - stdout[start_lint_groups..].lines().skip(1).take_while(|l| !l.is_empty()).map(|line| { - let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap(); - ( - name.trim(), - format!("lint group for: {}", lints.trim()).into(), - lints - .split_ascii_whitespace() - .map(|s| s.trim().trim_matches(',').replace('-', "_")) - .collect(), - ) - }); + let lint_groups = lint_groups.lines().take_while(|l| !l.is_empty()).map(|line| { + let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap(); + ( + name.trim(), + format!("lint group for: {}", lints.trim()).into(), + lints + .split_ascii_whitespace() + .map(|s| s.trim().trim_matches(',').replace('-', "_")) + .collect(), + ) + }); let lints = lints .chain(lint_groups) @@ -94,7 +137,8 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) { for (name, description, ..) in &lints { push_lint_completion(buf, &name.replace('-', "_"), description); } - buf.push_str("];\n"); + buf.push_str("];\n\n"); + buf.push_str(r#"pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &["#); for (name, description, children) in &lints { if !children.is_empty() { @@ -115,27 +159,23 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) { buf.push_str(r#"pub const RUSTDOC_LINTS: &[Lint] = &["#); buf.push('\n'); - let lints_rustdoc = - stdout[start_lints_rustdoc..].lines().skip(2).take_while(|l| !l.is_empty()).map(|line| { - let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap(); - let (_default_level, description) = - rest.trim().split_once(char::is_whitespace).unwrap(); - (name.trim(), Cow::Borrowed(description.trim()), vec![]) - }); + let lints_rustdoc = lints_rustdoc.lines().take_while(|l| !l.is_empty()).map(|line| { + let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap(); + let (_default_level, description) = rest.trim().split_once(char::is_whitespace).unwrap(); + (name.trim(), Cow::Borrowed(description.trim()), vec![]) + }); let lint_groups_rustdoc = - stdout[start_lint_groups_rustdoc..].lines().skip(2).take_while(|l| !l.is_empty()).map( - |line| { - let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap(); - ( - name.trim(), - format!("lint group for: {}", lints.trim()).into(), - lints - .split_ascii_whitespace() - .map(|s| s.trim().trim_matches(',').replace('-', "_")) - .collect(), - ) - }, - ); + lint_groups_rustdoc.lines().take_while(|l| !l.is_empty()).map(|line| { + let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap(); + ( + name.trim(), + format!("lint group for: {}", lints.trim()).into(), + lints + .split_ascii_whitespace() + .map(|s| s.trim().trim_matches(',').replace('-', "_")) + .collect(), + ) + }); let lints_rustdoc = lints_rustdoc .chain(lint_groups_rustdoc) @@ -145,7 +185,7 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) { for (name, description, ..) in &lints_rustdoc { push_lint_completion(buf, &name.replace('-', "_"), description) } - buf.push_str("];\n"); + buf.push_str("];\n\n"); buf.push_str(r#"pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &["#); for (name, description, children) in &lints_rustdoc { @@ -157,14 +197,24 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) { buf.push_str("];\n"); } +#[track_caller] +fn find_and_slice<'a>(i: &'a str, p: &str) -> &'a str { + let idx = i.find(p).unwrap(); + &i[idx + p.len()..] +} + +/// Parses the unstable book `src_dir` and prints a constant with the list of +/// unstable features into `buf`. +/// +/// It does this by looking for all `.md` files in the `language-features` and +/// `library-features` directories, and using the file name as the feature +/// name, and the file contents as the feature description. fn generate_feature_descriptor(buf: &mut String, src_dir: &Path) { let mut features = ["language-features", "library-features"] .into_iter() .flat_map(|it| sourcegen::list_files(&src_dir.join(it))) - .filter(|path| { - // Get all `.md ` files - path.extension().unwrap_or_default().to_str().unwrap_or_default() == "md" - }) + // Get all `.md` files + .filter(|path| path.extension() == Some("md".as_ref())) .map(|path| { let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace('-', "_"); let doc = fs::read_to_string(path).unwrap(); @@ -196,7 +246,7 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) { let mut clippy_lints: Vec<ClippyLint> = Vec::new(); let mut clippy_groups: std::collections::BTreeMap<String, Vec<String>> = Default::default(); - for line in file_content.lines().map(|line| line.trim()) { + for line in file_content.lines().map(str::trim) { if let Some(line) = line.strip_prefix(r#""id": ""#) { let clippy_lint = ClippyLint { id: line.strip_suffix(r#"","#).expect("should be suffixed by comma").into(), @@ -211,12 +261,19 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) { .push(clippy_lints.last().unwrap().id.clone()); } } else if let Some(line) = line.strip_prefix(r#""docs": ""#) { - let prefix_to_strip = r#" ### What it does"#; - let line = match line.strip_prefix(prefix_to_strip) { - Some(line) => line, + let header = "### What it does"; + let line = match line.find(header) { + Some(idx) => &line[idx + header.len()..], None => { - eprintln!("unexpected clippy prefix for {}", clippy_lints.last().unwrap().id); - continue; + let id = &clippy_lints.last().unwrap().id; + // these just don't have the common header + let allowed = ["allow_attributes", "read_line_without_trim"]; + if allowed.contains(&id.as_str()) { + line + } else { + eprintln!("\nunexpected clippy prefix for {id}, line={line:?}\n",); + continue; + } } }; // Only take the description, any more than this is a lot of additional data we would embed into the exe diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 14aa3940199..f4055024cc3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -13,9 +13,9 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -either = "1.7.0" -itertools = "0.10.5" -serde_json = "1.0.86" +either.workspace = true +itertools.workspace = true +serde_json.workspace = true once_cell = "1.17.0" # local deps diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs index 3b69640af9b..9ed8199ae4d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs @@ -166,7 +166,7 @@ fn main() { check_diagnostics( r#" struct A { a: &'static str } -fn f(a: A) { let A { a: hello } = a; } +fn f(a: A) { let A { a: _hello } = a; } "#, ); check_diagnostics( @@ -181,12 +181,14 @@ fn f(a: A) { let A { 0: 0 } = a; } struct A { a: &'static str } fn f(a: A) { let A { a$0: a } = a; + _ = a; } "#, r#" struct A { a: &'static str } fn f(a: A) { let A { a } = a; + _ = a; } "#, ); @@ -196,12 +198,14 @@ fn f(a: A) { struct A { a: &'static str, b: &'static str } fn f(a: A) { let A { a$0: a, b } = a; + _ = (a, b); } "#, r#" struct A { a: &'static str, b: &'static str } fn f(a: A) { let A { a, b } = a; + _ = (a, b); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 235062bf531..0f12e814ba3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -113,6 +113,31 @@ fn some_fn() { } "#, ); + + check_fix( + r#" +static S: i32 = M::A; + +mod $0M { + pub const A: i32 = 10; +} + +mod other { + use crate::M::A; +} +"#, + r#" +static S: i32 = m::A; + +mod m { + pub const A: i32 = 10; +} + +mod other { + use crate::m::A; +} +"#, + ); } #[test] @@ -175,10 +200,10 @@ fn NonSnakeCaseName() {} fn incorrect_function_params() { check_diagnostics( r#" -fn foo(SomeParam: u8) {} +fn foo(SomeParam: u8) { _ = SomeParam; } // ^^^^^^^^^ 💡 warn: Parameter `SomeParam` should have snake_case name, e.g. `some_param` -fn foo2(ok_param: &str, CAPS_PARAM: u8) {} +fn foo2(ok_param: &str, CAPS_PARAM: u8) { _ = (ok_param, CAPS_PARAM); } // ^^^^^^^^^^ 💡 warn: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` "#, ); @@ -188,6 +213,7 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {} fn incorrect_variable_names() { check_diagnostics( r#" +#[allow(unused)] fn foo() { let SOME_VALUE = 10; // ^^^^^^^^^^ 💡 warn: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` @@ -294,6 +320,7 @@ impl someStruct { // ^^^^^^^^ 💡 warn: Function `SomeFunc` should have snake_case name, e.g. `some_func` let WHY_VAR_IS_CAPS = 10; // ^^^^^^^^^^^^^^^ 💡 warn: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` + _ = WHY_VAR_IS_CAPS; } } "#, @@ -306,6 +333,7 @@ impl someStruct { r#" enum Option { Some, None } +#[allow(unused)] fn main() { match Option::None { None => (), @@ -322,6 +350,7 @@ fn main() { r#" enum Option { Some, None } +#[allow(unused)] fn main() { match Option::None { SOME_VAR @ None => (), @@ -349,7 +378,9 @@ enum E { } mod F { - fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} + fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) { + _ = BAD_NAME_HI; + } } "#, ); @@ -395,7 +426,7 @@ fn qualify() { #[test] // Issue #8809. fn parenthesized_parameter() { - check_diagnostics(r#"fn f((O): _) {}"#) + check_diagnostics(r#"fn f((O): _) { _ = O; }"#) } #[test] @@ -472,7 +503,9 @@ mod CheckBadStyle { mod F { #![allow(non_snake_case)] - fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} + fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) { + _ = BAD_NAME_HI; + } } #[allow(non_snake_case, non_camel_case_types)] @@ -510,17 +543,20 @@ fn NonSnakeCaseName(some_var: u8) -> u8 { #[deny(nonstandard_style)] mod CheckNonstandardStyle { + //^^^^^^^^^^^^^^^^^^^^^ 💡 error: Module `CheckNonstandardStyle` should have snake_case name, e.g. `check_nonstandard_style` fn HiImABadFnName() {} //^^^^^^^^^^^^^^ 💡 error: Function `HiImABadFnName` should have snake_case name, e.g. `hi_im_abad_fn_name` } #[deny(warnings)] mod CheckBadStyle { + //^^^^^^^^^^^^^ 💡 error: Module `CheckBadStyle` should have snake_case name, e.g. `check_bad_style` struct fooo; //^^^^ 💡 error: Structure `fooo` should have CamelCase name, e.g. `Fooo` } mod F { + //^ 💡 warn: Module `F` should have snake_case name, e.g. `f` #![deny(non_snake_case)] fn CheckItWorksWithModAttr() {} //^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr` @@ -563,12 +599,12 @@ fn main() { //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR` fn BAZ() { //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz` - let INNER_INNER = 42; - //^^^^^^^^^^^ 💡 warn: Variable `INNER_INNER` should have snake_case name, e.g. `inner_inner` + let _INNER_INNER = 42; + //^^^^^^^^^^^^ 💡 warn: Variable `_INNER_INNER` should have snake_case name, e.g. `_inner_inner` } - let INNER_LOCAL = 42; - //^^^^^^^^^^^ 💡 warn: Variable `INNER_LOCAL` should have snake_case name, e.g. `inner_local` + let _INNER_LOCAL = 42; + //^^^^^^^^^^^^ 💡 warn: Variable `_INNER_LOCAL` should have snake_case name, e.g. `_inner_local` } } "#, @@ -641,4 +677,30 @@ enum E { "#, ); } + + #[test] + fn module_name_inline() { + check_diagnostics( + r#" +mod M { + //^ 💡 warn: Module `M` should have snake_case name, e.g. `m` + mod IncorrectCase {} + //^^^^^^^^^^^^^ 💡 warn: Module `IncorrectCase` should have snake_case name, e.g. `incorrect_case` +} +"#, + ); + } + + #[test] + fn module_name_decl() { + check_diagnostics( + r#" +//- /Foo.rs + +//- /main.rs +mod Foo; + //^^^ 💡 warn: Module `Foo` should have snake_case name, e.g. `foo` +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index a337e2660d6..659b74445f8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -136,6 +136,7 @@ pub(crate) fn json_in_items( it, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } @@ -148,6 +149,7 @@ pub(crate) fn json_in_items( it, config.insert_use.prefix_kind, config.prefer_no_std, + config.prefer_prelude, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 8265e0b1c11..06ba13bcc55 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -23,12 +23,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count( Diagnostic::new( DiagnosticCode::RustcHardError("E0023"), message, - invalid_args_range( - ctx, - d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)), - d.expected, - d.found, - ), + invalid_args_range(ctx, d.expr_or_pat.clone().map(Into::into), d.expected, d.found), ) } @@ -131,7 +126,7 @@ fn f() { zero(); } fn simple_free_fn_one() { check_diagnostics( r#" -fn one(arg: u8) {} +fn one(_arg: u8) {} fn f() { one(); } //^^ error: expected 1 argument, found 0 "#, @@ -139,7 +134,7 @@ fn f() { one(); } check_diagnostics( r#" -fn one(arg: u8) {} +fn one(_arg: u8) {} fn f() { one(1); } "#, ); @@ -176,7 +171,7 @@ fn f() { check_diagnostics( r#" struct S; -impl S { fn method(&self, arg: u8) {} } +impl S { fn method(&self, _arg: u8) {} } fn f() { S.method(); @@ -187,7 +182,7 @@ impl S { fn method(&self, arg: u8) {} } check_diagnostics( r#" struct S; -impl S { fn method(&self, arg: u8) {} } +impl S { fn method(&self, _arg: u8) {} } fn f() { S::method(&S, 0); @@ -335,8 +330,8 @@ struct S; impl S { fn method(#[cfg(NEVER)] self) {} - fn method2(#[cfg(NEVER)] self, arg: u8) {} - fn method3(self, #[cfg(NEVER)] arg: u8) {} + fn method2(#[cfg(NEVER)] self, _arg: u8) {} + fn method3(self, #[cfg(NEVER)] _arg: u8) {} } extern "C" { @@ -365,8 +360,8 @@ fn main() { r#" #[rustc_legacy_const_generics(1, 3)] fn mixed<const N1: &'static str, const N2: bool>( - a: u8, - b: i8, + _a: u8, + _b: i8, ) {} fn f() { @@ -376,8 +371,8 @@ fn f() { #[rustc_legacy_const_generics(1, 3)] fn b<const N1: u8, const N2: u8>( - a: u8, - b: u8, + _a: u8, + _b: u8, ) {} fn g() { @@ -403,7 +398,7 @@ fn f( // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields S(e, f, .., g, d): S // ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields -) {} +) { _ = (a, b, c, d, e, f, g); } "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index acc31cd117a..d7dca1083a0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -39,7 +39,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField d.field_list_parent_path .clone() .map(SyntaxNodePtr::from) - .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())), + .unwrap_or_else(|| d.field_list_parent.clone().into()), ); Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr) @@ -58,10 +58,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass let root = ctx.sema.db.parse_or_expand(d.file); - let current_module = match &d.field_list_parent { - Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()), - Either::Right(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()), - }; + let current_module = + ctx.sema.scope(d.field_list_parent.to_node(&root).syntax()).map(|it| it.module()); let build_text_edit = |parent_syntax, new_syntax: &SyntaxNode, old_syntax| { let edit = { @@ -87,9 +85,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass )]) }; - match &d.field_list_parent { - Either::Left(record_expr) => { - let field_list_parent = record_expr.to_node(&root); + match &d.field_list_parent.to_node(&root) { + Either::Left(field_list_parent) => { let missing_fields = ctx.sema.record_literal_missing_fields(&field_list_parent); let mut locals = FxHashMap::default(); @@ -125,6 +122,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, ctx.config.prefer_no_std, + ctx.config.prefer_prelude, )?; use_trivial_constructor( @@ -152,8 +150,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass old_field_list.syntax(), ) } - Either::Right(record_pat) => { - let field_list_parent = record_pat.to_node(&root); + Either::Right(field_list_parent) => { let missing_fields = ctx.sema.record_pattern_missing_fields(&field_list_parent); let old_field_list = field_list_parent.record_pat_field_list()?; @@ -290,6 +287,7 @@ fn x(a: S) { struct S { s: u32 } fn x(a: S) { let S { ref s } = a; + _ = s; } ", ) @@ -626,7 +624,7 @@ struct TestStruct { one: i32, two: i64 } fn test_fn() { let one = 1; - let s = TestStruct{ one, two: 2 }; + let _s = TestStruct{ one, two: 2 }; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 06b03d3d198..84267d3d906 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -19,6 +19,7 @@ pub(crate) fn missing_match_arms( mod tests { use crate::tests::check_diagnostics; + #[track_caller] fn check_diagnostics_no_bails(ra_fixture: &str) { cov_mark::check_count!(validate_match_bailed_out, 0); crate::tests::check_diagnostics(ra_fixture) @@ -564,6 +565,7 @@ fn bang(never: !) { r#" enum Option<T> { Some(T), None } +#[allow(unused)] fn main() { // `Never` is deliberately not defined so that it's an uninferred type. match Option::<Never>::None { @@ -719,7 +721,7 @@ fn main() { r#" struct S { a: char} fn main(v: S) { - match v { S{ a } => {} } + match v { S{ a } => { _ = a; } } match v { S{ a: _x } => {} } match v { S{ a: 'a' } => {} } match v { S{..} => {} } @@ -901,7 +903,7 @@ enum E{ A, B } fn foo() { match &E::A { E::A => {} - x => {} + _x => {} } }", ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 70b26009bae..0f695b2745a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -100,9 +100,9 @@ mod tests { r#" fn main() { let x = &5 as *const usize; - unsafe { let y = *x; } - let z = *x; -} //^^💡 error: this operation is unsafe and requires an unsafe function or block + unsafe { let _y = *x; } + let _z = *x; +} //^^💡 error: this operation is unsafe and requires an unsafe function or block "#, ) } @@ -116,13 +116,13 @@ struct HasUnsafe; impl HasUnsafe { unsafe fn unsafe_fn(&self) { let x = &5 as *const usize; - let y = *x; + let _y = *x; } } unsafe fn unsafe_fn() { let x = &5 as *const usize; - let y = *x; + let _y = *x; } fn main() { @@ -152,10 +152,10 @@ struct Ty { static mut STATIC_MUT: Ty = Ty { a: 0 }; fn main() { - let x = STATIC_MUT.a; - //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + let _x = STATIC_MUT.a; + //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block unsafe { - let x = STATIC_MUT.a; + let _x = STATIC_MUT.a; } } "#, @@ -187,13 +187,13 @@ fn main() { r#" fn main() { let x = &5 as *const usize; - let z = *x$0; + let _z = *x$0; } "#, r#" fn main() { let x = &5 as *const usize; - let z = unsafe { *x }; + let _z = unsafe { *x }; } "#, ); @@ -231,7 +231,7 @@ struct S(usize); impl S { unsafe fn func(&self) { let x = &self.0 as *const usize; - let z = *x; + let _z = *x; } } fn main() { @@ -244,7 +244,7 @@ struct S(usize); impl S { unsafe fn func(&self) { let x = &self.0 as *const usize; - let z = *x; + let _z = *x; } } fn main() { @@ -267,7 +267,7 @@ struct Ty { static mut STATIC_MUT: Ty = Ty { a: 0 }; fn main() { - let x = STATIC_MUT$0.a; + let _x = STATIC_MUT$0.a; } "#, r#" @@ -278,7 +278,7 @@ struct Ty { static mut STATIC_MUT: Ty = Ty { a: 0 }; fn main() { - let x = unsafe { STATIC_MUT.a }; + let _x = unsafe { STATIC_MUT.a }; } "#, ) @@ -382,16 +382,16 @@ fn main() { static mut STATIC_MUT: u8 = 0; fn main() { - let x; - x = STATIC_MUT$0; + let _x; + _x = STATIC_MUT$0; } "#, r#" static mut STATIC_MUT: u8 = 0; fn main() { - let x; - x = unsafe { STATIC_MUT }; + let _x; + _x = unsafe { STATIC_MUT }; } "#, ) @@ -405,14 +405,14 @@ fn main() { static mut STATIC_MUT: u8 = 0; fn main() { - let x = STATIC_MUT$0 + 1; + let _x = STATIC_MUT$0 + 1; } "#, r#" static mut STATIC_MUT: u8 = 0; fn main() { - let x = unsafe { STATIC_MUT } + 1; + let _x = unsafe { STATIC_MUT } + 1; } "#, ) @@ -425,14 +425,14 @@ fn main() { static mut STATIC_MUT: u8 = 0; fn main() { - let x = &STATIC_MUT$0; + let _x = &STATIC_MUT$0; } "#, r#" static mut STATIC_MUT: u8 = 0; fn main() { - let x = unsafe { &STATIC_MUT }; + let _x = unsafe { &STATIC_MUT }; } "#, ) @@ -445,14 +445,14 @@ fn main() { static mut STATIC_MUT: u8 = 0; fn main() { - let x = &&STATIC_MUT$0; + let _x = &&STATIC_MUT$0; } "#, r#" static mut STATIC_MUT: u8 = 0; fn main() { - let x = unsafe { &&STATIC_MUT }; + let _x = unsafe { &&STATIC_MUT }; } "#, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 3aa4aa97026..886aefeb575 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -29,6 +29,7 @@ fn main() { let a = &X; let b = *a; //^ error: cannot move `X` out of reference + _ = b; } "#, ); @@ -46,6 +47,7 @@ fn main() { let b = a.0; //^ error: cannot move `X` out of reference let y = a.1; + _ = (b, y); } "#, ); @@ -59,8 +61,8 @@ fn main() { struct X; fn main() { static S: X = X; - let s = S; - //^ error: cannot move `X` out of reference + let _s = S; + //^^ error: cannot move `X` out of reference } "#, ); @@ -165,7 +167,7 @@ enum X { fn main() { let x = &X::Bar; - let c = || match *x { + let _c = || match *x { X::Foo(t) => t, _ => 5, }; @@ -173,4 +175,19 @@ fn main() { "#, ); } + + #[test] + fn regression_15787() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, slice, copy +fn foo(mut slice: &[u32]) -> usize { + slice = match slice { + [0, rest @ ..] | rest => rest, + }; + slice.len() +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index d056e5c85cc..1875111492c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -324,6 +324,7 @@ fn main() { let x_own = 2; let ref mut x_ref = x_own; //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own` + _ = x_ref; } "#, ); @@ -331,7 +332,7 @@ fn main() { r#" struct Foo; impl Foo { - fn method(&mut self, x: i32) {} + fn method(&mut self, _x: i32) {} } fn main() { let x = Foo; @@ -391,6 +392,7 @@ fn main() { //^^^^^ 💡 warn: variable does not need to be mutable x = 7; //^^^^^ 💡 error: cannot mutate immutable variable `x` + _ = y; } } } @@ -404,12 +406,14 @@ fn main() { // there would be no mutability error for locals in dead code. Rustc tries to // not emit `unused_mut` in this case, but since it works without `mut`, and // special casing it is not trivial, we emit it. + + // Update: now MIR based `unused-variable` is taking over `unused-mut` for the same reason. check_diagnostics( r#" fn main() { return; let mut x = 2; - //^^^^^ 💡 warn: variable does not need to be mutable + //^^^^^ warn: unused variable &mut x; } "#, @@ -419,7 +423,7 @@ fn main() { fn main() { loop {} let mut x = 2; - //^^^^^ 💡 warn: variable does not need to be mutable + //^^^^^ warn: unused variable &mut x; } "#, @@ -440,7 +444,7 @@ fn main(b: bool) { g(); } let mut x = 2; - //^^^^^ 💡 warn: variable does not need to be mutable + //^^^^^ warn: unused variable &mut x; } "#, @@ -454,7 +458,7 @@ fn main(b: bool) { return; } let mut x = 2; - //^^^^^ 💡 warn: variable does not need to be mutable + //^^^^^ warn: unused variable &mut x; } "#, @@ -536,6 +540,7 @@ fn main() { (k @ 5, ref mut t) if { continue; } => { //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z` *t = 5; + _ = k; } _ => { let y = (1, 2); @@ -588,6 +593,7 @@ fn main() { b = 1; c = (2, 3); d = 3; + _ = (c, b, d); } } "#, @@ -600,6 +606,7 @@ fn main() { r#" fn f(mut x: i32) { //^^^^^ 💡 warn: variable does not need to be mutable + f(x + 2); } "#, ); @@ -615,8 +622,11 @@ fn f(x: i32) { r#" fn f((x, y): (i32, i32)) { let t = [0; 2]; - x = 5; - //^^^^^ 💡 error: cannot mutate immutable variable `x` + x = 5; + //^^^^^ 💡 error: cannot mutate immutable variable `x` + _ = x; + _ = y; + _ = t; } "#, ); @@ -645,6 +655,7 @@ fn f(x: [(i32, u8); 10]) { //^^^^^ 💡 warn: variable does not need to be mutable a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` + _ = b; } } "#, @@ -666,6 +677,7 @@ fn f(x: [(i32, u8); 10]) { //^^^^^ 💡 error: cannot mutate immutable variable `a` c = 2; //^^^^^ 💡 error: cannot mutate immutable variable `c` + _ = (b, d); } } } @@ -696,18 +708,18 @@ fn f() { fn overloaded_index() { check_diagnostics( r#" -//- minicore: index +//- minicore: index, copy use core::ops::{Index, IndexMut}; struct Foo; impl Index<usize> for Foo { type Output = (i32, u8); - fn index(&self, index: usize) -> &(i32, u8) { + fn index(&self, _index: usize) -> &(i32, u8) { &(5, 2) } } impl IndexMut<usize> for Foo { - fn index_mut(&mut self, index: usize) -> &mut (i32, u8) { + fn index_mut(&mut self, _index: usize) -> &mut (i32, u8) { &mut (5, 2) } } @@ -715,26 +727,32 @@ fn f() { let mut x = Foo; //^^^^^ 💡 warn: variable does not need to be mutable let y = &x[2]; + _ = (x, y); let x = Foo; let y = &mut x[2]; //^💡 error: cannot mutate immutable variable `x` + _ = (x, y); let mut x = &mut Foo; //^^^^^ 💡 warn: variable does not need to be mutable let y: &mut (i32, u8) = &mut x[2]; + _ = (x, y); let x = Foo; let ref mut y = x[7]; //^ 💡 error: cannot mutate immutable variable `x` + _ = (x, y); let (ref mut y, _) = x[3]; //^ 💡 error: cannot mutate immutable variable `x` + _ = y; match x[10] { //^ 💡 error: cannot mutate immutable variable `x` - (ref y, _) => (), - (_, ref mut y) => (), + (ref y, 5) => _ = y, + (_, ref mut y) => _ = y, } let mut x = Foo; let mut i = 5; //^^^^^ 💡 warn: variable does not need to be mutable let y = &mut x[i]; + _ = y; } "#, ); @@ -744,7 +762,7 @@ fn f() { fn overloaded_deref() { check_diagnostics( r#" -//- minicore: deref_mut +//- minicore: deref_mut, copy use core::ops::{Deref, DerefMut}; struct Foo; @@ -763,21 +781,27 @@ fn f() { let mut x = Foo; //^^^^^ 💡 warn: variable does not need to be mutable let y = &*x; + _ = (x, y); let x = Foo; let y = &mut *x; //^^ 💡 error: cannot mutate immutable variable `x` + _ = (x, y); let x = Foo; + //^ warn: unused variable let x = Foo; let y: &mut (i32, u8) = &mut x; //^^^^^^ 💡 error: cannot mutate immutable variable `x` + _ = (x, y); let ref mut y = *x; //^^ 💡 error: cannot mutate immutable variable `x` + _ = y; let (ref mut y, _) = *x; //^^ 💡 error: cannot mutate immutable variable `x` + _ = y; match *x { //^^ 💡 error: cannot mutate immutable variable `x` - (ref y, _) => (), - (_, ref mut y) => (), + (ref y, 5) => _ = y, + (_, ref mut y) => _ = y, } } "#, @@ -866,6 +890,7 @@ pub fn test() { data: 0 } ); + _ = tree; } "#, ); @@ -925,6 +950,7 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { let x = X; let closure4 = || { x.mutate(); }; //^ 💡 error: cannot mutate immutable variable `x` + _ = (closure2, closure3, closure4); } "#, ); @@ -941,7 +967,9 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { z = 3; let mut k = z; //^^^^^ 💡 warn: variable does not need to be mutable + _ = k; }; + _ = (x, closure); } "#, ); @@ -958,6 +986,7 @@ fn f() { } } }; + _ = closure; } "#, ); @@ -972,7 +1001,8 @@ fn f() { let mut x = X; let c2 = || { x = X; x }; let mut x = X; - let c2 = move || { x = X; }; + let c3 = move || { x = X; }; + _ = (c1, c2, c3); } "#, ); @@ -1023,7 +1053,7 @@ fn x(t: &[u8]) { a = 2; //^^^^^ 💡 error: cannot mutate immutable variable `a` - + _ = b; } _ => {} } @@ -1079,6 +1109,7 @@ fn f() { let x = Box::new(5); let closure = || *x = 2; //^ 💡 error: cannot mutate immutable variable `x` + _ = closure; } "#, ); @@ -1156,6 +1187,7 @@ macro_rules! mac { fn main2() { let mut x = mac![]; //^^^^^ 💡 warn: variable does not need to be mutable + _ = x; } "#, ); @@ -1196,4 +1228,20 @@ fn foo(mut foo: Foo) { "#, ); } + + #[test] + fn regression_15670() { + check_diagnostics( + r#" +//- minicore: fn + +pub struct A {} +pub unsafe fn foo(a: *mut A) { + let mut b = || -> *mut A { &mut *a }; + //^^^^^ 💡 warn: variable does not need to be mutable + let _ = b(); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 290c16c9d24..ee8a9c95793 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -13,7 +13,7 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if created structure does not have field provided in record. pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { - let node = d.field.clone().map(|it| it.either(Into::into, Into::into)); + let node = d.field.clone().map(Into::into); if d.private { // FIXME: quickfix to add required visibility Diagnostic::new_with_syntax_node_ptr( @@ -35,15 +35,13 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { // FIXME: quickfix for pattern - match &d.field.value { - Either::Left(ptr) => { - let root = ctx.sema.db.parse_or_expand(d.field.file_id); - missing_record_expr_field_fixes( - &ctx.sema, - d.field.file_id.original_file(ctx.sema.db), - &ptr.to_node(&root), - ) - } + let root = ctx.sema.db.parse_or_expand(d.field.file_id); + match &d.field.value.to_node(&root) { + Either::Left(node) => missing_record_expr_field_fixes( + &ctx.sema, + d.field.file_id.original_file(ctx.sema.db), + node, + ), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index c44d28e77f6..a828b8b4fd2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -1,5 +1,3 @@ -use either::Either; - use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: private-assoc-item @@ -28,13 +26,7 @@ pub(crate) fn private_assoc_item( }, name, ), - d.expr_or_pat.clone().map(|it| match it { - Either::Left(it) => it.into(), - Either::Right(it) => match it { - Either::Left(it) => it.into(), - Either::Right(it) => it.into(), - }, - }), + d.expr_or_pat.clone().map(Into::into), ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 083ef3e8dc1..d15233d15c2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -74,8 +74,8 @@ mod tests { r#" //- minicore: iterators fn foo() { - let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); -} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) + let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) "#, ); } @@ -117,7 +117,7 @@ fn foo() { fn foo() { let mut m = core::iter::repeat(()) .filter_map(|()| Some(92)); - let n = m.next(); + let _n = m.next(); } "#, ); @@ -148,22 +148,22 @@ fn foo() { fn foo() { #[allow(clippy::filter_map_next)] - let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); + let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); } #[deny(clippy::filter_map_next)] fn foo() { - let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); -} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: replace filter_map(..).next() with find_map(..) + let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: replace filter_map(..).next() with find_map(..) fn foo() { - let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); -} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) + let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) #[warn(clippy::filter_map_next)] fn foo() { - let m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); -} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warn: replace filter_map(..).next() with find_map(..) + let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next(); +} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warn: replace filter_map(..).next() with find_map(..) "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs new file mode 100644 index 00000000000..251a645292e --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -0,0 +1,129 @@ +use hir::InFile; +use syntax::ast; + +use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; + +// Diagnostic: trait-impl-incorrect-safety +// +// Diagnoses incorrect safety annotations of trait impls. +pub(crate) fn trait_impl_incorrect_safety( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplIncorrectSafety, +) -> Diagnostic { + Diagnostic::new( + DiagnosticCode::Ra("trait-impl-incorrect-safety", Severity::Error), + if d.should_be_safe { + "unsafe impl for safe trait" + } else { + "impl for unsafe trait needs to be unsafe" + }, + adjusted_display_range::<ast::Impl>( + ctx, + InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() }, + &|impl_| { + if d.should_be_safe { + Some(match (impl_.unsafe_token(), impl_.impl_token()) { + (None, None) => return None, + (None, Some(t)) | (Some(t), None) => t.text_range(), + (Some(t1), Some(t2)) => t1.text_range().cover(t2.text_range()), + }) + } else { + impl_.impl_token().map(|t| t.text_range()) + } + }, + ), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn simple() { + check_diagnostics( + r#" +trait Safe {} +unsafe trait Unsafe {} + + impl Safe for () {} + + impl Unsafe for () {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl Safe for () {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl Unsafe for () {} +"#, + ); + } + + #[test] + fn drop_may_dangle() { + check_diagnostics( + r#" +#[lang = "drop"] +trait Drop {} +struct S<T>; +struct L<'l>; + + impl<T> Drop for S<T> {} + + impl<#[may_dangle] T> Drop for S<T> {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl<T> Drop for S<T> {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl<#[may_dangle] T> Drop for S<T> {} + + impl<'l> Drop for L<'l> {} + + impl<#[may_dangle] 'l> Drop for L<'l> {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl<'l> Drop for L<'l> {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl<#[may_dangle] 'l> Drop for L<'l> {} +"#, + ); + } + + #[test] + fn negative() { + check_diagnostics( + r#" +trait Trait {} + + impl !Trait for () {} + + unsafe impl !Trait for () {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + +unsafe trait UnsafeTrait {} + + impl !UnsafeTrait for () {} + + unsafe impl !UnsafeTrait for () {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + +"#, + ); + } + + #[test] + fn inherent() { + check_diagnostics( + r#" +struct S; + + impl S {} + + unsafe impl S {} +//^^^^^^^^^^^ error: unsafe impl for safe trait +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs new file mode 100644 index 00000000000..40d0b6fdd4b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -0,0 +1,102 @@ +use hir::InFile; +use itertools::Itertools; +use syntax::{ast, AstNode}; + +use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: trait-impl-missing-assoc_item +// +// Diagnoses missing trait items in a trait impl. +pub(crate) fn trait_impl_missing_assoc_item( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplMissingAssocItems, +) -> Diagnostic { + let missing = d.missing.iter().format_with(", ", |(name, item), f| { + f(&match *item { + hir::AssocItem::Function(_) => "`fn ", + hir::AssocItem::Const(_) => "`const ", + hir::AssocItem::TypeAlias(_) => "`type ", + })?; + f(&name.display(ctx.sema.db))?; + f(&"`") + }); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0046"), + format!("not all trait items implemented, missing: {missing}"), + adjusted_display_range::<ast::Impl>( + ctx, + InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() }, + &|impl_| impl_.trait_().map(|t| t.syntax().text_range()), + ), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn simple() { + check_diagnostics( + r#" +trait Trait { + const C: (); + type T; + fn f(); +} + +impl Trait for () { + const C: () = (); + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C`, `type T`, `fn f` +} + +"#, + ); + } + + #[test] + fn default() { + check_diagnostics( + r#" +trait Trait { + const C: (); + type T = (); + fn f() {} +} + +impl Trait for () { + const C: () = (); + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` + type T = (); + fn f() {} +} + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` + type T = (); + } + +impl Trait for () { + //^^^^^ error: not all trait items implemented, missing: `const C` +} + +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs new file mode 100644 index 00000000000..159d87d269d --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs @@ -0,0 +1,106 @@ +use hir::InFile; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: trait-impl-orphan +// +// Only traits defined in the current crate can be implemented for arbitrary types +pub(crate) fn trait_impl_orphan( + ctx: &DiagnosticsContext<'_>, + d: &hir::TraitImplOrphan, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0117"), + format!("only traits defined in the current crate can be implemented for arbitrary types"), + InFile::new(d.file_id, d.impl_.clone().into()), + ) + // Not yet checked for false positives + .experimental() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn simple() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo {} +//- /bar.rs crate:bar +pub struct Bar; +//- /main.rs crate:main deps:foo,bar +struct LocalType; +trait LocalTrait {} + impl foo::Foo for bar::Bar {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types +impl foo::Foo for LocalType {} +impl LocalTrait for bar::Bar {} +"#, + ); + } + + #[test] + fn generics() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo<T> {} +//- /bar.rs crate:bar +pub struct Bar<T>(T); +//- /main.rs crate:main deps:foo,bar +struct LocalType<T>; +trait LocalTrait<T> {} + impl<T> foo::Foo<T> for bar::Bar<T> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + + impl<T> foo::Foo<T> for bar::Bar<LocalType<T>> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + + impl<T> foo::Foo<LocalType<T>> for bar::Bar<T> {} + + impl<T> foo::Foo<bar::Bar<LocalType<T>>> for bar::Bar<LocalType<T>> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types +"#, + ); + } + + #[test] + fn fundamental() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo<T> {} +//- /bar.rs crate:bar +pub struct Bar<T>(T); +#[lang = "owned_box"] +#[fundamental] +pub struct Box<T>(T); +//- /main.rs crate:main deps:foo,bar +struct LocalType; + impl<T> foo::Foo<T> for bar::Box<T> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + impl<T> foo::Foo<T> for &LocalType {} + impl<T> foo::Foo<T> for bar::Box<LocalType> {} +"#, + ); + } + + #[test] + fn dyn_object() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo<T> {} +//- /bar.rs crate:bar +pub struct Bar; +//- /main.rs crate:main deps:foo,bar +trait LocalTrait {} +impl<T> foo::Foo<T> for dyn LocalTrait {} +impl<T> foo::Foo<dyn LocalTrait> for Bar {} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 15bd28c00df..c92d92ceae8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,4 +1,3 @@ -use either::Either; use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type}; use ide_db::{famous_defs::FamousDefs, source_change::SourceChange}; use syntax::{ @@ -14,9 +13,11 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, Dia // This diagnostic is triggered when the type of an expression or pattern does not match // the expected type. pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic { - let display_range = match &d.expr_or_pat { - Either::Left(expr) => { - adjusted_display_range::<ast::Expr>(ctx, expr.clone().map(|it| it.into()), &|expr| { + let display_range = match &d.expr_or_pat.value { + expr if ast::Expr::can_cast(expr.kind()) => adjusted_display_range::<ast::Expr>( + ctx, + InFile { file_id: d.expr_or_pat.file_id, value: expr.syntax_node_ptr() }, + &|expr| { let salient_token_range = match expr { ast::Expr::IfExpr(it) => it.if_token()?.text_range(), ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(), @@ -32,10 +33,15 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) cov_mark::hit!(type_mismatch_range_adjustment); Some(salient_token_range) - }) - } - Either::Right(pat) => { - ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range + }, + ), + pat => { + ctx.sema + .diagnostics_display_range(InFile { + file_id: d.expr_or_pat.file_id, + value: pat.syntax_node_ptr(), + }) + .range } }; let mut diag = Diagnostic::new( @@ -57,14 +63,12 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> { let mut fixes = Vec::new(); - match &d.expr_or_pat { - Either::Left(expr_ptr) => { - add_reference(ctx, d, expr_ptr, &mut fixes); - add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); - remove_semicolon(ctx, d, expr_ptr, &mut fixes); - str_ref_to_owned(ctx, d, expr_ptr, &mut fixes); - } - Either::Right(_pat_ptr) => {} + if let Some(expr_ptr) = d.expr_or_pat.value.clone().cast::<ast::Expr>() { + let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr.clone() }; + add_reference(ctx, d, expr_ptr, &mut fixes); + add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); + remove_semicolon(ctx, d, expr_ptr, &mut fixes); + str_ref_to_owned(ctx, d, expr_ptr, &mut fixes); } if fixes.is_empty() { @@ -205,7 +209,7 @@ fn main() { test(123); //^^^ 💡 error: expected &i32, found i32 } -fn test(arg: &i32) {} +fn test(_arg: &i32) {} "#, ); } @@ -217,13 +221,13 @@ fn test(arg: &i32) {} fn main() { test(123$0); } -fn test(arg: &i32) {} +fn test(_arg: &i32) {} "#, r#" fn main() { test(&123); } -fn test(arg: &i32) {} +fn test(_arg: &i32) {} "#, ); } @@ -235,13 +239,13 @@ fn test(arg: &i32) {} fn main() { test($0123); } -fn test(arg: &mut i32) {} +fn test(_arg: &mut i32) {} "#, r#" fn main() { test(&mut 123); } -fn test(arg: &mut i32) {} +fn test(_arg: &mut i32) {} "#, ); } @@ -254,13 +258,13 @@ fn test(arg: &mut i32) {} fn main() { test($0[1, 2, 3]); } -fn test(arg: &[i32]) {} +fn test(_arg: &[i32]) {} "#, r#" fn main() { test(&[1, 2, 3]); } -fn test(arg: &[i32]) {} +fn test(_arg: &[i32]) {} "#, ); } @@ -274,24 +278,26 @@ struct Foo; struct Bar; impl core::ops::Deref for Foo { type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } } fn main() { test($0Foo); } -fn test(arg: &Bar) {} +fn test(_arg: &Bar) {} "#, r#" struct Foo; struct Bar; impl core::ops::Deref for Foo { type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } } fn main() { test(&Foo); } -fn test(arg: &Bar) {} +fn test(_arg: &Bar) {} "#, ); } @@ -305,7 +311,7 @@ fn main() { } struct Test; impl Test { - fn call_by_ref(&self, arg: &i32) {} + fn call_by_ref(&self, _arg: &i32) {} } "#, r#" @@ -314,7 +320,7 @@ fn main() { } struct Test; impl Test { - fn call_by_ref(&self, arg: &i32) {} + fn call_by_ref(&self, _arg: &i32) {} } "#, ); @@ -345,7 +351,7 @@ macro_rules! thousand { 1000_u64 }; } -fn test(foo: &u64) {} +fn test(_foo: &u64) {} fn main() { test($0thousand!()); } @@ -356,7 +362,7 @@ macro_rules! thousand { 1000_u64 }; } -fn test(foo: &u64) {} +fn test(_foo: &u64) {} fn main() { test(&thousand!()); } @@ -369,12 +375,12 @@ fn main() { check_fix( r#" fn main() { - let test: &mut i32 = $0123; + let _test: &mut i32 = $0123; } "#, r#" fn main() { - let test: &mut i32 = &mut 123; + let _test: &mut i32 = &mut 123; } "#, ); @@ -411,7 +417,7 @@ fn div(x: i32, y: i32) -> Option<i32> { fn f<const N: u64>() -> Rate<N> { // FIXME: add some error loop {} } - fn run(t: Rate<5>) { + fn run(_t: Rate<5>) { } fn main() { run(f()) // FIXME: remove this error @@ -426,7 +432,7 @@ fn div(x: i32, y: i32) -> Option<i32> { check_diagnostics( r#" pub struct Rate<T, const NOM: u32, const DENOM: u32>(T); - fn run(t: Rate<u32, 1, 1>) { + fn run(_t: Rate<u32, 1, 1>) { } fn main() { run(Rate::<_, _, _>(5)); @@ -650,7 +656,7 @@ fn h() { r#" struct X<T>(T); -fn foo(x: X<Unknown>) {} +fn foo(_x: X<Unknown>) {} fn test1() { // Unknown might be `i32`, so we should not emit type mismatch here. foo(X(42)); @@ -736,4 +742,19 @@ fn g() { return; } "#, ); } + + #[test] + fn smoke_test_inner_items() { + check_diagnostics( + r#" +fn f() { + fn inner() -> i32 { + return; + // ^^^^^^ error: expected i32, found () + 0 + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index 4af67227115..4e215a89d79 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -142,8 +142,8 @@ fn t<T>() -> T { loop {} } check_diagnostics( r#" fn main() { - let x = [(); _]; - let y: [(); 10] = [(); _]; + let _x = [(); _]; + let _y: [(); 10] = [(); _]; _ = 0; (_,) = (1,); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs new file mode 100644 index 00000000000..28ccf474b40 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -0,0 +1,111 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: unused-variables +// +// This diagnostic is triggered when a local variable is not used. +pub(crate) fn unused_variables( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnusedVariable, +) -> Diagnostic { + let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_variables"), + "unused variable", + ast, + ) + .experimental() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn unused_variables_simple() { + check_diagnostics( + r#" +//- minicore: fn +struct Foo { f1: i32, f2: i64 } + +fn f(kkk: i32) {} + //^^^ warn: unused variable +fn main() { + let a = 2; + //^ warn: unused variable + let b = 5; + // note: `unused variable` implies `unused mut`, so we should not emit both at the same time. + let mut c = f(b); + //^^^^^ warn: unused variable + let (d, e) = (3, 5); + //^ warn: unused variable + let _ = e; + let f1 = 2; + let f2 = 5; + let f = Foo { f1, f2 }; + match f { + Foo { f1, f2 } => { + //^^ warn: unused variable + _ = f2; + } + } + let g = false; + if g {} + let h: fn() -> i32 = || 2; + let i = h(); + //^ warn: unused variable +} +"#, + ); + } + + #[test] + fn unused_self() { + check_diagnostics( + r#" +struct S { +} +impl S { + fn owned_self(self, u: i32) {} + //^ warn: unused variable + fn ref_self(&self, u: i32) {} + //^ warn: unused variable + fn ref_mut_self(&mut self, u: i32) {} + //^ warn: unused variable + fn owned_mut_self(mut self) {} + //^^^^^^^^ 💡 warn: variable does not need to be mutable + +} +"#, + ); + } + + #[test] + fn allow_unused_variables_for_identifiers_starting_with_underline() { + check_diagnostics( + r#" +fn main() { + let _x = 2; +} +"#, + ); + } + + #[test] + fn respect_lint_attributes_for_unused_variables() { + check_diagnostics( + r#" +fn main() { + #[allow(unused_variables)] + let x = 2; +} + +#[deny(unused)] +fn main2() { + let x = 2; + //^ error: unused variable +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index ebe197a6790..6744895f3cd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -44,6 +44,9 @@ mod handlers { pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod replace_filter_map_next_with_find_map; + pub(crate) mod trait_impl_orphan; + pub(crate) mod trait_impl_incorrect_safety; + pub(crate) mod trait_impl_missing_assoc_item; pub(crate) mod typed_hole; pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; @@ -56,6 +59,7 @@ mod handlers { pub(crate) mod unresolved_proc_macro; pub(crate) mod undeclared_label; pub(crate) mod unreachable_label; + pub(crate) mod unused_variables; // The handlers below are unusual, the implement the diagnostics as well. pub(crate) mod field_shorthand; @@ -224,6 +228,7 @@ pub struct DiagnosticsConfig { // FIXME: We may want to include a whole `AssistConfig` here pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, } impl DiagnosticsConfig { @@ -246,6 +251,7 @@ impl DiagnosticsConfig { skip_glob_imports: false, }, prefer_no_std: false, + prefer_prelude: true, } } } @@ -355,6 +361,9 @@ pub fn diagnostics( AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), + AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d), + AnyDiagnostic::TraitImplMissingAssocItems(d) => handlers::trait_impl_missing_assoc_item::trait_impl_missing_assoc_item(&ctx, &d), + AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d), AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d), AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d), AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d), @@ -368,6 +377,7 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), + AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d), AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index ee0e0354906..c766a018bfd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -5,7 +5,7 @@ use expect_test::Expect; use ide_db::{ assists::AssistResolveStrategy, base_db::{fixture::WithFixture, SourceDatabaseExt}, - RootDatabase, + LineIndexDatabase, RootDatabase, }; use stdx::trim_indent; use test_utils::{assert_eq_text, extract_annotations, MiniCore}; @@ -43,7 +43,8 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) .pop() .expect("no diagnostics"); - let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth]; + let fix = + &diagnostic.fixes.expect(&format!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; let actual = { let source_change = fix.source_change.as_ref().unwrap(); let file_id = *source_change.source_file_edits.keys().next().unwrap(); @@ -103,6 +104,7 @@ pub(crate) fn check_diagnostics(ra_fixture: &str) { pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) { let (db, files) = RootDatabase::with_many_files(ra_fixture); for file_id in files { + let line_index = db.line_index(file_id); let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); let expected = extract_annotations(&db.file_text(file_id)); @@ -136,8 +138,16 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur } } if expected != actual { - let fneg = expected.iter().filter(|x| !actual.contains(x)).collect::<Vec<_>>(); - let fpos = actual.iter().filter(|x| !expected.contains(x)).collect::<Vec<_>>(); + let fneg = expected + .iter() + .filter(|x| !actual.contains(x)) + .map(|(range, s)| (line_index.line_col(range.start()), range, s)) + .collect::<Vec<_>>(); + let fpos = actual + .iter() + .filter(|x| !expected.contains(x)) + .map(|(range, s)| (line_index.line_col(range.start()), range, s)) + .collect::<Vec<_>>(); panic!("Diagnostic test failed.\nFalse negatives: {fneg:?}\nFalse positives: {fpos:?}"); } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 70ed6dea5bf..56b29f92b82 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true triomphe.workspace = true nohash-hasher.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 60fcbbbd397..0312a0f11eb 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -651,7 +651,7 @@ impl Match { for (path, resolved_path) in &template.resolved_paths { if let hir::PathResolution::Def(module_def) = resolved_path.resolution { let mod_path = - module.find_use_path(sema.db, module_def, false).ok_or_else(|| { + module.find_use_path(sema.db, module_def, false, true).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") })?; self.rendered_template_paths.insert(path.clone(), mod_path); diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 2aee203c4ea..d5c3439f956 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -14,9 +14,9 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" crossbeam-channel = "0.5.5" -either = "1.7.0" -itertools = "0.10.5" -tracing = "0.1.35" +either.workspace = true +itertools.workspace = true +tracing.workspace = true oorandom = "11.1.3" pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.1", default-features = false } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 37a17762216..ac15b6aba61 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -602,7 +602,17 @@ fn filename_and_frag_for_def( } Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())), Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())), - Definition::Macro(mac) => format!("macro.{}.html", mac.name(db).display(db.upcast())), + Definition::Macro(mac) => match mac.kind(db) { + hir::MacroKind::Declarative + | hir::MacroKind::BuiltIn + | hir::MacroKind::Attr + | hir::MacroKind::ProcMacro => { + format!("macro.{}.html", mac.name(db).display(db.upcast())) + } + hir::MacroKind::Derive => { + format!("derive.{}.html", mac.name(db).display(db.upcast())) + } + }, Definition::Field(field) => { let def = match field.parent_def(db) { hir::VariantDef::Struct(it) => Definition::Adt(it.into()), diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 119a9c7c3f4..32207745677 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -14,12 +14,12 @@ pub struct ExpandedMacro { // Feature: Expand Macro Recursively // -// Shows the full macro expansion of the macro at current cursor. +// Shows the full macro expansion of the macro at the current caret position. // // |=== // | Editor | Action Name // -// | VS Code | **rust-analyzer: Expand macro recursively** +// | VS Code | **rust-analyzer: Expand macro recursively at caret** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 21934b9480e..e0b64fe7988 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -180,26 +180,24 @@ fn hover_simple( descended() .filter_map(|token| { let node = token.parent()?; - let class = IdentClass::classify_token(sema, token)?; - if let IdentClass::Operator(OperatorClass::Await(_)) = class { + match IdentClass::classify_node(sema, &node)? { // It's better for us to fall back to the keyword hover here, // rendering poll is very confusing - return None; + IdentClass::Operator(OperatorClass::Await(_)) => None, + + IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { + decl, + .. + }) => Some(vec![(Definition::ExternCrateDecl(decl), node)]), + + class => Some( + class + .definitions() + .into_iter() + .zip(iter::repeat(node)) + .collect::<Vec<_>>(), + ), } - if let IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { - decl, - .. - }) = class - { - return Some(vec![(Definition::ExternCrateDecl(decl), node)]); - } - Some( - class - .definitions() - .into_iter() - .zip(iter::once(node).cycle()) - .collect::<Vec<_>>(), - ) }) .flatten() .unique_by(|&(def, _)| def) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index f72ce37d1d9..d0a02fd0dba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -402,10 +402,9 @@ pub(super) fn definition( |&it| it.layout(db), |_| { let var_def = it.parent_def(db); - let id = it.index(); match var_def { hir::VariantDef::Struct(s) => { - Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id)) + Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(it)) } _ => None, } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 81d6db564ff..d3d492f3fde 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1136,7 +1136,9 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); @@ -1155,7 +1157,9 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); @@ -1174,7 +1178,9 @@ impl Thing { ``` ```rust - enum Thing + enum Thing { + A, + } ``` "#]], ); @@ -1193,7 +1199,9 @@ impl Thing { ``` ```rust - enum Thing + enum Thing { + A, + } ``` "#]], ); @@ -2005,7 +2013,10 @@ fn test_hover_layout_of_enum() { ``` ```rust - enum Foo // size = 16 (0x10), align = 8, niches = 254 + enum Foo { + Variant1(u8, u16), + Variant2(i32, u8, i64), + } // size = 16 (0x10), align = 8, niches = 254 ``` "#]], ); @@ -2346,7 +2357,7 @@ fn main() { let s$0t = S{ f1:0 }; } focus_range: 7..8, name: "S", kind: Struct, - description: "struct S", + description: "struct S {\n f1: u32,\n}", }, }, ], @@ -2379,7 +2390,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S<T>", + description: "struct S<T> {\n f1: T,\n}", }, }, HoverGotoTypeData { @@ -2392,7 +2403,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg", + description: "struct Arg(u32);", }, }, ], @@ -2438,7 +2449,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S<T>", + description: "struct S<T> {\n f1: T,\n}", }, }, HoverGotoTypeData { @@ -2451,7 +2462,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg", + description: "struct Arg(u32);", }, }, ], @@ -2487,7 +2498,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 7..8, name: "A", kind: Struct, - description: "struct A", + description: "struct A(u32);", }, }, HoverGotoTypeData { @@ -2500,7 +2511,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 22..23, name: "B", kind: Struct, - description: "struct B", + description: "struct B(u32);", }, }, HoverGotoTypeData { @@ -2514,7 +2525,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } name: "C", kind: Struct, container_name: "M", - description: "pub struct C", + description: "pub struct C(u32);", }, }, ], @@ -2704,7 +2715,7 @@ fn main() { let s$0t = foo(); } focus_range: 39..41, name: "S1", kind: Struct, - description: "struct S1", + description: "struct S1 {}", }, }, HoverGotoTypeData { @@ -2717,7 +2728,7 @@ fn main() { let s$0t = foo(); } focus_range: 52..54, name: "S2", kind: Struct, - description: "struct S2", + description: "struct S2 {}", }, }, ], @@ -2808,7 +2819,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {} focus_range: 36..37, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -2908,7 +2919,7 @@ fn foo(ar$0g: &impl Foo<S>) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -2945,7 +2956,7 @@ fn main() { let s$0t = foo(); } focus_range: 49..50, name: "B", kind: Struct, - description: "struct B<T>", + description: "struct B<T> {}", }, }, HoverGotoTypeData { @@ -3034,7 +3045,7 @@ fn foo(ar$0g: &dyn Foo<S>) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -3082,7 +3093,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} focus_range: 50..51, name: "B", kind: Struct, - description: "struct B<T>", + description: "struct B<T> {}", }, }, HoverGotoTypeData { @@ -3108,7 +3119,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} focus_range: 65..66, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -3335,7 +3346,7 @@ struct S$0T<const C: usize = 1, T = Foo>(T); ``` ```rust - struct ST<const C: usize = 1, T = Foo> + struct ST<const C: usize = 1, T = Foo>(T); ``` "#]], ); @@ -3356,7 +3367,7 @@ struct S$0T<const C: usize = {40 + 2}, T = Foo>(T); ``` ```rust - struct ST<const C: usize = {const}, T = Foo> + struct ST<const C: usize = {const}, T = Foo>(T); ``` "#]], ); @@ -3378,7 +3389,7 @@ struct S$0T<const C: usize = VAL, T = Foo>(T); ``` ```rust - struct ST<const C: usize = VAL, T = Foo> + struct ST<const C: usize = VAL, T = Foo>(T); ``` "#]], ); @@ -5266,38 +5277,46 @@ pub fn foo() {} #[test] fn hover_feature() { check( - r#"#![feature(box_syntax$0)]"#, - expect![[r##" - *box_syntax* - ``` - box_syntax - ``` - ___ + r#"#![feature(intrinsics$0)]"#, + expect![[r#" + *intrinsics* + ``` + intrinsics + ``` + ___ - # `box_syntax` + # `intrinsics` - The tracking issue for this feature is: [#49733] + The tracking issue for this feature is: None. - [#49733]: https://github.com/rust-lang/rust/issues/49733 + Intrinsics are never intended to be stable directly, but intrinsics are often + exported in some sort of stable manner. Prefer using the stable interfaces to + the intrinsic directly when you can. - See also [`box_patterns`](box-patterns.md) + ------------------------ - ------------------------ - Currently the only stable way to create a `Box` is via the `Box::new` method. - Also it is not possible in stable Rust to destructure a `Box` in a match - pattern. The unstable `box` keyword can be used to create a `Box`. An example - usage would be: + These are imported as if they were FFI functions, with the special + `rust-intrinsic` ABI. For example, if one was in a freestanding + context, but wished to be able to `transmute` between types, and + perform efficient pointer arithmetic, one would import those functions + via a declaration like - ```rust - #![feature(box_syntax)] + ```rust + #![feature(intrinsics)] + #![allow(internal_features)] + # fn main() {} - fn main() { - let b = box 5; - } - ``` + extern "rust-intrinsic" { + fn transmute<T, U>(x: T) -> U; - "##]], + fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; + } + ``` + + As with any other FFI functions, these are always `unsafe` to call. + + "#]], ) } @@ -5927,7 +5946,7 @@ pub struct Foo(i32); ``` ```rust - pub struct Foo // size = 4, align = 4 + pub struct Foo(i32); // size = 4, align = 4 ``` --- diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index a5d070fe767..24f44ca06ff 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -563,6 +563,7 @@ mod tests { use hir::ClosureStyle; use itertools::Itertools; use test_utils::extract_annotations; + use text_edit::{TextRange, TextSize}; use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; use crate::DiscriminantHints; @@ -629,6 +630,22 @@ mod tests { expect.assert_debug_eq(&inlay_hints) } + #[track_caller] + pub(super) fn check_expect_clear_loc( + config: InlayHintsConfig, + ra_fixture: &str, + expect: Expect, + ) { + let (analysis, file_id) = fixture::file(ra_fixture); + let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { + if let Some(loc) = &mut hint.linked_location { + loc.range = TextRange::empty(TextSize::from(0)); + } + }); + expect.assert_debug_eq(&inlay_hints) + } + /// Computes inlay hints for the fixture, applies all the provided text edits and then runs /// expect test. #[track_caller] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 12e46c0f883..c9e9a223786 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -78,7 +78,9 @@ mod tests { use expect_test::expect; use crate::{ - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::tests::{ + check_expect, check_expect_clear_loc, check_with_config, DISABLED_CONFIG, TEST_CONFIG, + }, InlayHintsConfig, }; @@ -444,7 +446,7 @@ fn main() { #[test] fn shorten_iterator_chaining_hints() { - check_expect( + check_expect_clear_loc( InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, r#" //- minicore: iterators @@ -484,7 +486,7 @@ fn main() { file_id: FileId( 1, ), - range: 10739..10747, + range: 0..0, }, ), tooltip: "", @@ -497,7 +499,7 @@ fn main() { file_id: FileId( 1, ), - range: 10771..10775, + range: 0..0, }, ), tooltip: "", @@ -522,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 10739..10747, + range: 0..0, }, ), tooltip: "", @@ -535,7 +537,7 @@ fn main() { file_id: FileId( 1, ), - range: 10771..10775, + range: 0..0, }, ), tooltip: "", @@ -560,7 +562,7 @@ fn main() { file_id: FileId( 1, ), - range: 10739..10747, + range: 0..0, }, ), tooltip: "", @@ -573,7 +575,7 @@ fn main() { file_id: FileId( 1, ), - range: 10771..10775, + range: 0..0, }, ), tooltip: "", @@ -598,7 +600,7 @@ fn main() { file_id: FileId( 0, ), - range: 24..30, + range: 0..0, }, ), tooltip: "", diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index aee03d218ad..2320c95b4a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -8,8 +8,9 @@ //! in this crate. // For proving that RootDatabase is RefUnwindSafe. -#![recursion_limit = "128"] #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![recursion_limit = "128"] #[allow(unused)] macro_rules! eprintln { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 2d0295692ac..f387bbf6b01 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -684,6 +684,32 @@ enum Foo { } #[test] + fn test_self() { + check( + r#" +struct S$0<T> { + t: PhantomData<T>, +} + +impl<T> S<T> { + fn new() -> Self { + Self { + t: Default::default(), + } + } +} +"#, + expect![[r#" + S Struct FileId(0) 0..38 7..8 + + FileId(0) 48..49 + FileId(0) 71..75 + FileId(0) 86..90 + "#]], + ) + } + + #[test] fn test_find_all_refs_two_modules() { check( r#" diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 2d528c64255..07cdddd15f8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -308,11 +308,7 @@ pub(crate) fn runnable_fn( sema: &Semantics<'_, RootDatabase>, def: hir::Function, ) -> Option<Runnable> { - let name = def.name(sema.db).to_smol_str(); - - let root = def.module(sema.db).krate().root_module(); - - let kind = if name == "main" && def.module(sema.db) == root { + let kind = if def.is_main(sema.db) { RunnableKind::Bin } else { let test_id = || { @@ -320,7 +316,9 @@ pub(crate) fn runnable_fn( let def: hir::ModuleDef = def.into(); def.canonical_path(sema.db) }; - canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name)) + canonical_path + .map(TestId::Path) + .unwrap_or(TestId::Name(def.name(sema.db).to_smol_str())) }; if def.is_test(sema.db) { @@ -587,6 +585,9 @@ mod tests { $0 fn main() {} +#[export_name = "main"] +fn __cortex_m_rt_main_trampoline() {} + #[test] fn test_foo() {} @@ -604,7 +605,7 @@ mod not_a_root { fn main() {} } "#, - &[TestMod, Bin, Test, Test, Test, Bench], + &[TestMod, Bin, Bin, Test, Test, Test, Bench], expect![[r#" [ Runnable { @@ -613,7 +614,7 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 0..190, + full_range: 0..253, name: "", kind: Module, }, @@ -642,8 +643,22 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 15..39, - focus_range: 26..34, + full_range: 15..76, + focus_range: 42..71, + name: "__cortex_m_rt_main_trampoline", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 78..102, + focus_range: 89..97, name: "test_foo", kind: Function, }, @@ -663,8 +678,8 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 41..92, - focus_range: 73..87, + full_range: 104..155, + focus_range: 136..150, name: "test_full_path", kind: Function, }, @@ -684,8 +699,8 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 94..128, - focus_range: 115..123, + full_range: 157..191, + focus_range: 178..186, name: "test_foo", kind: Function, }, @@ -705,8 +720,8 @@ mod not_a_root { file_id: FileId( 0, ), - full_range: 130..152, - focus_range: 142..147, + full_range: 193..215, + focus_range: 205..210, name: "bench", kind: Function, }, @@ -1655,12 +1670,18 @@ macro_rules! gen2 { } } } +macro_rules! gen_main { + () => { + fn main() {} + } +} mod tests { gen!(); } gen2!(); +gen_main!(); "#, - &[TestMod, TestMod, Test, Test, TestMod], + &[TestMod, TestMod, Test, Test, TestMod, Bin], expect![[r#" [ Runnable { @@ -1669,7 +1690,7 @@ gen2!(); file_id: FileId( 0, ), - full_range: 0..237, + full_range: 0..315, name: "", kind: Module, }, @@ -1684,8 +1705,8 @@ gen2!(); file_id: FileId( 0, ), - full_range: 202..227, - focus_range: 206..211, + full_range: 267..292, + focus_range: 271..276, name: "tests", kind: Module, description: "mod tests", @@ -1701,7 +1722,7 @@ gen2!(); file_id: FileId( 0, ), - full_range: 218..225, + full_range: 283..290, name: "foo_test", kind: Function, }, @@ -1721,7 +1742,7 @@ gen2!(); file_id: FileId( 0, ), - full_range: 228..236, + full_range: 293..301, name: "foo_test2", kind: Function, }, @@ -1741,7 +1762,7 @@ gen2!(); file_id: FileId( 0, ), - full_range: 228..236, + full_range: 293..301, name: "tests2", kind: Module, description: "mod tests2", @@ -1751,6 +1772,19 @@ gen2!(); }, cfg: None, }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 302..314, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index bb01c81d66f..dd72484b380 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -245,7 +245,7 @@ fn traverse( let mut macro_highlighter = MacroHighlighter::default(); // FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree - // an an attribute nested in a macro call will not emit `inside_attribute` + // an attribute nested in a macro call will not emit `inside_attribute` let mut inside_attribute = false; let mut inside_macro_call = false; diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index b40509715ba..d21850bcff3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -47,7 +47,7 @@ struct ExtendedTextEdit { // - typing `=` between two expressions adds `;` when in statement position // - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position // - typing `.` in a chain method call auto-indents -// - typing `{` in front of an expression inserts a closing `}` after the expression +// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression // - typing `{` in a use item adds a closing `}` in the right place // // VS Code:: diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 2f6332abd25..3802978f494 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -55,6 +55,7 @@ impl fmt::Display for RecursiveMemoryLayout { } } +#[derive(Copy, Clone)] enum FieldOrTupleIdx { Field(Field), TupleIdx(usize), @@ -71,13 +72,6 @@ impl FieldOrTupleIdx { FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(), } } - - fn index(&self) -> usize { - match *self { - FieldOrTupleIdx::Field(f) => f.index(), - FieldOrTupleIdx::TupleIdx(i) => i, - } - } } // Feature: View Memory Layout @@ -138,7 +132,10 @@ pub(crate) fn view_memory_layout( return; } - fields.sort_by_key(|(f, _)| layout.field_offset(f.index()).unwrap()); + fields.sort_by_key(|&(f, _)| match f { + FieldOrTupleIdx::Field(f) => layout.field_offset(f).unwrap_or(0), + FieldOrTupleIdx::TupleIdx(f) => layout.tuple_field_offset(f).unwrap_or(0), + }); let children_start = nodes.len(); nodes[parent_idx].children_start = children_start as i64; @@ -151,7 +148,10 @@ pub(crate) fn view_memory_layout( typename: child_ty.display(db).to_string(), size: child_layout.size(), alignment: child_layout.align(), - offset: layout.field_offset(field.index()).unwrap_or(0), + offset: match *field { + FieldOrTupleIdx::Field(f) => layout.field_offset(f).unwrap_or(0), + FieldOrTupleIdx::TupleIdx(f) => layout.tuple_field_offset(f).unwrap_or(0), + }, parent_idx: parent_idx as i64, children_start: -1, children_len: 0, diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs index 2934d26674d..d784321c7c7 100644 --- a/src/tools/rust-analyzer/crates/intern/src/lib.rs +++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs @@ -33,13 +33,10 @@ impl<T: Internable> Interned<T> { // - if not, box it up, insert it, and return a clone // This needs to be atomic (locking the shard) to avoid races with other thread, which could // insert the same object between us looking it up and inserting it. - match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, &obj) { + match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &obj) { RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, RawEntryMut::Vacant(vac) => Self { - arc: vac - .insert_hashed_nocheck(hash as u64, Arc::new(obj), SharedValue::new(())) - .0 - .clone(), + arc: vac.insert_hashed_nocheck(hash, Arc::new(obj), SharedValue::new(())).0.clone(), }, } } @@ -54,13 +51,10 @@ impl Interned<str> { // - if not, box it up, insert it, and return a clone // This needs to be atomic (locking the shard) to avoid races with other thread, which could // insert the same object between us looking it up and inserting it. - match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, s) { + match shard.raw_entry_mut().from_key_hashed_nocheck(hash, s) { RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, RawEntryMut::Vacant(vac) => Self { - arc: vac - .insert_hashed_nocheck(hash as u64, Arc::from(s), SharedValue::new(())) - .0 - .clone(), + arc: vac.insert_hashed_nocheck(hash, Arc::from(s), SharedValue::new(())).0.clone(), }, } } diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index f041ca88ac0..31b9f6c76d0 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -11,13 +11,13 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.62" +anyhow.workspace = true crossbeam-channel = "0.5.5" -itertools = "0.10.5" -tracing = "0.1.35" +itertools.workspace = true +tracing.workspace = true ide.workspace = true -ide-db.workspace =true +ide-db.workspace = true proc-macro-api.workspace = true project-model.workspace = true tt.workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 7a795dd62ab..68b592ffaa4 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -208,6 +208,7 @@ impl ProjectFolders { let entry = { let mut dirs = vfs::loader::Directories::default(); dirs.extensions.push("rs".into()); + dirs.extensions.push("toml".into()); dirs.include.extend(root.include); dirs.exclude.extend(root.exclude); for excl in global_excludes { diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 82105522ebd..adab1003d10 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -15,7 +15,7 @@ doctest = false cov-mark = "2.0.0-pre.1" rustc-hash = "1.1.0" smallvec.workspace = true -tracing = "0.1.35" +tracing.workspace = true # local deps syntax.workspace = true diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index f2d89d3efe5..908048c9904 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -119,7 +119,7 @@ enum Fragment { /// precedence. Note that this impl is different from the one currently in /// `rustc` -- `rustc` doesn't translate fragments into token trees at all. /// - /// At one point in time, we tried to to use "fake" delimiters here a-la + /// At one point in time, we tried to use "fake" delimiters here à la /// proc-macro delimiter=none. As we later discovered, "none" delimiters are /// tricky to handle in the parser, and rustc doesn't handle those either. Expr(tt::TokenTree), diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 9d886a1c979..a439c9c50d6 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -3,7 +3,7 @@ //! interface, although it contains some code to bridge `SyntaxNode`s and //! `TokenTree`s as well! //! -//! The tes for this functionality live in another crate: +//! The tests for this functionality live in another crate: //! `hir_def::macro_expansion_tests::mbe`. #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index 09e62c35278..efb326323f9 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] drop_bomb = "0.1.5" -rustc_lexer.workspace = true +rustc-dependencies.workspace = true limit.workspace = true @@ -22,3 +22,6 @@ expect-test = "1.4.0" stdx.workspace = true sourcegen.workspace = true + +[features] +in-rust-tree = ["rustc-dependencies/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs index 577eb0967b4..e38571dd3ec 100644 --- a/src/tools/rust-analyzer/crates/parser/src/event.rs +++ b/src/tools/rust-analyzer/crates/parser/src/event.rs @@ -2,11 +2,6 @@ //! It is intended to be completely decoupled from the //! parser, so as to allow to evolve the tree representation //! and the parser algorithm independently. -//! -//! The `TreeSink` trait is the bridge between the parser and the -//! tree builder: the parser produces a stream of events like -//! `start node`, `finish node`, and `FileBuilder` converts -//! this stream to a real tree. use std::mem; use crate::{ diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs index 74eae9151a2..846da28cb01 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs @@ -7,6 +7,9 @@ use super::*; // fn b(x: i32) {} // fn c(x: i32, ) {} // fn d(x: i32, y: ()) {} + +// test_err empty_param_slot +// fn f(y: i32, ,t: i32) {} pub(super) fn param_list_fn_def(p: &mut Parser<'_>) { list_(p, Flavor::FnDef); } @@ -71,7 +74,11 @@ fn list_(p: &mut Parser<'_>, flavor: Flavor) { if !p.at_ts(PARAM_FIRST.union(ATTRIBUTE_FIRST)) { p.error("expected value parameter"); m.abandon(p); - break; + if p.eat(T![,]) { + continue; + } else { + break; + } } param(p, m, flavor); if !p.at(T![,]) { diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 36c52953a02..b9e7566fdf9 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -8,8 +8,12 @@ //! Note that these tokens, unlike the tokens we feed into the parser, do //! include info about comments and whitespace. +use rustc_dependencies::lexer as rustc_lexer; + use std::ops; +use rustc_lexer::unescape::{EscapeError, Mode}; + use crate::{ SyntaxKind::{self, *}, T, @@ -253,30 +257,60 @@ impl<'a> Converter<'a> { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { err = "Missing trailing `'` symbol to terminate the character literal"; + } else { + let text = &self.res.text[self.offset + 1..][..len - 1]; + let i = text.rfind('\'').unwrap(); + let text = &text[..i]; + if let Err(e) = rustc_lexer::unescape::unescape_char(text) { + err = error_to_diagnostic_message(e, Mode::Char); + } } CHAR } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { err = "Missing trailing `'` symbol to terminate the byte literal"; + } else { + let text = &self.res.text[self.offset + 2..][..len - 2]; + let i = text.rfind('\'').unwrap(); + let text = &text[..i]; + if let Err(e) = rustc_lexer::unescape::unescape_byte(text) { + err = error_to_diagnostic_message(e, Mode::Byte); + } } + BYTE } rustc_lexer::LiteralKind::Str { terminated } => { if !terminated { err = "Missing trailing `\"` symbol to terminate the string literal"; + } else { + let text = &self.res.text[self.offset + 1..][..len - 1]; + let i = text.rfind('"').unwrap(); + let text = &text[..i]; + err = unescape_string_error_message(text, Mode::Str); } STRING } rustc_lexer::LiteralKind::ByteStr { terminated } => { if !terminated { err = "Missing trailing `\"` symbol to terminate the byte string literal"; + } else { + let text = &self.res.text[self.offset + 2..][..len - 2]; + let i = text.rfind('"').unwrap(); + let text = &text[..i]; + err = unescape_string_error_message(text, Mode::ByteStr); } BYTE_STRING } rustc_lexer::LiteralKind::CStr { terminated } => { if !terminated { err = "Missing trailing `\"` symbol to terminate the string literal"; + } else { + let text = &self.res.text[self.offset + 2..][..len - 2]; + let i = text.rfind('"').unwrap(); + let text = &text[..i]; + err = unescape_string_error_message(text, Mode::CStr); } C_STRING } @@ -304,3 +338,64 @@ impl<'a> Converter<'a> { self.push(syntax_kind, len, err); } } + +fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str { + match error { + EscapeError::ZeroChars => "empty character literal", + EscapeError::MoreThanOneChar => "character literal may only contain one codepoint", + EscapeError::LoneSlash => "", + EscapeError::InvalidEscape if mode == Mode::Byte || mode == Mode::ByteStr => { + "unknown byte escape" + } + EscapeError::InvalidEscape => "unknown character escape", + EscapeError::BareCarriageReturn => "", + EscapeError::BareCarriageReturnInRawString => "", + EscapeError::EscapeOnlyChar if mode == Mode::Byte => "byte constant must be escaped", + EscapeError::EscapeOnlyChar => "character constant must be escaped", + EscapeError::TooShortHexEscape => "numeric character escape is too short", + EscapeError::InvalidCharInHexEscape => "invalid character in numeric character escape", + EscapeError::OutOfRangeHexEscape => "out of range hex escape", + EscapeError::NoBraceInUnicodeEscape => "incorrect unicode escape sequence", + EscapeError::InvalidCharInUnicodeEscape => "invalid character in unicode escape", + EscapeError::EmptyUnicodeEscape => "empty unicode escape", + EscapeError::UnclosedUnicodeEscape => "unterminated unicode escape", + EscapeError::LeadingUnderscoreUnicodeEscape => "invalid start of unicode escape", + EscapeError::OverlongUnicodeEscape => "overlong unicode escape", + EscapeError::LoneSurrogateUnicodeEscape => "invalid unicode character escape", + EscapeError::OutOfRangeUnicodeEscape => "invalid unicode character escape", + EscapeError::UnicodeEscapeInByte => "unicode escape in byte string", + EscapeError::NonAsciiCharInByte if mode == Mode::Byte => { + "non-ASCII character in byte literal" + } + EscapeError::NonAsciiCharInByte if mode == Mode::ByteStr => { + "non-ASCII character in byte string literal" + } + EscapeError::NonAsciiCharInByte => "non-ASCII character in raw byte string literal", + EscapeError::UnskippedWhitespaceWarning => "", + EscapeError::MultipleSkippedLinesWarning => "", + } +} + +fn unescape_string_error_message(text: &str, mode: Mode) -> &'static str { + let mut error_message = ""; + match mode { + Mode::CStr => { + rustc_lexer::unescape::unescape_c_string(text, mode, &mut |_, res| { + if let Err(e) = res { + error_message = error_to_diagnostic_message(e, mode); + } + }); + } + Mode::ByteStr | Mode::Str => { + rustc_lexer::unescape::unescape_literal(text, mode, &mut |_, res| { + if let Err(e) = res { + error_message = error_to_diagnostic_message(e, mode); + } + }); + } + _ => { + // Other Modes are not supported yet or do not apply + } + } + error_message +} diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index c155e8aaf67..fcfd1a50719 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -19,6 +19,7 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] #![allow(rustdoc::private_intra_doc_links)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] mod lexed_str; mod token_set; diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs index 2c47e3d086d..57005a6834c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs @@ -32,29 +32,27 @@ impl LexedStr<'_> { let kind = self.kind(i); if kind.is_trivia() { was_joint = false + } else if kind == SyntaxKind::IDENT { + let token_text = self.text(i); + let contextual_kw = + SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT); + res.push_ident(contextual_kw); } else { - if kind == SyntaxKind::IDENT { - let token_text = self.text(i); - let contextual_kw = SyntaxKind::from_contextual_keyword(token_text) - .unwrap_or(SyntaxKind::IDENT); - res.push_ident(contextual_kw); - } else { - if was_joint { + if was_joint { + res.was_joint(); + } + res.push(kind); + // Tag the token as joint if it is float with a fractional part + // we use this jointness to inform the parser about what token split + // event to emit when we encounter a float literal in a field access + if kind == SyntaxKind::FLOAT_NUMBER { + if !self.text(i).ends_with('.') { res.was_joint(); - } - res.push(kind); - // Tag the token as joint if it is float with a fractional part - // we use this jointness to inform the parser about what token split - // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER { - if !self.text(i).ends_with('.') { - res.was_joint(); - } else { - was_joint = false; - } } else { - was_joint = true; + was_joint = false; } + } else { + was_joint = true; } } } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_char_literals.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_char_literals.rast new file mode 100644 index 00000000000..7603c9099da --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_char_literals.rast @@ -0,0 +1,86 @@ +BYTE "b''" error: empty character literal +WHITESPACE "\n" +BYTE "b'\\'" error: Missing trailing `'` symbol to terminate the byte literal +WHITESPACE "\n" +BYTE "b'\n'" error: byte constant must be escaped +WHITESPACE "\n" +BYTE "b'spam'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\x0ff'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\\"a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\na'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\ra'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\ta'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\\\a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\'a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\0a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\u{0}x'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{1F63b}}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\v'" error: unknown byte escape +WHITESPACE "\n" +BYTE "b'\\💩'" error: unknown byte escape +WHITESPACE "\n" +BYTE "b'\\●'" error: unknown byte escape +WHITESPACE "\n" +BYTE "b'\\\\\\r'" error: character literal may only contain one codepoint +WHITESPACE "\n" +BYTE "b'\\x'" error: numeric character escape is too short +WHITESPACE "\n" +BYTE "b'\\x0'" error: numeric character escape is too short +WHITESPACE "\n" +BYTE "b'\\xf'" error: numeric character escape is too short +WHITESPACE "\n" +BYTE "b'\\xa'" error: numeric character escape is too short +WHITESPACE "\n" +BYTE "b'\\xx'" error: invalid character in numeric character escape +WHITESPACE "\n" +BYTE "b'\\xы'" error: invalid character in numeric character escape +WHITESPACE "\n" +BYTE "b'\\x🦀'" error: invalid character in numeric character escape +WHITESPACE "\n" +BYTE "b'\\xtt'" error: invalid character in numeric character escape +WHITESPACE "\n" +BYTE "b'\\u'" error: incorrect unicode escape sequence +WHITESPACE "\n" +BYTE "b'\\u[0123]'" error: incorrect unicode escape sequence +WHITESPACE "\n" +BYTE "b'\\u{0x}'" error: invalid character in unicode escape +WHITESPACE "\n" +BYTE "b'\\u{'" error: unterminated unicode escape +WHITESPACE "\n" +BYTE "b'\\u{0000'" error: unterminated unicode escape +WHITESPACE "\n" +BYTE "b'\\u{}'" error: empty unicode escape +WHITESPACE "\n" +BYTE "b'\\u{_0000}'" error: invalid start of unicode escape +WHITESPACE "\n" +BYTE "b'\\u{0000000}'" error: overlong unicode escape +WHITESPACE "\n" +BYTE "b'\\u{FFFFFF}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{ffffff}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{ffffff}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{DC00}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{DDDD}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{DFFF}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{D800}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{DAAA}'" error: unicode escape in byte string +WHITESPACE "\n" +BYTE "b'\\u{DBFF}'" error: unicode escape in byte string +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_char_literals.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_char_literals.rs new file mode 100644 index 00000000000..b2d06e490bd --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_char_literals.rs @@ -0,0 +1,44 @@ +b'' +b'\' +b' +' +b'spam' +b'\x0ff' +b'\"a' +b'\na' +b'\ra' +b'\ta' +b'\\a' +b'\'a' +b'\0a' +b'\u{0}x' +b'\u{1F63b}}' +b'\v' +b'\💩' +b'\●' +b'\\\r' +b'\x' +b'\x0' +b'\xf' +b'\xa' +b'\xx' +b'\xы' +b'\x🦀' +b'\xtt' +b'\u' +b'\u[0123]' +b'\u{0x}' +b'\u{' +b'\u{0000' +b'\u{}' +b'\u{_0000}' +b'\u{0000000}' +b'\u{FFFFFF}' +b'\u{ffffff}' +b'\u{ffffff}' +b'\u{DC00}' +b'\u{DDDD}' +b'\u{DFFF}' +b'\u{D800}' +b'\u{DAAA}' +b'\u{DBFF}' diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_strings.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_strings.rast new file mode 100644 index 00000000000..e8d8ff8cefb --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_strings.rast @@ -0,0 +1,28 @@ +BYTE_STRING "b\"\\💩\"" error: unknown byte escape +WHITESPACE "\n" +BYTE_STRING "b\"\\●\"" error: unknown byte escape +WHITESPACE "\n" +BYTE_STRING "b\"\\u{_0000}\"" error: invalid start of unicode escape +WHITESPACE "\n" +BYTE_STRING "b\"\\u{0000000}\"" error: overlong unicode escape +WHITESPACE "\n" +BYTE_STRING "b\"\\u{FFFFFF}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{ffffff}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{ffffff}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{DC00}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{DDDD}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{DFFF}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{D800}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{DAAA}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\u{DBFF}\"" error: unicode escape in byte string +WHITESPACE "\n" +BYTE_STRING "b\"\\xы\"" error: invalid character in numeric character escape +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_strings.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_strings.rs new file mode 100644 index 00000000000..e74847137b1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/byte_strings.rs @@ -0,0 +1,14 @@ +b"\💩" +b"\●" +b"\u{_0000}" +b"\u{0000000}" +b"\u{FFFFFF}" +b"\u{ffffff}" +b"\u{ffffff}" +b"\u{DC00}" +b"\u{DDDD}" +b"\u{DFFF}" +b"\u{D800}" +b"\u{DAAA}" +b"\u{DBFF}" +b"\xы" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/c_strings.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/c_strings.rast new file mode 100644 index 00000000000..1b4424ba5c7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/c_strings.rast @@ -0,0 +1,28 @@ +C_STRING "c\"\\💩\"" error: unknown character escape +WHITESPACE "\n" +C_STRING "c\"\\●\"" error: unknown character escape +WHITESPACE "\n" +C_STRING "c\"\\u{_0000}\"" error: invalid start of unicode escape +WHITESPACE "\n" +C_STRING "c\"\\u{0000000}\"" error: overlong unicode escape +WHITESPACE "\n" +C_STRING "c\"\\u{FFFFFF}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{ffffff}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{ffffff}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{DC00}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{DDDD}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{DFFF}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{D800}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{DAAA}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\u{DBFF}\"" error: invalid unicode character escape +WHITESPACE "\n" +C_STRING "c\"\\xы\"" error: invalid character in numeric character escape +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/c_strings.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/c_strings.rs new file mode 100644 index 00000000000..1b78ffc28a0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/c_strings.rs @@ -0,0 +1,14 @@ +c"\💩" +c"\●" +c"\u{_0000}" +c"\u{0000000}" +c"\u{FFFFFF}" +c"\u{ffffff}" +c"\u{ffffff}" +c"\u{DC00}" +c"\u{DDDD}" +c"\u{DFFF}" +c"\u{D800}" +c"\u{DAAA}" +c"\u{DBFF}" +c"\xы" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/char_literals.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/char_literals.rast new file mode 100644 index 00000000000..b1e1364d4c2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/char_literals.rast @@ -0,0 +1,92 @@ +CHAR "'hello'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "''" error: empty character literal +WHITESPACE "\n" +CHAR "'\n'" error: character constant must be escaped +WHITESPACE "\n" +CHAR "'spam'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\x0ff'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\\"a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\na'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\ra'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\ta'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\\\a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\'a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\0a'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\u{0}x'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\u{1F63b}}'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\v'" error: unknown character escape +WHITESPACE "\n" +CHAR "'\\💩'" error: unknown character escape +WHITESPACE "\n" +CHAR "'\\●'" error: unknown character escape +WHITESPACE "\n" +CHAR "'\\\\\\r'" error: character literal may only contain one codepoint +WHITESPACE "\n" +CHAR "'\\x'" error: numeric character escape is too short +WHITESPACE "\n" +CHAR "'\\x0'" error: numeric character escape is too short +WHITESPACE "\n" +CHAR "'\\xf'" error: numeric character escape is too short +WHITESPACE "\n" +CHAR "'\\xa'" error: numeric character escape is too short +WHITESPACE "\n" +CHAR "'\\xx'" error: invalid character in numeric character escape +WHITESPACE "\n" +CHAR "'\\xы'" error: invalid character in numeric character escape +WHITESPACE "\n" +CHAR "'\\x🦀'" error: invalid character in numeric character escape +WHITESPACE "\n" +CHAR "'\\xtt'" error: invalid character in numeric character escape +WHITESPACE "\n" +CHAR "'\\xff'" error: out of range hex escape +WHITESPACE "\n" +CHAR "'\\xFF'" error: out of range hex escape +WHITESPACE "\n" +CHAR "'\\x80'" error: out of range hex escape +WHITESPACE "\n" +CHAR "'\\u'" error: incorrect unicode escape sequence +WHITESPACE "\n" +CHAR "'\\u[0123]'" error: incorrect unicode escape sequence +WHITESPACE "\n" +CHAR "'\\u{0x}'" error: invalid character in unicode escape +WHITESPACE "\n" +CHAR "'\\u{'" error: unterminated unicode escape +WHITESPACE "\n" +CHAR "'\\u{0000'" error: unterminated unicode escape +WHITESPACE "\n" +CHAR "'\\u{}'" error: empty unicode escape +WHITESPACE "\n" +CHAR "'\\u{_0000}'" error: invalid start of unicode escape +WHITESPACE "\n" +CHAR "'\\u{0000000}'" error: overlong unicode escape +WHITESPACE "\n" +CHAR "'\\u{FFFFFF}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{ffffff}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{ffffff}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{DC00}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{DDDD}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{DFFF}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{D800}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{DAAA}'" error: invalid unicode character escape +WHITESPACE "\n" +CHAR "'\\u{DBFF}'" error: invalid unicode character escape +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/char_literals.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/char_literals.rs new file mode 100644 index 00000000000..291f99d8020 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/char_literals.rs @@ -0,0 +1,47 @@ +'hello' +'' +' +' +'spam' +'\x0ff' +'\"a' +'\na' +'\ra' +'\ta' +'\\a' +'\'a' +'\0a' +'\u{0}x' +'\u{1F63b}}' +'\v' +'\💩' +'\●' +'\\\r' +'\x' +'\x0' +'\xf' +'\xa' +'\xx' +'\xы' +'\x🦀' +'\xtt' +'\xff' +'\xFF' +'\x80' +'\u' +'\u[0123]' +'\u{0x}' +'\u{' +'\u{0000' +'\u{}' +'\u{_0000}' +'\u{0000000}' +'\u{FFFFFF}' +'\u{ffffff}' +'\u{ffffff}' +'\u{DC00}' +'\u{DDDD}' +'\u{DFFF}' +'\u{D800}' +'\u{DAAA}' +'\u{DBFF}' diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/strings.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/strings.rast new file mode 100644 index 00000000000..0cd1747208e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/strings.rast @@ -0,0 +1,28 @@ +STRING "\"\\💩\"" error: unknown character escape +WHITESPACE "\n" +STRING "\"\\●\"" error: unknown character escape +WHITESPACE "\n" +STRING "\"\\u{_0000}\"" error: invalid start of unicode escape +WHITESPACE "\n" +STRING "\"\\u{0000000}\"" error: overlong unicode escape +WHITESPACE "\n" +STRING "\"\\u{FFFFFF}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{ffffff}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{ffffff}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{DC00}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{DDDD}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{DFFF}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{D800}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{DAAA}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\u{DBFF}\"" error: invalid unicode character escape +WHITESPACE "\n" +STRING "\"\\xы\"" error: invalid character in numeric character escape +WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/strings.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/strings.rs new file mode 100644 index 00000000000..2499516d3fa --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/strings.rs @@ -0,0 +1,14 @@ +"\💩" +"\●" +"\u{_0000}" +"\u{0000000}" +"\u{FFFFFF}" +"\u{ffffff}" +"\u{ffffff}" +"\u{DC00}" +"\u{DDDD}" +"\u{DFFF}" +"\u{D800}" +"\u{DAAA}" +"\u{DBFF}" +"\xы" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rast index c848ac368e4..fd20ca57ac6 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rast @@ -1,13 +1,9 @@ -BYTE "b''" -WHITESPACE " " BYTE "b'x'" WHITESPACE " " BYTE_STRING "b\"foo\"" WHITESPACE " " BYTE_STRING "br\"\"" WHITESPACE "\n" -BYTE "b''suf" -WHITESPACE " " BYTE_STRING "b\"\"ix" WHITESPACE " " BYTE_STRING "br\"\"br" @@ -17,6 +13,4 @@ WHITESPACE " " BYTE "b'\\\\'" WHITESPACE " " BYTE "b'\\''" -WHITESPACE " " -BYTE "b'hello'" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rs index b54930f5e69..65460d02cb2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/byte_strings.rs @@ -1,3 +1,3 @@ -b'' b'x' b"foo" br"" -b''suf b""ix br""br -b'\n' b'\\' b'\'' b'hello' +b'x' b"foo" br"" +b""ix br""br +b'\n' b'\\' b'\'' diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rast index 66e58cc298f..07172a4ecc0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rast @@ -4,8 +4,6 @@ CHAR "' '" WHITESPACE " " CHAR "'0'" WHITESPACE " " -CHAR "'hello'" -WHITESPACE " " CHAR "'\\x7f'" WHITESPACE " " CHAR "'\\n'" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rs index 454ee0a5f61..15f52c113c1 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/chars.rs @@ -1 +1 @@ -'x' ' ' '0' 'hello' '\x7f' '\n' '\\' '\'' +'x' ' ' '0' '\x7f' '\n' '\\' '\'' diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rast new file mode 100644 index 00000000000..39e35a81ee2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rast @@ -0,0 +1,41 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + PARAM + IDENT_PAT + NAME + IDENT "y" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + COMMA "," + WHITESPACE " " + COMMA "," + PARAM + IDENT_PAT + NAME + IDENT "t" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 12: expected value parameter diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rs new file mode 100644 index 00000000000..0adf7b8d2f0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rs @@ -0,0 +1 @@ +fn f(y: i32, ,t: i32) {} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 4229f289130..2c2d2e8a945 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -21,7 +21,7 @@ object = { version = "0.32.0", default-features = false, features = [ ] } serde.workspace = true serde_json = { workspace = true, features = ["unbounded_depth"] } -tracing = "0.1.37" +tracing.workspace = true triomphe.workspace = true memmap2 = "0.5.4" snap = "1.1.0" diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml index 77b4afd7d7e..12d7c07d3ed 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-test/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [build-dependencies] -cargo_metadata = "0.15.0" +cargo_metadata.workspace = true proc-macro-test-impl = { path = "imp", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 937834a82ae..56ce9d11c08 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -14,8 +14,8 @@ doctest = false [dependencies] once_cell = "1.17.0" cfg-if = "1.0.0" -libc = "0.2.135" la-arena.workspace = true +libc.workspace = true countme = { version = "3.0.1", features = ["enable"] } jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true } diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 75977fc5b04..3e48de6456b 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -12,16 +12,16 @@ rust-version.workspace = true doctest = false [dependencies] -tracing = "0.1.35" +anyhow.workspace = true +cargo_metadata.workspace = true rustc-hash = "1.1.0" -cargo_metadata = "0.15.0" semver = "1.0.14" serde_json.workspace = true serde.workspace = true +tracing.workspace = true triomphe.workspace = true -anyhow = "1.0.62" la-arena.workspace = true -itertools = "0.10.5" +itertools.workspace = true # local deps base-db.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index fb0f3ab7d17..68cd40c040b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -73,6 +73,10 @@ impl WorkspaceBuildScripts { cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&config.extra_args); + if let Some(target_dir) = &config.target_dir { + cmd.arg("--target-dir").arg(target_dir); + } + // --all-targets includes tests, benches and examples in addition to the // default lib and bins. This is an independent concept from the --target // flag below. diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index e47808a2cc9..ca3d6e0596c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -96,6 +96,8 @@ pub struct CargoConfig { pub extra_env: FxHashMap<String, String>, pub invocation_strategy: InvocationStrategy, pub invocation_location: InvocationLocation, + /// Optional path to use instead of `target` when building + pub target_dir: Option<PathBuf>, } pub type Package = Idx<PackageData>; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 7410f0a3a66..76f76446032 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -19,30 +19,26 @@ name = "rust-analyzer" path = "src/bin/main.rs" [dependencies] -anyhow = "1.0.62" +anyhow.workspace = true crossbeam-channel = "0.5.5" -dissimilar = "1.0.4" -itertools = "0.10.5" -scip = "0.1.1" +dissimilar.workspace = true +itertools.workspace = true +scip = "0.3.1" lsp-types = { version = "=0.94.0", features = ["proposed"] } parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" +rayon.workspace = true rustc-hash = "1.1.0" serde_json = { workspace = true, features = ["preserve_order"] } serde.workspace = true -rayon = "1.6.1" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } lsp-server.workspace = true -tracing = "0.1.35" -tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "registry", - "fmt", - "tracing-log", -] } -tracing-log = "0.1.3" -tracing-tree = "0.2.1" +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-log = "0.2.0" +tracing-tree.workspace = true triomphe.workspace = true nohash-hasher.workspace = true always-assert = "0.1.2" @@ -57,6 +53,7 @@ flycheck.workspace = true hir-def.workspace = true hir-ty.workspace = true hir.workspace = true +rustc-dependencies.workspace = true ide-db.workspace = true # This should only be used in CLI ide-ssr.workspace = true @@ -67,6 +64,7 @@ profile.workspace = true project-model.workspace = true stdx.workspace = true syntax.workspace = true +parser.workspace = true toolchain.workspace = true vfs-notify.workspace = true vfs.workspace = true @@ -79,7 +77,7 @@ jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = tr [dev-dependencies] expect-test = "1.4.0" -xshell = "0.2.2" +xshell.workspace = true test-utils.workspace = true sourcegen.workspace = true @@ -89,4 +87,13 @@ mbe.workspace = true jemalloc = ["jemallocator", "profile/jemalloc"] force-always-assert = ["always-assert/force"] sysroot-abi = [] -in-rust-tree = ["sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree"] +in-rust-tree = [ + "sysroot-abi", + "ide/in-rust-tree", + "syntax/in-rust-tree", + "parser/in-rust-tree", + "rustc-dependencies/in-rust-tree", + "hir/in-rust-tree", + "hir-def/in-rust-tree", + "hir-ty/in-rust-tree", +] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 2fa14fc7e28..a7d0a0b0dfc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -3,6 +3,10 @@ //! Based on cli flags, either spawns an LSP server, or runs a batch analysis #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#[cfg(feature = "in-rust-tree")] +#[allow(unused_extern_crates)] +extern crate rustc_driver; mod logger; mod rustc_wrapper; @@ -190,6 +194,12 @@ fn run_server() -> anyhow::Result<()> { } }; + let mut is_visual_studio_code = false; + if let Some(client_info) = client_info { + tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); + is_visual_studio_code = client_info.name.starts_with("Visual Studio Code"); + } + let workspace_roots = workspace_folders .map(|workspaces| { workspaces @@ -201,7 +211,7 @@ fn run_server() -> anyhow::Result<()> { }) .filter(|workspaces| !workspaces.is_empty()) .unwrap_or_else(|| vec![root_path.clone()]); - let mut config = Config::new(root_path, capabilities, workspace_roots); + let mut config = Config::new(root_path, capabilities, workspace_roots, is_visual_studio_code); if let Some(json) = initialization_options { if let Err(e) = config.update(json) { use lsp_types::{ @@ -231,10 +241,6 @@ fn run_server() -> anyhow::Result<()> { connection.initialize_finish(initialize_id, initialize_result)?; - if let Some(client_info) = client_info { - tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); - } - if !config.has_linked_projects() && config.detached_files().is_empty() { config.rediscover_workspaces(); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index dcb3ca6581c..0f6539f224d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -762,7 +762,8 @@ impl flags::AnalysisStats { group: true, skip_glob_imports: true, }, - prefer_no_std: Default::default(), + prefer_no_std: false, + prefer_prelude: true, }, ide::AssistResolveStrategy::All, file_id, @@ -846,9 +847,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa Err(SyntheticSyntax) => return "synthetic,,".to_string(), }; let root = db.parse_or_expand(src.file_id); - let node = src.map(|e| { - e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone()) - }); + let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range(db); let path = vfs.file_path(original_range.file_id); let line_index = db.line_index(original_range.file_id); @@ -888,12 +887,7 @@ fn pat_syntax_range( let src = sm.pat_syntax(pat_id); if let Ok(src) = src { let root = db.parse_or_expand(src.file_id); - let node = src.map(|e| { - e.either( - |it| it.to_node(&root).syntax().clone(), - |it| it.to_node(&root).syntax().clone(), - ) - }); + let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range(db); let path = vfs.file_path(original_range.file_id); let line_index = db.line_index(original_range.file_id); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 419440b6df7..fe5022f8606 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -131,6 +131,9 @@ xflags::xflags! { /// The output path where the SCIP file will be written to. Defaults to `index.scip`. optional --output path: PathBuf + + /// A path to an json configuration file that can be used to customize cargo behavior. + optional --config-path config_path: PathBuf } } } @@ -239,6 +242,7 @@ pub struct Scip { pub path: PathBuf, pub output: Option<PathBuf>, + pub config_path: Option<PathBuf>, } impl RustAnalyzer { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 8c056fff000..30e11402cd8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -11,10 +11,8 @@ use ide::{ TokenStaticData, }; use ide_db::LineIndexDatabase; -use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; -use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource}; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use scip::types as scip_types; -use std::env; use crate::{ cli::flags, @@ -25,8 +23,6 @@ impl flags::Scip { pub fn run(self) -> anyhow::Result<()> { eprintln!("Generating SCIP start..."); let now = Instant::now(); - let mut cargo_config = CargoConfig::default(); - cargo_config.sysroot = Some(RustLibSource::Discover); let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}")); let load_cargo_config = LoadCargoConfig { @@ -34,14 +30,27 @@ impl flags::Scip { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, }; - let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path)); - let rootpath = path.normalize(); - let manifest = ProjectManifest::discover_single(&path)?; + let root = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&self.path)).normalize(); - let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; + let mut config = crate::config::Config::new( + root.clone(), + lsp_types::ClientCapabilities::default(), + /* workspace_roots = */ vec![], + /* is_visual_studio_code = */ false, + ); - let (host, vfs, _) = - load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; + if let Some(p) = self.config_path { + let mut file = std::io::BufReader::new(std::fs::File::open(p)?); + let json = serde_json::from_reader(&mut file)?; + config.update(json)?; + } + let cargo_config = config.cargo(); + let (host, vfs, _) = load_workspace_at( + root.as_path().as_ref(), + &cargo_config, + &load_cargo_config, + &no_progress, + )?; let db = host.raw_database(); let analysis = host.analysis(); @@ -58,8 +67,7 @@ impl flags::Scip { .into(), project_root: format!( "file://{}", - path.normalize() - .as_os_str() + root.as_os_str() .to_str() .ok_or(anyhow::format_err!("Unable to normalize project_root path"))? ), @@ -80,7 +88,7 @@ impl flags::Scip { new_symbol }; - let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) { + let relative_path = match get_relative_filepath(&vfs, &root, file_id) { Some(relative_path) => relative_path, None => continue, }; @@ -125,6 +133,10 @@ impl flags::Scip { documentation: documentation.unwrap_or_default(), relationships: Vec::new(), special_fields: Default::default(), + kind: Default::default(), + display_name: String::new(), + signature_documentation: Default::default(), + enclosing_symbol: String::new(), }; symbols.push(symbol_info) @@ -139,6 +151,7 @@ impl flags::Scip { syntax_kind: Default::default(), diagnostics: Vec::new(), special_fields: Default::default(), + enclosing_range: Vec::new(), }); }); @@ -152,6 +165,7 @@ impl flags::Scip { occurrences, symbols, special_fields: Default::default(), + text: String::new(), }); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index ea3a21241cb..f28f6ffb874 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -91,6 +91,12 @@ config_data! { /// and should therefore include `--message-format=json` or a similar /// option. /// + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and + /// `#rust-analyzer.cargo.buildScripts.invocationLocation#`. + /// /// By default, a cargo invocation will be constructed for the configured /// targets and features, with the following base command line: /// @@ -182,9 +188,11 @@ config_data! { /// Cargo, you might also want to change /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. /// - /// If there are multiple linked projects, this command is invoked for - /// each of them, with the working directory being the project root - /// (i.e., the folder containing the `Cargo.toml`). + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.cargo.check.invocationStrategy#` and + /// `#rust-analyzer.cargo.check.invocationLocation#`. /// /// An example command would be: /// @@ -209,6 +217,8 @@ config_data! { completion_autoself_enable: bool = "true", /// Whether to add parenthesis and argument snippets when completing function. completion_callable_snippets: CallableCompletionDef = "\"fill_arguments\"", + /// Whether to show full function/method signatures in completion docs. + completion_fullFunctionSignatures_enable: bool = "false", /// Maximum number of completions to return. If `None`, the limit is infinite. completion_limit: Option<usize> = "null", /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. @@ -342,7 +352,9 @@ config_data! { /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. imports_merge_glob: bool = "true", /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. - imports_prefer_no_std: bool = "false", + imports_preferNoStd | imports_prefer_no_std: bool = "false", + /// Whether to prefer import paths containing a `prelude` module. + imports_preferPrelude: bool = "false", /// The path structure for newly inserted paths to use. imports_prefix: ImportPrefixDef = "\"plain\"", @@ -470,6 +482,14 @@ config_data! { /// tests or binaries. For example, it may be `--release`. runnables_extraArgs: Vec<String> = "[]", + /// Optional path to a rust-analyzer specific target directory. + /// This prevents rust-analyzer's `cargo check` from locking the `Cargo.lock` + /// at the expense of duplicating build artifacts. + /// + /// Set to `true` to use a subdirectory of the existing target directory or + /// set to a path relative to the workspace to use that path. + rust_analyzerTargetDir: Option<TargetDirectory> = "null", + /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private /// projects, or "discover" to try to automatically find it if the `rustc-dev` component /// is installed. @@ -565,6 +585,7 @@ pub struct Config { data: ConfigData, detached_files: Vec<AbsPathBuf>, snippets: Vec<Snippet>, + is_visual_studio_code: bool, } type ParallelCachePrimingNumThreads = u8; @@ -755,11 +776,14 @@ impl fmt::Display for ConfigError { } } +impl std::error::Error for ConfigError {} + impl Config { pub fn new( root_path: AbsPathBuf, caps: ClientCapabilities, workspace_roots: Vec<AbsPathBuf>, + is_visual_studio_code: bool, ) -> Self { Config { caps, @@ -769,6 +793,7 @@ impl Config { root_path, snippets: Default::default(), workspace_roots, + is_visual_studio_code, } } @@ -1094,7 +1119,8 @@ impl Config { ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, }, insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_prefer_no_std, + prefer_no_std: self.data.imports_preferNoStd, + prefer_prelude: self.data.imports_preferPrelude, } } @@ -1248,6 +1274,7 @@ impl Config { run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), extra_args: self.data.cargo_extraArgs.clone(), extra_env: self.data.cargo_extraEnv.clone(), + target_dir: self.target_dir_from_config(), } } @@ -1320,10 +1347,21 @@ impl Config { extra_args: self.check_extra_args(), extra_env: self.check_extra_env(), ansi_color_output: self.color_diagnostic_output(), + target_dir: self.target_dir_from_config(), }, } } + fn target_dir_from_config(&self) -> Option<PathBuf> { + self.data.rust_analyzerTargetDir.as_ref().and_then(|target_dir| match target_dir { + TargetDirectory::UseSubdirectory(yes) if *yes => { + Some(PathBuf::from("target/rust-analyzer")) + } + TargetDirectory::UseSubdirectory(_) => None, + TargetDirectory::Directory(dir) => Some(dir.clone()), + }) + } + pub fn check_on_save(&self) -> bool { self.data.checkOnSave } @@ -1444,13 +1482,15 @@ impl Config { && completion_item_edit_resolve(&self.caps), enable_self_on_the_fly: self.data.completion_autoself_enable, enable_private_editable: self.data.completion_privateEditable_enable, + full_function_signatures: self.data.completion_fullFunctionSignatures_enable, callable: match self.data.completion_callable_snippets { CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, }, insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_prefer_no_std, + prefer_no_std: self.data.imports_preferNoStd, + prefer_prelude: self.data.imports_preferPrelude, snippet_cap: SnippetCap::new(try_or_def!( self.caps .text_document @@ -1479,7 +1519,8 @@ impl Config { snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), allowed: None, insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_prefer_no_std, + prefer_no_std: self.data.imports_preferNoStd, + prefer_prelude: self.data.imports_preferPrelude, assist_emit_must_use: self.data.assist_emitMustUse, } } @@ -1667,6 +1708,12 @@ impl Config { pub fn typing_autoclose_angle(&self) -> bool { self.data.typing_autoClosingAngleBrackets_enable } + + // FIXME: VSCode seems to work wrong sometimes, see https://github.com/microsoft/vscode/issues/193124 + // hence, distinguish it for now. + pub fn is_visual_studio_code(&self) -> bool { + self.is_visual_studio_code + } } // Deserialization definitions @@ -2015,6 +2062,14 @@ pub enum MemoryLayoutHoverRenderKindDef { Both, } +#[derive(Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "snake_case")] +#[serde(untagged)] +pub enum TargetDirectory { + UseSubdirectory(bool), + Directory(PathBuf), +} + macro_rules! _config_data { (struct $name:ident { $( @@ -2443,6 +2498,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, ], }, + "Option<TargetDirectory>" => set! { + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "string" + }, + ], + }, _ => panic!("missing entry for {ty}: {default}"), } @@ -2555,8 +2623,12 @@ mod tests { #[test] fn proc_macro_srv_null() { - let mut config = - Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]); + let mut config = Config::new( + AbsPathBuf::try_from(project_root()).unwrap(), + Default::default(), + vec![], + false, + ); config .update(serde_json::json!({ "procMacro_server": null, @@ -2567,8 +2639,12 @@ mod tests { #[test] fn proc_macro_srv_abs() { - let mut config = - Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]); + let mut config = Config::new( + AbsPathBuf::try_from(project_root()).unwrap(), + Default::default(), + vec![], + false, + ); config .update(serde_json::json!({ "procMacro": {"server": project_root().display().to_string()} @@ -2579,8 +2655,12 @@ mod tests { #[test] fn proc_macro_srv_rel() { - let mut config = - Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]); + let mut config = Config::new( + AbsPathBuf::try_from(project_root()).unwrap(), + Default::default(), + vec![], + false, + ); config .update(serde_json::json!({ "procMacro": {"server": "./server"} @@ -2591,4 +2671,67 @@ mod tests { Some(AbsPathBuf::try_from(project_root().join("./server")).unwrap()) ); } + + #[test] + fn cargo_target_dir_unset() { + let mut config = Config::new( + AbsPathBuf::try_from(project_root()).unwrap(), + Default::default(), + vec![], + false, + ); + config + .update(serde_json::json!({ + "rust": { "analyzerTargetDir": null } + })) + .unwrap(); + assert_eq!(config.data.rust_analyzerTargetDir, None); + assert!( + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == None) + ); + } + + #[test] + fn cargo_target_dir_subdir() { + let mut config = Config::new( + AbsPathBuf::try_from(project_root()).unwrap(), + Default::default(), + vec![], + false, + ); + config + .update(serde_json::json!({ + "rust": { "analyzerTargetDir": true } + })) + .unwrap(); + assert_eq!( + config.data.rust_analyzerTargetDir, + Some(TargetDirectory::UseSubdirectory(true)) + ); + assert!( + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer"))) + ); + } + + #[test] + fn cargo_target_dir_relative_dir() { + let mut config = Config::new( + AbsPathBuf::try_from(project_root()).unwrap(), + Default::default(), + vec![], + false, + ); + config + .update(serde_json::json!({ + "rust": { "analyzerTargetDir": "other_folder" } + })) + .unwrap(); + assert_eq!( + config.data.rust_analyzerTargetDir, + Some(TargetDirectory::Directory(PathBuf::from("other_folder"))) + ); + assert!( + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("other_folder"))) + ); + } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index 731580557c2..f8bc66ff8e7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -538,7 +538,12 @@ mod tests { let (sender, _) = crossbeam_channel::unbounded(); let state = GlobalState::new( sender, - Config::new(workspace_root.to_path_buf(), ClientCapabilities::default(), Vec::new()), + Config::new( + workspace_root.to_path_buf(), + ClientCapabilities::default(), + Vec::new(), + false, + ), ); let snap = state.snapshot(); let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index b8a1a39be19..06c27332d44 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -4,6 +4,7 @@ use std::{ fs, io::Write as _, + path::PathBuf, process::{self, Stdio}, }; @@ -1995,7 +1996,25 @@ fn run_rustfmt( cmd } RustfmtConfig::CustomCommand { command, args } => { - let mut cmd = process::Command::new(command); + let cmd = PathBuf::from(&command); + let workspace = CargoTargetSpec::for_file(&snap, file_id)?; + let mut cmd = match workspace { + Some(spec) => { + // approach: if the command name contains a path separator, join it with the workspace root. + // however, if the path is absolute, joining will result in the absolute path being preserved. + // as a fallback, rely on $PATH-based discovery. + let cmd_path = + if cfg!(windows) && command.contains(&[std::path::MAIN_SEPARATOR, '/']) { + spec.workspace_root.join(cmd).into() + } else if command.contains(std::path::MAIN_SEPARATOR) { + spec.workspace_root.join(cmd).into() + } else { + cmd + }; + process::Command::new(cmd_path) + } + None => process::Command::new(cmd), + }; cmd.envs(snap.config.extra_env()); cmd.args(args); @@ -2003,6 +2022,8 @@ fn run_rustfmt( } }; + tracing::debug!(?command, "created format command"); + // try to chdir to the file so we can respect `rustfmt.toml` // FIXME: use `rustfmt --config-path` once // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 5a11012b93c..b7e839ac2a5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -134,6 +134,7 @@ fn integrated_completion_benchmark() { enable_imports_on_the_fly: true, enable_self_on_the_fly: true, enable_private_editable: true, + full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { @@ -145,6 +146,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = @@ -173,6 +175,7 @@ fn integrated_completion_benchmark() { enable_imports_on_the_fly: true, enable_self_on_the_fly: true, enable_private_editable: true, + full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { @@ -184,6 +187,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 23074493aee..aca91570f7c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -443,15 +443,17 @@ pub(crate) fn inlay_hint( file_id: FileId, inlay_hint: InlayHint, ) -> Cancellable<lsp_types::InlayHint> { + let is_visual_studio_code = snap.config.is_visual_studio_code(); let needs_resolve = inlay_hint.needs_resolve; let (label, tooltip, mut something_to_resolve) = inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; - let text_edits = if needs_resolve && fields_to_resolve.resolve_text_edits { - something_to_resolve |= inlay_hint.text_edit.is_some(); - None - } else { - inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) - }; + let text_edits = + if !is_visual_studio_code && needs_resolve && fields_to_resolve.resolve_text_edits { + something_to_resolve |= inlay_hint.text_edit.is_some(); + None + } else { + inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + }; let data = if needs_resolve && something_to_resolve { Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap()) } else { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 3fae08b82e2..8dba83ed5ed 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -380,7 +380,6 @@ impl GlobalState { ws }) .collect::<Vec<_>>(); - // Workspaces are the same, but we've updated build data. self.workspaces = Arc::new(workspaces); } else { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index e49b5768fa4..106b99cb935 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -150,6 +150,7 @@ impl Project<'_> { ..Default::default() }, roots, + false, ); config.update(self.config).expect("invalid config"); config.rediscover_workspaces(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs index 8b5c92c6602..56b5fcef3c2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -157,7 +157,6 @@ Apache-2.0 OR MIT Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT Apache-2.0/MIT BSD-3-Clause -BlueOak-1.0.0 OR MIT OR Apache-2.0 CC0-1.0 ISC MIT @@ -300,6 +299,8 @@ fn check_test_attrs(path: &Path, text: &str) { // This file. "slow-tests/tidy.rs", "test-utils/src/fixture.rs", + // Generated code from lints contains doc tests in string literals. + "ide-db/src/generated/lints.rs", ]; if text.contains("#[should_panic") && !need_panic.iter().any(|p| path.ends_with(p)) { panic!( diff --git a/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml b/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml new file mode 100644 index 00000000000..ba36fb0b044 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rustc-dependencies" +version = "0.0.0" +description = "TBD" + +rust-version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ra-ap-rustc_lexer = { version = "0.19.0" } +ra-ap-rustc_parse_format = { version = "0.14.0", default-features = false } +ra-ap-rustc_index = { version = "0.19.0", default-features = false } +ra-ap-rustc_abi = { version = "0.19.0", default-features = false } + +[features] +in-rust-tree = [] diff --git a/src/tools/rust-analyzer/crates/rustc-dependencies/src/lib.rs b/src/tools/rust-analyzer/crates/rustc-dependencies/src/lib.rs new file mode 100644 index 00000000000..13fcbc49193 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rustc-dependencies/src/lib.rs @@ -0,0 +1,48 @@ +//! A wrapper around rustc internal crates, which enables switching between compiler provided +//! ones and stable ones published in crates.io + +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + +pub mod lexer { + #[cfg(not(feature = "in-rust-tree"))] + pub use ::ra_ap_rustc_lexer::*; + + #[cfg(feature = "in-rust-tree")] + pub use ::rustc_lexer::*; +} + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_parse_format; + +pub mod parse_format { + #[cfg(not(feature = "in-rust-tree"))] + pub use ::ra_ap_rustc_parse_format::*; + + #[cfg(feature = "in-rust-tree")] + pub use ::rustc_parse_format::*; +} + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_abi; + +pub mod abi { + #[cfg(not(feature = "in-rust-tree"))] + pub use ::ra_ap_rustc_abi::*; + + #[cfg(feature = "in-rust-tree")] + pub use ::rustc_abi::*; +} + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_index; + +pub mod index { + #[cfg(not(feature = "in-rust-tree"))] + pub use ::ra_ap_rustc_index::*; + + #[cfg(feature = "in-rust-tree")] + pub use ::rustc_index::*; +} diff --git a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml index fb2b9ebef50..0514af8e783 100644 --- a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml +++ b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml @@ -12,4 +12,4 @@ rust-version.workspace = true doctest = false [dependencies] -xshell = "0.2.2" +xshell.workspace = true diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 536f000a44b..35986799fdd 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -12,10 +12,10 @@ rust-version.workspace = true doctest = false [dependencies] -libc = "0.2.135" backtrace = { version = "0.3.67", optional = true } always-assert = { version = "0.1.2", features = ["log"] } jod-thread = "0.1.2" +libc.workspace = true crossbeam-channel = "0.5.5" # Think twice before adding anything here diff --git a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs new file mode 100644 index 00000000000..fd44e6c6d0f --- /dev/null +++ b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs @@ -0,0 +1,379 @@ +//! This file is a port of only the necessary features from https://github.com/chris-morgan/anymap version 1.0.0-beta.2 for use within rust-analyzer. +//! Copyright © 2014–2022 Chris Morgan. COPYING: https://github.com/chris-morgan/anymap/blob/master/COPYING" +//! Note that the license is changed from Blue Oak Model 1.0.0 or MIT or Apache-2.0 to MIT OR Apache-2.0 +//! +//! This implementation provides a safe and convenient store for one value of each type. +//! +//! Your starting point is [`Map`]. It has an example. +//! +//! # Cargo features +//! +//! This implementation has two independent features, each of which provides an implementation providing +//! types `Map`, `AnyMap`, `OccupiedEntry`, `VacantEntry`, `Entry` and `RawMap`: +//! +//! - **std** (default, *enabled* in this build): +//! an implementation using `std::collections::hash_map`, placed in the crate root +//! (e.g. `anymap::AnyMap`). + +#![warn(missing_docs, unused_results)] + +use core::convert::TryInto; +use core::hash::Hasher; + +/// A hasher designed to eke a little more speed out, given `TypeId`’s known characteristics. +/// +/// Specifically, this is a no-op hasher that expects to be fed a u64’s worth of +/// randomly-distributed bits. It works well for `TypeId` (eliminating start-up time, so that my +/// get_missing benchmark is ~30ns rather than ~900ns, and being a good deal faster after that, so +/// that my insert_and_get_on_260_types benchmark is ~12μs instead of ~21.5μs), but will +/// panic in debug mode and always emit zeros in release mode for any other sorts of inputs, so +/// yeah, don’t use it! 😀 +#[derive(Default)] +pub struct TypeIdHasher { + value: u64, +} + +impl Hasher for TypeIdHasher { + #[inline] + fn write(&mut self, bytes: &[u8]) { + // This expects to receive exactly one 64-bit value, and there’s no realistic chance of + // that changing, but I don’t want to depend on something that isn’t expressly part of the + // contract for safety. But I’m OK with release builds putting everything in one bucket + // if it *did* change (and debug builds panicking). + debug_assert_eq!(bytes.len(), 8); + let _ = bytes.try_into().map(|array| self.value = u64::from_ne_bytes(array)); + } + + #[inline] + fn finish(&self) -> u64 { + self.value + } +} + +use core::any::{Any, TypeId}; +use core::hash::BuildHasherDefault; +use core::marker::PhantomData; + +use ::std::collections::hash_map::{self, HashMap}; + +/// Raw access to the underlying `HashMap`. +/// +/// This alias is provided for convenience because of the ugly third generic parameter. +pub type RawMap<A> = HashMap<TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>; + +/// A collection containing zero or one values for any given type and allowing convenient, +/// type-safe access to those values. +/// +/// The type parameter `A` allows you to use a different value type; normally you will want +/// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices: +/// +/// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`; with +/// that, you can only add types that implement `Clone` to the map. +/// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map<dyn Any + Send>`) to add those +/// auto traits. +/// +/// Cumulatively, there are thus six forms of map: +/// +/// - <code>[Map]<dyn [core::any::Any]></code>, +/// also spelled [`AnyMap`] for convenience. +/// - <code>[Map]<dyn [core::any::Any] + Send></code> +/// - <code>[Map]<dyn [core::any::Any] + Send + Sync></code> +/// - <code>[Map]<dyn [CloneAny]></code> +/// - <code>[Map]<dyn [CloneAny] + Send></code> +/// - <code>[Map]<dyn [CloneAny] + Send + Sync></code> +/// +/// ## Example +/// +/// (Here using the [`AnyMap`] convenience alias; the first line could use +/// <code>[anymap::Map][Map]::<[core::any::Any]>::new()</code> instead if desired.) +/// +/// ```rust +#[doc = "let mut data = anymap::AnyMap::new();"] +/// assert_eq!(data.get(), None::<&i32>); +/// ``` +/// +/// Values containing non-static references are not permitted. +#[derive(Debug)] +pub struct Map<A: ?Sized + Downcast = dyn Any> { + raw: RawMap<A>, +} + +/// The most common type of `Map`: just using `Any`; <code>[Map]<dyn [Any]></code>. +/// +/// Why is this a separate type alias rather than a default value for `Map<A>`? +/// `Map::new()` doesn’t seem to be happy to infer that it should go with the default +/// value. It’s a bit sad, really. Ah well, I guess this approach will do. +pub type AnyMap = Map<dyn Any>; +impl<A: ?Sized + Downcast> Default for Map<A> { + #[inline] + fn default() -> Map<A> { + Map::new() + } +} + +impl<A: ?Sized + Downcast> Map<A> { + /// Create an empty collection. + #[inline] + pub fn new() -> Map<A> { + Map { raw: RawMap::with_hasher(Default::default()) } + } + + /// Returns a reference to the value stored in the collection for the type `T`, + /// if it exists. + #[inline] + pub fn get<T: IntoBox<A>>(&self) -> Option<&T> { + self.raw.get(&TypeId::of::<T>()).map(|any| unsafe { any.downcast_ref_unchecked::<T>() }) + } + + /// Gets the entry for the given type in the collection for in-place manipulation + #[inline] + pub fn entry<T: IntoBox<A>>(&mut self) -> Entry<'_, A, T> { + match self.raw.entry(TypeId::of::<T>()) { + hash_map::Entry::Occupied(e) => { + Entry::Occupied(OccupiedEntry { inner: e, type_: PhantomData }) + } + hash_map::Entry::Vacant(e) => { + Entry::Vacant(VacantEntry { inner: e, type_: PhantomData }) + } + } + } +} + +/// A view into a single occupied location in an `Map`. +pub struct OccupiedEntry<'a, A: ?Sized + Downcast, V: 'a> { + inner: hash_map::OccupiedEntry<'a, TypeId, Box<A>>, + type_: PhantomData<V>, +} + +/// A view into a single empty location in an `Map`. +pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> { + inner: hash_map::VacantEntry<'a, TypeId, Box<A>>, + type_: PhantomData<V>, +} + +/// A view into a single location in an `Map`, which may be vacant or occupied. +pub enum Entry<'a, A: ?Sized + Downcast, V> { + /// An occupied Entry + Occupied(OccupiedEntry<'a, A, V>), + /// A vacant Entry + Vacant(VacantEntry<'a, A, V>), +} + +impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> Entry<'a, A, V> { + /// Ensures a value is in the entry by inserting the result of the default function if + /// empty, and returns a mutable reference to the value in the entry. + #[inline] + pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V { + match self { + Entry::Occupied(inner) => inner.into_mut(), + Entry::Vacant(inner) => inner.insert(default()), + } + } +} + +impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> OccupiedEntry<'a, A, V> { + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the collection itself + #[inline] + pub fn into_mut(self) -> &'a mut V { + unsafe { self.inner.into_mut().downcast_mut_unchecked() } + } +} + +impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'a, A, V> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it + #[inline] + pub fn insert(self, value: V) -> &'a mut V { + unsafe { self.inner.insert(value.into_box()).downcast_mut_unchecked() } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Clone, Debug, PartialEq)] + struct A(i32); + #[derive(Clone, Debug, PartialEq)] + struct B(i32); + #[derive(Clone, Debug, PartialEq)] + struct C(i32); + #[derive(Clone, Debug, PartialEq)] + struct D(i32); + #[derive(Clone, Debug, PartialEq)] + struct E(i32); + #[derive(Clone, Debug, PartialEq)] + struct F(i32); + #[derive(Clone, Debug, PartialEq)] + struct J(i32); + + #[test] + fn test_varieties() { + fn assert_send<T: Send>() {} + fn assert_sync<T: Sync>() {} + fn assert_debug<T: ::core::fmt::Debug>() {} + assert_send::<Map<dyn Any + Send>>(); + assert_send::<Map<dyn Any + Send + Sync>>(); + assert_sync::<Map<dyn Any + Send + Sync>>(); + assert_debug::<Map<dyn Any>>(); + assert_debug::<Map<dyn Any + Send>>(); + assert_debug::<Map<dyn Any + Send + Sync>>(); + assert_send::<Map<dyn CloneAny + Send>>(); + assert_send::<Map<dyn CloneAny + Send + Sync>>(); + assert_sync::<Map<dyn CloneAny + Send + Sync>>(); + assert_debug::<Map<dyn CloneAny>>(); + assert_debug::<Map<dyn CloneAny + Send>>(); + assert_debug::<Map<dyn CloneAny + Send + Sync>>(); + } + + #[test] + fn type_id_hasher() { + use core::any::TypeId; + use core::hash::Hash; + fn verify_hashing_with(type_id: TypeId) { + let mut hasher = TypeIdHasher::default(); + type_id.hash(&mut hasher); + // SAFETY: u64 is valid for all bit patterns. + let _ = hasher.finish(); + } + // Pick a variety of types, just to demonstrate it’s all sane. Normal, zero-sized, unsized, &c. + verify_hashing_with(TypeId::of::<usize>()); + verify_hashing_with(TypeId::of::<()>()); + verify_hashing_with(TypeId::of::<str>()); + verify_hashing_with(TypeId::of::<&str>()); + verify_hashing_with(TypeId::of::<Vec<u8>>()); + } +} + +// impl some traits for dyn Any +use core::fmt; + +#[doc(hidden)] +pub trait CloneToAny { + /// Clone `self` into a new `Box<dyn CloneAny>` object. + fn clone_to_any(&self) -> Box<dyn CloneAny>; +} + +impl<T: Any + Clone> CloneToAny for T { + #[inline] + fn clone_to_any(&self) -> Box<dyn CloneAny> { + Box::new(self.clone()) + } +} + +macro_rules! impl_clone { + ($t:ty) => { + impl Clone for Box<$t> { + #[inline] + fn clone(&self) -> Box<$t> { + // SAFETY: this dance is to reapply any Send/Sync marker. I’m not happy about this + // approach, given that I used to do it in safe code, but then came a dodgy + // future-compatibility warning where_clauses_object_safety, which is spurious for + // auto traits but still super annoying (future-compatibility lints seem to mean + // your bin crate needs a corresponding allow!). Although I explained my plight¹ + // and it was all explained and agreed upon, no action has been taken. So I finally + // caved and worked around it by doing it this way, which matches what’s done for + // core::any², so it’s probably not *too* bad. + // + // ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013 + // ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616 + let clone: Box<dyn CloneAny> = (**self).clone_to_any(); + let raw: *mut dyn CloneAny = Box::into_raw(clone); + unsafe { Box::from_raw(raw as *mut $t) } + } + } + + impl fmt::Debug for $t { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad(stringify!($t)) + } + } + }; +} + +/// Methods for downcasting from an `Any`-like trait object. +/// +/// This should only be implemented on trait objects for subtraits of `Any`, though you can +/// implement it for other types and it’ll work fine, so long as your implementation is correct. +pub trait Downcast { + /// Gets the `TypeId` of `self`. + fn type_id(&self) -> TypeId; + + // Note the bound through these downcast methods is 'static, rather than the inexpressible + // concept of Self-but-as-a-trait (where Self is `dyn Trait`). This is sufficient, exceeding + // TypeId’s requirements. Sure, you *can* do CloneAny.downcast_unchecked::<NotClone>() and the + // type system won’t protect you, but that doesn’t introduce any unsafety: the method is + // already unsafe because you can specify the wrong type, and if this were exposing safe + // downcasting, CloneAny.downcast::<NotClone>() would just return an error, which is just as + // correct. + // + // Now in theory we could also add T: ?Sized, but that doesn’t play nicely with the common + // implementation, so I’m doing without it. + + /// Downcast from `&Any` to `&T`, without checking the type matches. + /// + /// # Safety + /// + /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. + unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T; + + /// Downcast from `&mut Any` to `&mut T`, without checking the type matches. + /// + /// # Safety + /// + /// The caller must ensure that `T` matches the trait object, on pain of *undefined behaviour*. + unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T; +} + +/// A trait for the conversion of an object into a boxed trait object. +pub trait IntoBox<A: ?Sized + Downcast>: Any { + /// Convert self into the appropriate boxed form. + fn into_box(self) -> Box<A>; +} + +macro_rules! implement { + ($any_trait:ident $(+ $auto_traits:ident)*) => { + impl Downcast for dyn $any_trait $(+ $auto_traits)* { + #[inline] + fn type_id(&self) -> TypeId { + self.type_id() + } + + #[inline] + unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T { + &*(self as *const Self as *const T) + } + + #[inline] + unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T { + &mut *(self as *mut Self as *mut T) + } + } + + impl<T: $any_trait $(+ $auto_traits)*> IntoBox<dyn $any_trait $(+ $auto_traits)*> for T { + #[inline] + fn into_box(self) -> Box<dyn $any_trait $(+ $auto_traits)*> { + Box::new(self) + } + } + } +} + +implement!(Any); +implement!(Any + Send); +implement!(Any + Send + Sync); + +/// [`Any`], but with cloning. +/// +/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`. +/// See [`core::any`] for more details on `Any` in general. +pub trait CloneAny: Any + CloneToAny {} +impl<T: Any + Clone> CloneAny for T {} +implement!(CloneAny); +implement!(CloneAny + Send); +implement!(CloneAny + Send + Sync); +impl_clone!(dyn CloneAny); +impl_clone!(dyn CloneAny + Send); +impl_clone!(dyn CloneAny + Send + Sync); diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs index 24990d6a0e7..f26c7f6dfc2 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs @@ -12,6 +12,7 @@ pub mod panic_context; pub mod non_empty_vec; pub mod rand; pub mod thread; +pub mod anymap; pub use always_assert::{always, never}; @@ -89,6 +90,57 @@ where words.join("_") } +// Taken from rustc. +pub fn to_camel_case(ident: &str) -> String { + ident + .trim_matches('_') + .split('_') + .filter(|component| !component.is_empty()) + .map(|component| { + let mut camel_cased_component = String::with_capacity(component.len()); + + let mut new_word = true; + let mut prev_is_lower_case = true; + + for c in component.chars() { + // Preserve the case if an uppercase letter follows a lowercase letter, so that + // `camelCase` is converted to `CamelCase`. + if prev_is_lower_case && c.is_uppercase() { + new_word = true; + } + + if new_word { + camel_cased_component.extend(c.to_uppercase()); + } else { + camel_cased_component.extend(c.to_lowercase()); + } + + prev_is_lower_case = c.is_lowercase(); + new_word = false; + } + + camel_cased_component + }) + .fold((String::new(), None), |(acc, prev): (_, Option<String>), next| { + // separate two components with an underscore if their boundary cannot + // be distinguished using an uppercase/lowercase case distinction + let join = prev + .and_then(|prev| { + let f = next.chars().next()?; + let l = prev.chars().last()?; + Some(!char_has_case(l) && !char_has_case(f)) + }) + .unwrap_or(false); + (acc + if join { "_" } else { "" } + &next, Some(next)) + }) + .0 +} + +// Taken from rustc. +pub fn char_has_case(c: char) -> bool { + c.is_lowercase() || c.is_uppercase() +} + pub fn replace(buf: &mut String, from: char, to: &str) { if !buf.contains(from) { return; diff --git a/src/tools/rust-analyzer/crates/stdx/src/macros.rs b/src/tools/rust-analyzer/crates/stdx/src/macros.rs index 1a9982fa8b2..d71e418c89b 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/macros.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/macros.rs @@ -15,7 +15,12 @@ macro_rules! eprintln { macro_rules! format_to { ($buf:expr) => (); ($buf:expr, $lit:literal $($arg:tt)*) => { - { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); } + { + use ::std::fmt::Write as _; + // We can't do ::std::fmt::Write::write_fmt($buf, format_args!($lit $($arg)*)) + // unfortunately, as that loses out on autoref behavior. + _ = $buf.write_fmt(format_args!($lit $($arg)*)) + } }; } diff --git a/src/tools/rust-analyzer/crates/stdx/src/process.rs b/src/tools/rust-analyzer/crates/stdx/src/process.rs index e5aa3436518..bca0cbc36d1 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/process.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/process.rs @@ -23,7 +23,7 @@ pub fn streaming_output( let idx = if eof { data.len() } else { - match data.iter().rposition(|b| *b == b'\n') { + match data.iter().rposition(|&b| b == b'\n') { Some(i) => i + 1, None => return, } diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 5ee0c479284..3b55921dc75 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -14,16 +14,16 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -either = "1.7.0" -itertools = "0.10.5" +either.workspace = true +itertools.workspace = true rowan = "0.15.11" rustc-hash = "1.1.0" once_cell = "1.17.0" -indexmap = "2.0.0" +indexmap.workspace = true smol_str.workspace = true triomphe.workspace = true -rustc_lexer.workspace = true +rustc-dependencies.workspace = true parser.workspace = true profile.workspace = true @@ -31,7 +31,7 @@ stdx.workspace = true text-edit.workspace = true [dev-dependencies] -rayon = "1.6.1" +rayon.workspace = true expect-test = "1.4.0" proc-macro2 = "1.0.47" quote = "1.0.20" @@ -41,4 +41,4 @@ test-utils.workspace = true sourcegen.workspace = true [features] -in-rust-tree = [] +in-rust-tree = ["rustc-dependencies/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 3603560d35c..c3010d090c6 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -36,7 +36,7 @@ PathSegment = '::'? NameRef | NameRef GenericArgList? | NameRef ParamList RetType? -| '<' PathType ('as' PathType)? '>' +| '<' Type ('as' PathType)? '>' GenericArgList = '::'? '<' (GenericArg (',' GenericArg)* ','?)? '>' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index a150d9e6c07..37d8212042d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -3,18 +3,17 @@ use std::iter::{empty, successors}; use parser::{SyntaxKind, T}; -use rowan::SyntaxElement; use crate::{ algo::{self, neighbor}, ast::{self, edit::IndentLevel, make, HasGenericParams}, ted::{self, Position}, - AstNode, AstToken, Direction, + AstNode, AstToken, Direction, SyntaxElement, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, }; -use super::HasName; +use super::{HasArgList, HasName}; pub trait GenericParamsOwnerEdit: ast::HasGenericParams { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; @@ -224,7 +223,7 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { let after_attrs_and_comments = node .children_with_tokens() .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) - .map_or(Position::first_child_of(node), |it| Position::before(it)); + .map_or(Position::first_child_of(node), Position::before); ted::insert_all( after_attrs_and_comments, @@ -362,6 +361,24 @@ impl ast::PathSegment { } } +impl ast::MethodCallExpr { + pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList { + if self.generic_arg_list().is_none() { + let generic_arg_list = make::turbofish_generic_arg_list(empty()).clone_for_update(); + + if let Some(arg_list) = self.arg_list() { + ted::insert_raw( + ted::Position::before(arg_list.syntax()), + generic_arg_list.syntax(), + ); + } else { + ted::append_child(self.syntax(), generic_arg_list.syntax()); + } + } + self.generic_arg_list().unwrap() + } +} + impl Removable for ast::UseTree { fn remove(&self) { for dir in [Direction::Next, Direction::Prev] { @@ -433,7 +450,9 @@ impl ast::UseTree { if &path == prefix && self.use_tree_list().is_none() { if self.star_token().is_some() { // path$0::* -> * - self.coloncolon_token().map(ted::remove); + if let Some(a) = self.coloncolon_token() { + ted::remove(a) + } ted::remove(prefix.syntax()); } else { // path$0 -> self @@ -460,7 +479,9 @@ impl ast::UseTree { for p in successors(parent.parent_path(), |it| it.parent_path()) { p.segment()?; } - prefix.parent_path().and_then(|p| p.coloncolon_token()).map(ted::remove); + if let Some(a) = prefix.parent_path().and_then(|p| p.coloncolon_token()) { + ted::remove(a) + } ted::remove(prefix.syntax()); Some(()) } @@ -555,7 +576,7 @@ impl ast::AssocItemList { None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), }, }; - let elements: Vec<SyntaxElement<_>> = vec![ + let elements: Vec<SyntaxElement> = vec![ make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ]; @@ -625,6 +646,50 @@ impl ast::MatchArmList { } } +impl ast::LetStmt { + pub fn set_ty(&self, ty: Option<ast::Type>) { + match ty { + None => { + if let Some(colon_token) = self.colon_token() { + ted::remove(colon_token); + } + + if let Some(existing_ty) = self.ty() { + if let Some(sibling) = existing_ty.syntax().prev_sibling_or_token() { + if sibling.kind() == SyntaxKind::WHITESPACE { + ted::remove(sibling); + } + } + + ted::remove(existing_ty.syntax()); + } + + // Remove any trailing ws + if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + Some(new_ty) => { + if self.colon_token().is_none() { + ted::insert_raw( + Position::after( + self.pat().expect("let stmt should have a pattern").syntax(), + ), + make::token(T![:]), + ); + } + + if let Some(old_ty) = self.ty() { + ted::replace(old_ty.syntax(), new_ty.syntax()); + } else { + ted::insert(Position::after(self.colon_token().unwrap()), new_ty.syntax()); + } + } + } + } +} + impl ast::RecordExprFieldList { pub fn add_field(&self, field: ast::RecordExprField) { let is_multiline = self.syntax().text().contains_char('\n'); @@ -749,7 +814,7 @@ impl ast::VariantList { None => (IndentLevel::single(), Position::last_child_of(self.syntax())), }, }; - let elements: Vec<SyntaxElement<_>> = vec![ + let elements: Vec<SyntaxElement> = vec![ make::tokens::whitespace(&format!("{}{indent}", "\n")).into(), variant.syntax().clone().into(), ast::make::token(T![,]).into(), @@ -784,6 +849,53 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } +impl ast::IdentPat { + pub fn set_pat(&self, pat: Option<ast::Pat>) { + match pat { + None => { + if let Some(at_token) = self.at_token() { + // Remove `@ Pat` + let start = at_token.clone().into(); + let end = self + .pat() + .map(|it| it.syntax().clone().into()) + .unwrap_or_else(|| at_token.into()); + + ted::remove_all(start..=end); + + // Remove any trailing ws + if let Some(last) = + self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + } + Some(pat) => { + if let Some(old_pat) = self.pat() { + // Replace existing pattern + ted::replace(old_pat.syntax(), pat.syntax()) + } else if let Some(at_token) = self.at_token() { + // Have an `@` token but not a pattern yet + ted::insert(ted::Position::after(at_token), pat.syntax()); + } else { + // Don't have an `@`, should have a name + let name = self.name().unwrap(); + + ted::insert_all( + ted::Position::after(name.syntax()), + vec![ + make::token(T![@]).into(), + make::tokens::single_space().into(), + pat.syntax().clone().into(), + ], + ) + } + } + } + } +} + pub trait HasVisibilityEdit: ast::HasVisibility { fn set_visibility(&self, visbility: ast::Visibility) { match self.visibility() { @@ -886,6 +998,65 @@ mod tests { } #[test] + fn test_ident_pat_set_pat() { + #[track_caller] + fn check(before: &str, expected: &str, pat: Option<ast::Pat>) { + let pat = pat.map(|it| it.clone_for_update()); + + let ident_pat = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {before} }}")); + ident_pat.set_pat(pat); + + let after = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {expected} }}")); + assert_eq!(ident_pat.to_string(), after.to_string()); + } + + // replacing + check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into())); + + // note: no trailing semicolon is added for the below tests since it + // seems to be picked up by the ident pat during error recovery? + + // adding + check("let a ", "let a @ ()", Some(make::tuple_pat([]).into())); + check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into())); + + // removing + check("let a @ ()", "let a", None); + check("let a @ ", "let a", None); + } + + #[test] + fn test_let_stmt_set_ty() { + #[track_caller] + fn check(before: &str, expected: &str, ty: Option<ast::Type>) { + let ty = ty.map(|it| it.clone_for_update()); + + let let_stmt = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {before} }}")); + let_stmt.set_ty(ty); + + let after = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {expected} }}")); + assert_eq!(let_stmt.to_string(), after.to_string(), "{let_stmt:#?}\n!=\n{after:#?}"); + } + + // adding + check("let a;", "let a: ();", Some(make::ty_tuple([]))); + // no semicolon due to it being eaten during error recovery + check("let a:", "let a: ()", Some(make::ty_tuple([]))); + + // replacing + check("let a: u8;", "let a: ();", Some(make::ty_tuple([]))); + check("let a: u8 = 3;", "let a: () = 3;", Some(make::ty_tuple([]))); + check("let a: = 3;", "let a: () = 3;", Some(make::ty_tuple([]))); + + // removing + check("let a: u8;", "let a;", None); + check("let a:;", "let a;", None); + + check("let a: u8 = 3;", "let a = 3;", None); + check("let a: = 3;", "let a = 3;", None); + } + + #[test] fn add_variant_to_empty_enum() { let variant = make::variant(make::name("Bar"), None).clone_for_update(); @@ -976,7 +1147,9 @@ enum Foo { fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) { let enum_ = ast_mut_from_text::<ast::Enum>(before); - enum_.variant_list().map(|it| it.add_variant(variant)); + if let Some(it) = enum_.variant_list() { + it.add_variant(variant) + } let after = enum_.to_string(); assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(after.trim())); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 7ba0d4dc656..6c86e591044 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -59,8 +59,9 @@ impl PathSegment { pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) } pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) } pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) } - pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) } + pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) } + pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) } pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) } } @@ -1577,14 +1578,6 @@ impl RecordPatField { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum GenericArg { - TypeArg(TypeArg), - AssocTypeArg(AssocTypeArg), - LifetimeArg(LifetimeArg), - ConstArg(ConstArg), -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Type { ArrayType(ArrayType), DynTraitType(DynTraitType), @@ -1603,6 +1596,14 @@ pub enum Type { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg { + TypeArg(TypeArg), + AssocTypeArg(AssocTypeArg), + LifetimeArg(LifetimeArg), + ConstArg(ConstArg), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Expr { ArrayExpr(ArrayExpr), AsmExpr(AsmExpr), @@ -3319,41 +3320,6 @@ impl AstNode for RecordPatField { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl From<TypeArg> for GenericArg { - fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) } -} -impl From<AssocTypeArg> for GenericArg { - fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) } -} -impl From<LifetimeArg> for GenericArg { - fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) } -} -impl From<ConstArg> for GenericArg { - fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) } -} -impl AstNode for GenericArg { - fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG) - } - fn cast(syntax: SyntaxNode) -> Option<Self> { - let res = match syntax.kind() { - TYPE_ARG => GenericArg::TypeArg(TypeArg { syntax }), - ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }), - LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }), - CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }), - _ => return None, - }; - Some(res) - } - fn syntax(&self) -> &SyntaxNode { - match self { - GenericArg::TypeArg(it) => &it.syntax, - GenericArg::AssocTypeArg(it) => &it.syntax, - GenericArg::LifetimeArg(it) => &it.syntax, - GenericArg::ConstArg(it) => &it.syntax, - } - } -} impl From<ArrayType> for Type { fn from(node: ArrayType) -> Type { Type::ArrayType(node) } } @@ -3455,6 +3421,41 @@ impl AstNode for Type { } } } +impl From<TypeArg> for GenericArg { + fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) } +} +impl From<AssocTypeArg> for GenericArg { + fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) } +} +impl From<LifetimeArg> for GenericArg { + fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) } +} +impl From<ConstArg> for GenericArg { + fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) } +} +impl AstNode for GenericArg { + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG) + } + fn cast(syntax: SyntaxNode) -> Option<Self> { + let res = match syntax.kind() { + TYPE_ARG => GenericArg::TypeArg(TypeArg { syntax }), + ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }), + LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }), + CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + GenericArg::TypeArg(it) => &it.syntax, + GenericArg::AssocTypeArg(it) => &it.syntax, + GenericArg::LifetimeArg(it) => &it.syntax, + GenericArg::ConstArg(it) => &it.syntax, + } + } +} impl From<ArrayExpr> for Expr { fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) } } @@ -4340,12 +4341,12 @@ impl AstNode for AnyHasVisibility { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl std::fmt::Display for GenericArg { +impl std::fmt::Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for Type { +impl std::fmt::Display for GenericArg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 17e311c0c50..ad63cc55862 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -433,7 +433,6 @@ pub fn record_field( ast_from_text(&format!("struct S {{ {visibility}{name}: {ty}, }}")) } -// TODO pub fn block_expr( stmts: impl IntoIterator<Item = ast::Stmt>, tail_expr: Option<ast::Expr>, @@ -853,6 +852,10 @@ pub fn self_param() -> ast::SelfParam { ast_from_text("fn f(&self) { }") } +pub fn mut_self_param() -> ast::SelfParam { + ast_from_text("fn f(&mut self) { }") +} + pub fn ret_type(ty: ast::Type) -> ast::RetType { ast_from_text(&format!("fn f() -> {ty} {{ }}")) } @@ -938,6 +941,13 @@ pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg { ast_from_text(&format!("const S: T<{lifetime}> = ();")) } +pub fn turbofish_generic_arg_list( + args: impl IntoIterator<Item = ast::GenericArg>, +) -> ast::GenericArgList { + let args = args.into_iter().join(", "); + ast_from_text(&format!("const S: T::<{args}> = ();")) +} + pub(crate) fn generic_arg_list( args: impl IntoIterator<Item = ast::GenericArg>, ) -> ast::GenericArgList { @@ -973,6 +983,11 @@ pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::T ast_from_text(&format!("struct f({visibility}{ty});")) } +pub fn variant_list(variants: impl IntoIterator<Item = ast::Variant>) -> ast::VariantList { + let variants = variants.into_iter().join(", "); + ast_from_text(&format!("enum f {{ {variants} }}")) +} + pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant { let field_list = match field_list { None => String::new(), @@ -1037,6 +1052,19 @@ pub fn struct_( ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",)) } +pub fn enum_( + visibility: Option<ast::Visibility>, + enum_name: ast::Name, + variant_list: ast::VariantList, +) -> ast::Enum { + let visibility = match visibility { + None => String::new(), + Some(it) => format!("{it} "), + }; + + ast_from_text(&format!("{visibility}enum {enum_name} {variant_list}")) +} + pub fn attr_outer(meta: ast::Meta) -> ast::Attr { ast_from_text(&format!("#[{meta}]")) } @@ -1105,7 +1133,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", + "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\n", ) }); @@ -1149,6 +1177,16 @@ pub mod tokens { lit.syntax().first_child_or_token().unwrap().into_token().unwrap() } + pub fn ident(text: &str) -> SyntaxToken { + assert_eq!(text.trim(), text); + let path: ast::Path = super::ext::ident_path(text); + path.syntax() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == IDENT) + .unwrap() + } + pub fn single_newline() -> SyntaxToken { let res = SOURCE_FILE .tree() diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 691d0c618f3..be5b954ad34 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -361,6 +361,15 @@ impl ast::Impl { } } +// [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778) +impl ast::PathSegment { + pub fn qualifying_trait(&self) -> Option<ast::PathType> { + let mut path_types = support::children(self.syntax()); + let first = path_types.next()?; + path_types.next().or(Some(first)) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum StructKind { Record(ast::RecordFieldList), diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index 87fd51d703c..8cc271d226c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -2,6 +2,8 @@ use std::borrow::Cow; +use rustc_dependencies::lexer as rustc_lexer; + use rustc_lexer::unescape::{ unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode, }; diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 27c8a13e58d..4939ab39049 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -19,6 +19,7 @@ //! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256> //! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md> +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] #[allow(unused)] @@ -74,7 +75,7 @@ pub use smol_str::SmolStr; #[derive(Debug, PartialEq, Eq)] pub struct Parse<T> { green: GreenNode, - errors: Arc<Vec<SyntaxError>>, + errors: Arc<[SyntaxError]>, _ty: PhantomData<fn() -> T>, } @@ -86,7 +87,7 @@ impl<T> Clone for Parse<T> { impl<T> Parse<T> { fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> { - Parse { green, errors: Arc::new(errors), _ty: PhantomData } + Parse { green, errors: errors.into(), _ty: PhantomData } } pub fn syntax_node(&self) -> SyntaxNode { @@ -106,7 +107,7 @@ impl<T: AstNode> Parse<T> { T::cast(self.syntax_node()).unwrap() } - pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> { + pub fn ok(self) -> Result<T, Arc<[SyntaxError]>> { if self.errors.is_empty() { Ok(self.tree()) } else { @@ -143,7 +144,7 @@ impl Parse<SourceFile> { parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map( |(green_node, errors, _reparsed_range)| Parse { green: green_node, - errors: Arc::new(errors), + errors: errors.into(), _ty: PhantomData, }, ) @@ -167,7 +168,7 @@ impl SourceFile { errors.extend(validation::validate(&root)); assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); - Parse { green, errors: Arc::new(errors), _ty: PhantomData } + Parse { green, errors: errors.into(), _ty: PhantomData } } } @@ -181,29 +182,27 @@ impl ast::TokenTree { let kind = t.kind(); if kind.is_trivia() { was_joint = false + } else if kind == SyntaxKind::IDENT { + let token_text = t.text(); + let contextual_kw = + SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT); + parser_input.push_ident(contextual_kw); } else { - if kind == SyntaxKind::IDENT { - let token_text = t.text(); - let contextual_kw = SyntaxKind::from_contextual_keyword(token_text) - .unwrap_or(SyntaxKind::IDENT); - parser_input.push_ident(contextual_kw); - } else { - if was_joint { + if was_joint { + parser_input.was_joint(); + } + parser_input.push(kind); + // Tag the token as joint if it is float with a fractional part + // we use this jointness to inform the parser about what token split + // event to emit when we encounter a float literal in a field access + if kind == SyntaxKind::FLOAT_NUMBER { + if !t.text().ends_with('.') { parser_input.was_joint(); - } - parser_input.push(kind); - // Tag the token as joint if it is float with a fractional part - // we use this jointness to inform the parser about what token split - // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER { - if !t.text().ends_with('.') { - parser_input.was_joint(); - } else { - was_joint = false; - } } else { - was_joint = true; + was_joint = false; } + } else { + was_joint = true; } } } @@ -276,7 +275,7 @@ impl ast::TokenTree { let (green, errors) = builder.finish_raw(); - Parse { green, errors: Arc::new(errors), _ty: PhantomData } + Parse { green, errors: errors.into(), _ty: PhantomData } } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index 1d4a89201ae..71762996cd7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs @@ -73,6 +73,10 @@ impl<N: AstNode> AstPtr<N> { Some(AstPtr { raw: self.raw, _ty: PhantomData }) } + pub fn kind(&self) -> parser::SyntaxKind { + self.raw.kind() + } + pub fn upcast<M: AstNode>(self) -> AstPtr<M> where N: Into<M>, @@ -84,6 +88,20 @@ impl<N: AstNode> AstPtr<N> { pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> { N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData }) } + + pub fn wrap_left<R>(self) -> AstPtr<either::Either<N, R>> + where + either::Either<N, R>: AstNode, + { + AstPtr { raw: self.raw, _ty: PhantomData } + } + + pub fn wrap_right<L>(self) -> AstPtr<either::Either<L, N>> + where + either::Either<L, N>: AstNode, + { + AstPtr { raw: self.raw, _ty: PhantomData } + } } impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr { diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs index 168439053c2..3010d77d827 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs @@ -17,11 +17,11 @@ use crate::{ast, fuzz, AstNode, SourceFile, SyntaxError}; #[test] fn parse_smoke_test() { - let code = r##" + let code = r#" fn main() { println!("Hello, world!") } - "##; + "#; let parse = SourceFile::parse(code); // eprintln!("{:#?}", parse.syntax_node()); diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index dc6c96343d6..c2e921e4b6f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -622,7 +622,7 @@ fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> { } fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) { - if lower_seperated_list(acc, grammar, label, rule) { + if lower_separated_list(acc, grammar, label, rule) { return; } @@ -688,7 +688,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r } // (T (',' T)* ','?) -fn lower_seperated_list( +fn lower_separated_list( acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index e0ec6a242ff..2b1bbac08e5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -5,7 +5,7 @@ mod block; use rowan::Direction; -use rustc_lexer::unescape::{self, unescape_literal, Mode}; +use rustc_dependencies::lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index 2b5b6f49561..438b599ffaa 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] # Avoid adding deps here, this crate is widely used in tests it should compile fast! -dissimilar = "1.0.4" +dissimilar = "1.0.7" text-size.workspace = true rustc-hash = "1.1.0" diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 573f56b003a..f2ca9d82ed0 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -44,7 +44,7 @@ //! panic: fmt //! phantom_data: //! pin: -//! pointee: +//! pointee: copy, send, sync, ord, hash, unpin //! range: //! result: //! send: sized @@ -54,6 +54,7 @@ //! sync: sized //! transmute: //! try: infallible +//! unpin: sized //! unsize: sized #![rustc_coherence_is_core] @@ -89,6 +90,11 @@ pub mod marker { pub trait Unsize<T: ?Sized> {} // endregion:unsize + // region:unpin + #[lang = "unpin"] + pub auto trait Unpin {} + // endregion:unpin + // region:copy #[lang = "copy"] pub trait Copy: Clone {} @@ -387,9 +393,10 @@ pub mod ptr { // region:pointee #[lang = "pointee_trait"] + #[rustc_deny_explicit_impl(implement_via_object = false)] pub trait Pointee { #[lang = "metadata_type"] - type Metadata; + type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } // endregion:pointee // region:non_null @@ -489,7 +496,7 @@ pub mod ops { I: SliceIndex<[T]>, { type Output = I::Output; - fn index(&self, index: I) -> &I::Output { + fn index(&self, _index: I) -> &I::Output { loop {} } } @@ -497,7 +504,7 @@ pub mod ops { where I: SliceIndex<[T]>, { - fn index_mut(&mut self, index: I) -> &mut I::Output { + fn index_mut(&mut self, _index: I) -> &mut I::Output { loop {} } } @@ -507,7 +514,7 @@ pub mod ops { I: SliceIndex<[T]>, { type Output = I::Output; - fn index(&self, index: I) -> &I::Output { + fn index(&self, _index: I) -> &I::Output { loop {} } } @@ -515,7 +522,7 @@ pub mod ops { where I: SliceIndex<[T]>, { - fn index_mut(&mut self, index: I) -> &mut I::Output { + fn index_mut(&mut self, _index: I) -> &mut I::Output { loop {} } } @@ -863,17 +870,17 @@ pub mod fmt { pub struct DebugTuple; pub struct DebugStruct; impl Formatter<'_> { - pub fn debug_tuple(&mut self, name: &str) -> DebugTuple { + pub fn debug_tuple(&mut self, _name: &str) -> DebugTuple { DebugTuple } - pub fn debug_struct(&mut self, name: &str) -> DebugStruct { + pub fn debug_struct(&mut self, _name: &str) -> DebugStruct { DebugStruct } } impl DebugTuple { - pub fn field(&mut self, value: &dyn Debug) -> &mut Self { + pub fn field(&mut self, _value: &dyn Debug) -> &mut Self { self } @@ -883,7 +890,7 @@ pub mod fmt { } impl DebugStruct { - pub fn field(&mut self, name: &str, value: &dyn Debug) -> &mut Self { + pub fn field(&mut self, _name: &str, _value: &dyn Debug) -> &mut Self { self } @@ -996,7 +1003,7 @@ pub mod fmt { ($($t:ty)*) => { $( impl const Debug for $t { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { + fn fmt(&self, _f: &mut Formatter<'_>) -> Result { Ok(()) } } @@ -1012,7 +1019,7 @@ pub mod fmt { } impl<T: Debug> Debug for [T] { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { + fn fmt(&self, _f: &mut Formatter<'_>) -> Result { Ok(()) } } @@ -1062,7 +1069,7 @@ pub mod option { } } - pub fn and<U>(self, optb: Option<U>) -> Option<U> { + pub fn and<U>(self, _optb: Option<U>) -> Option<U> { loop {} } pub fn unwrap_or(self, default: T) -> T { @@ -1080,25 +1087,25 @@ pub mod option { } // endregion:result // region:fn - pub fn and_then<U, F>(self, f: F) -> Option<U> + pub fn and_then<U, F>(self, _f: F) -> Option<U> where F: FnOnce(T) -> Option<U>, { loop {} } - pub fn unwrap_or_else<F>(self, f: F) -> T + pub fn unwrap_or_else<F>(self, _f: F) -> T where F: FnOnce() -> T, { loop {} } - pub fn map_or<U, F>(self, default: U, f: F) -> U + pub fn map_or<U, F>(self, _default: U, _f: F) -> U where F: FnOnce(T) -> U, { loop {} } - pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U + pub fn map_or_else<U, D, F>(self, _default: D, _f: F) -> U where D: FnOnce() -> U, F: FnOnce(T) -> U, @@ -1129,7 +1136,7 @@ pub mod pin { pointer: P, } impl<P> Pin<P> { - pub fn new(pointer: P) -> Pin<P> { + pub fn new(_pointer: P) -> Pin<P> { loop {} } } @@ -1226,7 +1233,7 @@ pub mod iter { mod sources { mod repeat { - pub fn repeat<T>(elt: T) -> Repeat<T> { + pub fn repeat<T>(_elt: T) -> Repeat<T> { loop {} } @@ -1266,7 +1273,7 @@ pub mod iter { fn take(self, n: usize) -> crate::iter::Take<Self> { loop {} } - fn filter_map<B, F>(self, f: F) -> crate::iter::FilterMap<Self, F> + fn filter_map<B, F>(self, _f: F) -> crate::iter::FilterMap<Self, F> where Self: Sized, F: FnMut(Self::Item) -> Option<B>, @@ -1337,7 +1344,7 @@ mod panic { mod panicking { #[lang = "panic_fmt"] - pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { + pub const fn panic_fmt(_fmt: crate::fmt::Arguments<'_>) -> ! { loop {} } } diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index 76d0ca5ccb6..4620cc72d0a 100644 --- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml @@ -12,5 +12,5 @@ rust-version.workspace = true doctest = false [dependencies] -itertools = "0.10.5" +itertools.workspace = true text-size.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index 11055f0280a..fe6cb0a2c3f 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -12,10 +12,9 @@ rust-version.workspace = true doctest = false [dependencies] -tracing = "0.1.35" +tracing.workspace = true walkdir = "2.3.2" crossbeam-channel = "0.5.5" -# We demand 5.1.0 as any higher version pulls in a new windows-sys dupe notify = "6.1.1" stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index c35785cf98c..11409f2eb81 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] rustc-hash = "1.1.0" fst = "0.4.7" -indexmap = "2.0.0" +indexmap.workspace = true nohash-hasher.workspace = true paths.workspace = true diff --git a/src/tools/rust-analyzer/docs/dev/guide.md b/src/tools/rust-analyzer/docs/dev/guide.md index 56a68ef0437..a5f1811bf24 100644 --- a/src/tools/rust-analyzer/docs/dev/guide.md +++ b/src/tools/rust-analyzer/docs/dev/guide.md @@ -272,7 +272,7 @@ several times, with different sets of `cfg`s enabled. The IDE-specific task of mapping source code into a semantic model is inherently imprecise for this reason and gets handled by the [`source_binder`]. -[`source_binder`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/source_binder.rs +[`source_binder`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/source_binder.rs The semantic interface is declared in the [`code_model_api`] module. Each entity is identified by an integer ID and has a bunch of methods which take a salsa database @@ -280,8 +280,8 @@ as an argument and returns other entities (which are also IDs). Internally, thes methods invoke various queries on the database to build the model on demand. Here's [the list of queries]. -[`code_model_api`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/code_model_api.rs -[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/7e84440e25e19529e4ff8a66e521d1b06349c6ec/crates/hir/src/db.rs#L20-L106 +[`code_model_api`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/code_model_api.rs +[the list of queries]: https://github.com/rust-lang/rust-analyzer/blob/7e84440e25e19529e4ff8a66e521d1b06349c6ec/crates/ra_hir/src/db.rs#L20-L106 The first step of building the model is parsing the source code. diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 0801e988f5c..b66c9c943a1 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -57,8 +57,9 @@ export interface TextDocumentEdit { } ``` -When applying such code action or text edit, the editor should insert snippet, with tab stops and placeholder. -At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. +When applying such code action or text edit, the editor should insert snippet, with tab stops and placeholders. +At the moment, rust-analyzer guarantees that only a single `TextDocumentEdit` will have edits which can be `InsertTextFormat.Snippet`. +Any additional `TextDocumentEdit`s will only have edits which are `InsertTextFormat.PlainText`. ### Example diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index 71feed0f72c..7091ea1ce9a 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -57,6 +57,12 @@ build procedural macros. The command is required to output json and should therefore include `--message-format=json` or a similar option. +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and +`#rust-analyzer.cargo.buildScripts.invocationLocation#`. + By default, a cargo invocation will be constructed for the configured targets and features, with the following base command line: @@ -206,9 +212,11 @@ If you're changing this because you're using some tool wrapping Cargo, you might also want to change `#rust-analyzer.cargo.buildScripts.overrideCommand#`. -If there are multiple linked projects, this command is invoked for -each of them, with the working directory being the project root -(i.e., the folder containing the `Cargo.toml`). +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.cargo.check.invocationStrategy#` and +`#rust-analyzer.cargo.check.invocationLocation#`. An example command would be: @@ -244,6 +252,11 @@ with `self` prefixed to them when inside a method. -- Whether to add parenthesis and argument snippets when completing function. -- +[[rust-analyzer.completion.fullFunctionSignatures.enable]]rust-analyzer.completion.fullFunctionSignatures.enable (default: `false`):: ++ +-- +Whether to show full function/method signatures in completion docs. +-- [[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`):: + -- @@ -480,11 +493,16 @@ Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-i -- Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. -- -[[rust-analyzer.imports.prefer.no.std]]rust-analyzer.imports.prefer.no.std (default: `false`):: +[[rust-analyzer.imports.preferNoStd]]rust-analyzer.imports.preferNoStd (default: `false`):: + -- Prefer to unconditionally use imports of the core and alloc crate, over the std crate. -- +[[rust-analyzer.imports.preferPrelude]]rust-analyzer.imports.preferPrelude (default: `false`):: ++ +-- +Whether to prefer import paths containing a `prelude` module. +-- [[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`):: + -- @@ -744,6 +762,16 @@ Command to be executed instead of 'cargo' for runnables. Additional arguments to be passed to cargo for runnables such as tests or binaries. For example, it may be `--release`. -- +[[rust-analyzer.rust.analyzerTargetDir]]rust-analyzer.rust.analyzerTargetDir (default: `null`):: ++ +-- +Optional path to a rust-analyzer specific target directory. +This prevents rust-analyzer's `cargo check` from locking the `Cargo.lock` +at the expense of duplicating build artifacts. + +Set to `true` to use a subdirectory of the existing target directory or +set to a path relative to the workspace to use that path. +-- [[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`):: + -- diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index 5dafd1a4c8c..18d5ddd4d0a 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -237,7 +237,7 @@ To use `rust-analyzer`, you need to install and enable one of the two popular LS ==== Eglot -Eglot is the more minimalistic and lightweight LSP client for Emacs, integrates well with existing Emacs functionality and will be built into Emacs starting from release 29. +Eglot is the more minimalistic and lightweight LSP client for Emacs, integrates well with existing Emacs functionality and is built into Emacs starting from release 29. After installing Eglot, e.g. via `M-x package-install` (not needed from Emacs 29), you can enable it via the `M-x eglot` command or load it automatically in `rust-mode` via @@ -246,6 +246,15 @@ After installing Eglot, e.g. via `M-x package-install` (not needed from Emacs 29 (add-hook 'rust-mode-hook 'eglot-ensure) ---- +To enable clippy, you will need to configure the initialization options to pass the `check.command` setting. + +[source,emacs-lisp] +---- +(add-to-list 'eglot-server-programs + '((rust-ts-mode rust-mode) . + ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) +---- + For more detailed instructions and options see the https://joaotavora.github.io/eglot[Eglot manual] (also available from Emacs via `M-x info`) and the https://github.com/joaotavora/eglot/blob/master/README.md[Eglot readme]. @@ -555,6 +564,11 @@ There is a package named `ra_ap_rust_analyzer` available on https://crates.io/cr For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/publish.yml[the publish workflow]. +=== Zed + +https://zed.dev[Zed] has native `rust-analyzer` support. +If the LSP binary is not available, Zed can install it when opening a Rust file. + == Troubleshooting Start with looking at the rust-analyzer version. @@ -932,7 +946,7 @@ Or it is possible to specify vars more granularly: "rust-analyzer.runnables.extraEnv": [ { // "mask": null, // null mask means that this rule will be applied for all runnables - env: { + "env": { "APP_ID": "1", "APP_DATA": "asdf" } @@ -954,7 +968,7 @@ If needed, you can set different values for different platforms: "rust-analyzer.runnables.extraEnv": [ { "platform": "win32", // windows only - env: { + "env": { "APP_DATA": "windows specific data" } }, diff --git a/src/tools/rust-analyzer/editors/code/language-configuration.json b/src/tools/rust-analyzer/editors/code/language-configuration.json index 51f0e65f4fd..1c348b63f1a 100644 --- a/src/tools/rust-analyzer/editors/code/language-configuration.json +++ b/src/tools/rust-analyzer/editors/code/language-configuration.json @@ -18,7 +18,8 @@ { "open": "[", "close": "]" }, { "open": "(", "close": ")" }, { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "/*", "close": " */" } + { "open": "/*", "close": " */" }, + { "open": "`", "close": "`", "notIn": ["string"] } ], "autoCloseBefore": ";:.,=}])> \n\t", "surroundingPairs": [ @@ -27,7 +28,8 @@ ["(", ")"], ["<", ">"], ["\"", "\""], - ["'", "'"] + ["'", "'"], + ["`", "`"] ], "indentationRules": { "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 20fe781ae9e..1c94f13d745 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -18,7 +18,7 @@ "devDependencies": { "@tsconfig/strictest": "^2.0.1", "@types/node": "~16.11.7", - "@types/vscode": "~1.75", + "@types/vscode": "~1.78.1", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vscode/test-electron": "^2.3.3", @@ -32,7 +32,7 @@ "typescript": "^5.1.6" }, "engines": { - "vscode": "^1.75.0" + "vscode": "^1.78.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -565,9 +565,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.75.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.1.tgz", - "integrity": "sha512-emg7wdsTFzdi+elvoyoA+Q8keEautdQHyY5LNmHVM4PTpY8JgOTVADrGVyXGepJ6dVW2OS5/xnLUWh+nZxvdiA==", + "version": "1.78.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.78.1.tgz", + "integrity": "sha512-wEA+54axejHu7DhcUfnFBan1IqFD1gBDxAFz8LoX06NbNDMRJv/T6OGthOs52yZccasKfN588EyffHWABkR0fg==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 233e7bf44b1..265c63f3e2d 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -17,6 +17,7 @@ "rust" ], "categories": [ + "Formatters", "Programming Languages" ], "capabilities": { @@ -26,7 +27,7 @@ } }, "engines": { - "vscode": "^1.75.0" + "vscode": "^1.78.0" }, "enabledApiProposals": [], "scripts": { @@ -53,7 +54,7 @@ "devDependencies": { "@tsconfig/strictest": "^2.0.1", "@types/node": "~16.11.7", - "@types/vscode": "~1.75", + "@types/vscode": "~1.78.1", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "@vscode/test-electron": "^2.3.3", @@ -155,7 +156,7 @@ }, { "command": "rust-analyzer.expandMacro", - "title": "Expand macro recursively", + "title": "Expand macro recursively at caret", "category": "rust-analyzer" }, { @@ -209,11 +210,6 @@ "category": "rust-analyzer" }, { - "command": "rust-analyzer.addProject", - "title": "Add current file's crate to workspace", - "category": "rust-analyzer" - }, - { "command": "rust-analyzer.restartServer", "title": "Restart server", "category": "rust-analyzer" @@ -249,6 +245,11 @@ "category": "rust-analyzer" }, { + "command": "rust-analyzer.openExternalDocs", + "title": "Open External Docs", + "category": "rust-analyzer" + }, + { "command": "rust-analyzer.openCargoToml", "title": "Open Cargo.toml", "category": "rust-analyzer" @@ -260,12 +261,12 @@ }, { "command": "rust-analyzer.moveItemUp", - "title": "Move item up", + "title": "Move Item Up", "category": "rust-analyzer" }, { "command": "rust-analyzer.moveItemDown", - "title": "Move item down", + "title": "Move Item Down", "category": "rust-analyzer" }, { @@ -371,6 +372,19 @@ ], "markdownDescription": "Problem matchers to use for `rust-analyzer.run` command, eg `[\"$rustc\", \"$rust-panic\"]`." }, + "rust-analyzer.statusBar.clickAction": { + "type": "string", + "enum": [ + "stopServer", + "openLogs" + ], + "enumDescriptions": [ + "Stop Server", + "Open Logs" + ], + "default": "openLogs", + "markdownDescription": "Action to run when clicking the extension status bar item." + }, "rust-analyzer.server.path": { "type": [ "null", @@ -467,7 +481,7 @@ "type": "boolean" }, "rust-analyzer.diagnostics.useRustcErrorCode": { - "markdownDescription": "Whether to use the rustc error code.", + "markdownDescription": "Whether to show diagnostics using the original rustc error code. If this is false, all rustc diagnostics will have the code 'rustc(Click for full compiler diagnostics)'", "default": false, "type": "boolean" }, @@ -557,7 +571,7 @@ ] }, "rust-analyzer.cargo.buildScripts.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.", "default": null, "type": [ "null", @@ -746,7 +760,7 @@ ] }, "rust-analyzer.check.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.check.invocationStrategy#` and\n`#rust-analyzer.cargo.check.invocationLocation#`.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", "default": null, "type": [ "null", @@ -799,6 +813,11 @@ "Do no snippet completions for callables." ] }, + "rust-analyzer.completion.fullFunctionSignatures.enable": { + "markdownDescription": "Whether to show full function/method signatures in completion docs.", + "default": false, + "type": "boolean" + }, "rust-analyzer.completion.limit": { "markdownDescription": "Maximum number of completions to return. If `None`, the limit is infinite.", "default": null, @@ -1105,11 +1124,16 @@ "default": true, "type": "boolean" }, - "rust-analyzer.imports.prefer.no.std": { + "rust-analyzer.imports.preferNoStd": { "markdownDescription": "Prefer to unconditionally use imports of the core and alloc crate, over the std crate.", "default": false, "type": "boolean" }, + "rust-analyzer.imports.preferPrelude": { + "markdownDescription": "Whether to prefer import paths containing a `prelude` module.", + "default": false, + "type": "boolean" + }, "rust-analyzer.imports.prefix": { "markdownDescription": "The path structure for newly inserted paths to use.", "default": "plain", @@ -1470,6 +1494,21 @@ "type": "string" } }, + "rust-analyzer.rust.analyzerTargetDir": { + "markdownDescription": "Optional path to a rust-analyzer specific target directory.\nThis prevents rust-analyzer's `cargo check` from locking the `Cargo.lock`\nat the expense of duplicating build artifacts.\n\nSet to `true` to use a subdirectory of the existing target directory or\nset to a path relative to the workspace to use that path.", + "default": null, + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, "rust-analyzer.rustc.source": { "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.", "default": null, @@ -1616,7 +1655,7 @@ "message": 3 }, { - "regexp": "^[\\s->=]*(.*?):(\\d*):(\\d*)\\s*$", + "regexp": "^[\\s->=]*(.*?):([1-9]\\d*):([1-9]\\d*)\\s*$", "file": 1, "line": 2, "column": 3 diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts index ba8546763ec..96e888402ba 100644 --- a/src/tools/rust-analyzer/editors/code/src/client.ts +++ b/src/tools/rust-analyzer/editors/code/src/client.ts @@ -389,6 +389,7 @@ class ExperimentalFeatures implements lc.StaticFeature { serverStatusNotification: true, colorDiagnosticOutput: true, openServerLogs: true, + localDocs: true, commands: { commands: [ "rust-analyzer.runSingle", diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 245557b1e88..3d33d255ad4 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -21,6 +21,7 @@ import type { LanguageClient } from "vscode-languageclient/node"; import { LINKED_COMMANDS } from "./client"; import type { DependencyId } from "./dependencies_provider"; import { unwrapUndefinable } from "./undefinable"; +import { log } from "./util"; export * from "./ast_inspector"; export * from "./run"; @@ -869,28 +870,6 @@ export function rebuildProcMacros(ctx: CtxInit): Cmd { return async () => ctx.client.sendRequest(ra.rebuildProcMacros); } -export function addProject(ctx: CtxInit): Cmd { - return async () => { - const extensionName = ctx.config.discoverProjectRunner; - // this command shouldn't be enabled in the first place if this isn't set. - if (!extensionName) { - return; - } - - const command = `${extensionName}.discoverWorkspaceCommand`; - const project: JsonProject = await vscode.commands.executeCommand(command); - - ctx.addToDiscoveredWorkspaces([project]); - - // this is a workaround to avoid needing writing the `rust-project.json` into - // a workspace-level VS Code-specific settings folder. We'd like to keep the - // `rust-project.json` entirely in-memory. - await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { - settings: "", - }); - }; -} - async function showReferencesImpl( client: LanguageClient | undefined, uri: string, @@ -947,10 +926,51 @@ export function openDocs(ctx: CtxInit): Cmd { const position = editor.selection.active; const textDocument = { uri: editor.document.uri.toString() }; - const doclink = await client.sendRequest(ra.openDocs, { position, textDocument }); + const docLinks = await client.sendRequest(ra.openDocs, { position, textDocument }); + log.debug(docLinks); + + let fileType = vscode.FileType.Unknown; + if (docLinks.local !== undefined) { + try { + fileType = (await vscode.workspace.fs.stat(vscode.Uri.parse(docLinks.local))).type; + } catch (e) { + log.debug("stat() threw error. Falling back to web version", e); + } + } + + let docLink = fileType & vscode.FileType.File ? docLinks.local : docLinks.web; + if (docLink) { + // instruct vscode to handle the vscode-remote link directly + if (docLink.startsWith("vscode-remote://")) { + docLink = docLink.replace("vscode-remote://", "vscode://vscode-remote/"); + } + const docUri = vscode.Uri.parse(docLink); + await vscode.env.openExternal(docUri); + } + }; +} + +export function openExternalDocs(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + const client = ctx.client; + + const position = editor.selection.active; + const textDocument = { uri: editor.document.uri.toString() }; + + const docLinks = await client.sendRequest(ra.openDocs, { position, textDocument }); - if (doclink != null) { - await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink)); + let docLink = docLinks.web; + if (docLink) { + // instruct vscode to handle the vscode-remote link directly + if (docLink.startsWith("vscode-remote://")) { + docLink = docLink.replace("vscode-remote://", "vscode://vscode-remote/"); + } + const docUri = vscode.Uri.parse(docLink); + await vscode.env.openExternal(docUri); } }; } diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 9821aee6f92..987d936943a 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -329,6 +329,10 @@ export class Config { get showDependenciesExplorer() { return this.get<boolean>("showDependenciesExplorer"); } + + get statusBarClickAction() { + return this.get<string>("statusBar.clickAction"); + } } // the optional `cb?` parameter is meant to be used to add additional diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 904efa4d5eb..63ae386c8ad 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import type * as lc from "vscode-languageclient/node"; +import * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; import { Config, prepareVSCodeConfig } from "./config"; @@ -22,6 +22,7 @@ import { import { execRevealDependency } from "./commands"; import { PersistentState } from "./persistent_state"; import { bootstrap } from "./bootstrap"; +import type { RustAnalyzerExtensionApi } from "./main"; // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if // only those are in use. We use "Empty" to represent these scenarios @@ -64,7 +65,7 @@ export type CtxInit = Ctx & { readonly client: lc.LanguageClient; }; -export class Ctx { +export class Ctx implements RustAnalyzerExtensionApi { readonly statusBar: vscode.StatusBarItem; config: Config; readonly workspace: Workspace; @@ -189,8 +190,11 @@ export class Ctx { if (this.config.discoverProjectRunner) { const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; log.info(`running command: ${command}`); - const project: JsonProject = await vscode.commands.executeCommand(command); - this.addToDiscoveredWorkspaces([project]); + const uris = vscode.workspace.textDocuments + .filter(isRustDocument) + .map((document) => document.uri); + const projects: JsonProject[] = await vscode.commands.executeCommand(command, uris); + this.setWorkspaces(projects); } if (this.workspace.kind === "Detached Files") { @@ -342,15 +346,17 @@ export class Ctx { return this._serverPath; } - addToDiscoveredWorkspaces(workspaces: JsonProject[]) { - for (const workspace of workspaces) { - const index = this.config.discoveredWorkspaces.indexOf(workspace); - if (~index) { - this.config.discoveredWorkspaces[index] = workspace; - } else { - this.config.discoveredWorkspaces.push(workspace); - } - } + setWorkspaces(workspaces: JsonProject[]) { + this.config.discoveredWorkspaces = workspaces; + } + + async notifyRustAnalyzer(): Promise<void> { + // this is a workaround to avoid needing writing the `rust-project.json` into + // a workspace-level VS Code-specific settings folder. We'd like to keep the + // `rust-project.json` entirely in-memory. + await this.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { + settings: "", + }); } private updateCommands(forceDisable?: "disable") { @@ -400,7 +406,11 @@ export class Ctx { statusBar.tooltip.appendText(status.message ?? "Ready"); statusBar.color = undefined; statusBar.backgroundColor = undefined; - statusBar.command = "rust-analyzer.openLogs"; + if (this.config.statusBarClickAction === "stopServer") { + statusBar.command = "rust-analyzer.stopServer"; + } else { + statusBar.command = "rust-analyzer.openLogs"; + } this.dependencies?.refresh(); break; case "warning": diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts index bb7896973f1..f959a76639e 100644 --- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts +++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts @@ -74,8 +74,8 @@ export interface FetchDependencyListParams {} export interface FetchDependencyListResult { crates: { - name: string | undefined; - version: string | undefined; + name?: string; + version?: string; path: string; }[]; } @@ -135,7 +135,11 @@ export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.Text export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>( "experimental/openCargoToml", ); -export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>( +export interface DocsUrls { + local?: string; + web?: string; +} +export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, DocsUrls, void>( "experimental/externalDocs", ); export const parentModule = new lc.RequestType< diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index ee5e5b1b80c..3073353674c 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -9,8 +9,12 @@ import { setContextValue } from "./util"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; +// This API is not stable and may break in between minor releases. export interface RustAnalyzerExtensionApi { readonly client?: lc.LanguageClient; + + setWorkspaces(workspaces: JsonProject[]): void; + notifyRustAnalyzer(): Promise<void>; } export async function deactivate() { @@ -152,7 +156,6 @@ function createCommands(): Record<string, CommandFactory> { shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, reloadWorkspace: { enabled: commands.reloadWorkspace }, rebuildProcMacros: { enabled: commands.rebuildProcMacros }, - addProject: { enabled: commands.addProject }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, parentModule: { enabled: commands.parentModule }, @@ -170,6 +173,7 @@ function createCommands(): Record<string, CommandFactory> { debug: { enabled: commands.debug }, newDebugConfig: { enabled: commands.newDebugConfig }, openDocs: { enabled: commands.openDocs }, + openExternalDocs: { enabled: commands.openExternalDocs }, openCargoToml: { enabled: commands.openCargoToml }, peekTests: { enabled: commands.peekTests }, moveItemUp: { enabled: commands.moveItemUp }, diff --git a/src/tools/rust-analyzer/editors/code/tsconfig.json b/src/tools/rust-analyzer/editors/code/tsconfig.json index ee353c28dd6..c74284a00d9 100644 --- a/src/tools/rust-analyzer/editors/code/tsconfig.json +++ b/src/tools/rust-analyzer/editors/code/tsconfig.json @@ -2,14 +2,14 @@ "extends": "@tsconfig/strictest/tsconfig.json", "compilerOptions": { "esModuleInterop": false, - "module": "commonjs", - "moduleResolution": "node16", - "target": "es2021", + "module": "CommonJS", + "moduleResolution": "Node16", + "target": "ES2021", "outDir": "out", - "lib": ["es2021"], + "lib": ["ES2021"], "sourceMap": true, "rootDir": ".", - "newLine": "LF", + "newLine": "lf", // FIXME: https://github.com/rust-lang/rust-analyzer/issues/15253 "exactOptionalPropertyTypes": false diff --git a/src/tools/rust-analyzer/lib/line-index/Cargo.toml b/src/tools/rust-analyzer/lib/line-index/Cargo.toml index 6c0d06f4717..b7b4a01818e 100644 --- a/src/tools/rust-analyzer/lib/line-index/Cargo.toml +++ b/src/tools/rust-analyzer/lib/line-index/Cargo.toml @@ -7,5 +7,5 @@ repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-in edition = "2021" [dependencies] -text-size = "1.1.0" +text-size = "1.1.1" nohash-hasher = "0.2.0" diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 7ec3247e943..8d00813b0d7 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] log = "0.4.17" -serde_json = "1.0.96" -serde = { version = "1.0.156", features = ["derive"] } +serde_json = "1.0.108" +serde = { version = "1.0.192", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] diff --git a/src/tools/rust-analyzer/rust-bors.toml b/src/tools/rust-analyzer/rust-bors.toml new file mode 100644 index 00000000000..c31ba66c50f --- /dev/null +++ b/src/tools/rust-analyzer/rust-bors.toml @@ -0,0 +1 @@ +timeout = 3600 diff --git a/src/tools/rust-analyzer/triagebot.toml b/src/tools/rust-analyzer/triagebot.toml index f0cd3539975..95eed3ee172 100644 --- a/src/tools/rust-analyzer/triagebot.toml +++ b/src/tools/rust-analyzer/triagebot.toml @@ -11,5 +11,10 @@ allow-unauthenticated = [ new_pr = true [no-merges] -exclude_labels = ["sync"] +exclude_titles = [ # exclude syncs from subtree in rust-lang/rust + "Sync from downstream", + "sync from downstream", + "Sync from rust", + "sync from rust", +] labels = ["has-merge-commits", "S-waiting-on-author"] diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 7a34617e255..1c785b60a3d 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" rust-version.workspace = true [dependencies] -anyhow = "1.0.62" +anyhow.workspace = true flate2 = "1.0.24" write-json = "0.1.2" -xshell = "0.2.2" +xshell.workspace = true xflags = "0.3.0" time = { version = "0.3", default-features = false } zip = { version = "0.6", default-features = false, features = ["deflate", "time"] } diff --git a/src/tools/suggest-tests/src/lib.rs b/src/tools/suggest-tests/src/lib.rs index 44cd3c7f6a8..1c1d9d0333d 100644 --- a/src/tools/suggest-tests/src/lib.rs +++ b/src/tools/suggest-tests/src/lib.rs @@ -33,13 +33,15 @@ pub fn get_suggestions<T: AsRef<str>>(modified_files: &[T]) -> Vec<Suggestion> { let mut suggestions = Vec::new(); // static suggestions - for sug in STATIC_SUGGESTIONS.iter() { - let glob = Pattern::new(&sug.0).expect("Found invalid glob pattern!"); - - for file in modified_files { - if glob.matches(file.as_ref()) { - suggestions.extend_from_slice(&sug.1); - } + for (globs, sugs) in STATIC_SUGGESTIONS.iter() { + let globs = globs + .iter() + .map(|glob| Pattern::new(glob).expect("Found invalid glob pattern!")) + .collect::<Vec<_>>(); + let matches_some_glob = |file: &str| globs.iter().any(|glob| glob.matches(file)); + + if modified_files.iter().map(AsRef::as_ref).any(matches_some_glob) { + suggestions.extend_from_slice(sugs); } } diff --git a/src/tools/suggest-tests/src/static_suggestions.rs b/src/tools/suggest-tests/src/static_suggestions.rs index a84e78254f2..fbd265ea42a 100644 --- a/src/tools/suggest-tests/src/static_suggestions.rs +++ b/src/tools/suggest-tests/src/static_suggestions.rs @@ -2,23 +2,34 @@ use crate::{sug, Suggestion}; // FIXME: perhaps this could use `std::lazy` when it is stablizied macro_rules! static_suggestions { - ($( $glob:expr => [ $( $suggestion:expr ),* ] ),*) => { - pub(crate) const STATIC_SUGGESTIONS: ::once_cell::unsync::Lazy<Vec<(&'static str, Vec<Suggestion>)>> - = ::once_cell::unsync::Lazy::new(|| vec![ $( ($glob, vec![ $($suggestion),* ]) ),*]); + ($( [ $( $glob:expr ),* $(,)? ] => [ $( $suggestion:expr ),* $(,)? ] ),* $(,)? ) => { + pub(crate) const STATIC_SUGGESTIONS: ::once_cell::unsync::Lazy<Vec<(Vec<&'static str>, Vec<Suggestion>)>> + = ::once_cell::unsync::Lazy::new(|| vec![ $( (vec![ $($glob),* ], vec![ $($suggestion),* ]) ),*]); } } static_suggestions! { - "*.md" => [ - sug!("test", 0, ["linkchecker"]) + ["*.md"] => [ + sug!("test", 0, ["linkchecker"]), ], - "compiler/*" => [ + ["compiler/*"] => [ sug!("check"), - sug!("test", 1, ["tests/ui", "tests/run-make"]) + sug!("test", 1, ["tests/ui", "tests/run-make"]), ], - "src/librustdoc/*" => [ - sug!("test", 1, ["rustdoc"]) - ] + ["compiler/rustc_mir_transform/*"] => [ + sug!("test", 1, ["mir-opt"]), + ], + + [ + "compiler/rustc_mir_transform/src/coverage/*", + "compiler/rustc_codegen_llvm/src/coverageinfo/*", + ] => [ + sug!("test", 1, ["coverage"]), + ], + + ["src/librustdoc/*"] => [ + sug!("test", 1, ["rustdoc"]), + ], } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index f88f9165596..10215520227 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -128,7 +128,6 @@ const EXCEPTIONS_CARGO: ExceptionList = &[ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-start - ("anymap", "BlueOak-1.0.0 OR MIT OR Apache-2.0"), // BlueOak is not acceptable, but we use it under MIT OR Apache-2 .0 ("dissimilar", "Apache-2.0"), ("instant", "BSD-3-Clause"), ("notify", "CC0-1.0"), diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 7e24793adee..dfa386b49de 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -10,7 +10,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: usize = 1854; +const ISSUES_ENTRY_LIMIT: usize = 1852; const ROOT_ENTRY_LIMIT: usize = 867; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ diff --git a/tests/assembly/stack-protector/stack-protector-target-support.rs b/tests/assembly/stack-protector/stack-protector-target-support.rs index e5cbace80b1..c6528ac7c8d 100644 --- a/tests/assembly/stack-protector/stack-protector-target-support.rs +++ b/tests/assembly/stack-protector/stack-protector-target-support.rs @@ -2,7 +2,7 @@ // targets, with the exception of nvptx64-nvidia-cuda // // revisions: r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 -// revisions: r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r34 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44 +// revisions: r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44 // revisions: r45 r46 r47 r48 r49 r50 r51 r52 r53 r54 r55 r56 r57 r58 r59 r60 r61 r62 r63 r64 r65 // revisions: r66 r67 r68 r69 r70 r71 r72 r73 r74 r75 r76 r77 r78 r79 r80 r81 r82 r83 r84 // assembly-output: emit-asm @@ -72,8 +72,7 @@ // [r32] needs-llvm-components: arm // [r33] compile-flags: --target armv7-unknown-linux-musleabihf // [r33] needs-llvm-components: arm -// [r34] compile-flags: --target asmjs-unknown-emscripten -// [r34] needs-llvm-components: webassembly + // [r35] compile-flags: --target i586-pc-windows-msvc // [r35] needs-llvm-components: x86 // [r36] compile-flags: --target i586-unknown-linux-gnu diff --git a/tests/codegen/llvm_module_flags.rs b/tests/codegen/llvm_module_flags.rs new file mode 100644 index 00000000000..acc035086de --- /dev/null +++ b/tests/codegen/llvm_module_flags.rs @@ -0,0 +1,7 @@ +// Test for -Z llvm_module_flags +// compile-flags: -Z llvm_module_flag=foo:u32:123:error -Z llvm_module_flag=bar:u32:42:max + +fn main() {} + +// CHECK: !{i32 1, !"foo", i32 123} +// CHECK: !{i32 7, !"bar", i32 42} diff --git a/tests/codegen/unchecked_shifts.rs b/tests/codegen/unchecked_shifts.rs index aca9bec77df..eded894c6d0 100644 --- a/tests/codegen/unchecked_shifts.rs +++ b/tests/codegen/unchecked_shifts.rs @@ -31,7 +31,7 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 { #[no_mangle] pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 { // CHECK-NOT: assume - // CHECK: %[[EXT:.+]] = zext i32 %b to i64 + // CHECK: %[[EXT:.+]] = zext{{( nneg)?}} i32 %b to i64 // CHECK: shl i64 %a, %[[EXT]] a.unchecked_shl(b) } @@ -63,7 +63,7 @@ pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 { #[no_mangle] pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 { // CHECK-NOT: assume - // CHECK: %[[EXT:.+]] = zext i32 %b to i64 + // CHECK: %[[EXT:.+]] = zext{{( nneg)?}} i32 %b to i64 // CHECK: ashr i64 %a, %[[EXT]] a.unchecked_shr(b) } diff --git a/tests/incremental/commandline-args.rs b/tests/incremental/commandline-args.rs index 35b7183db7f..e17e6feae07 100644 --- a/tests/incremental/commandline-args.rs +++ b/tests/incremental/commandline-args.rs @@ -1,7 +1,6 @@ // Test that changing a tracked commandline argument invalidates // the cache while changing an untracked one doesn't. -// ignore-asmjs wasm2js does not support source maps yet // revisions:rpass1 rpass2 rpass3 rpass4 // compile-flags: -Z query-dep-graph diff --git a/tests/incremental/remapped_paths_cc/main.rs b/tests/incremental/remapped_paths_cc/main.rs index b01f02444ea..12411a92879 100644 --- a/tests/incremental/remapped_paths_cc/main.rs +++ b/tests/incremental/remapped_paths_cc/main.rs @@ -2,7 +2,6 @@ // compile-flags: -Z query-dep-graph -g // aux-build:extern_crate.rs -// ignore-asmjs wasm2js does not support source maps yet // This test case makes sure that we detect if paths emitted into debuginfo // are changed, even when the change happens in an external crate. diff --git a/tests/incremental/span_hash_stable/main.rs b/tests/incremental/span_hash_stable/main.rs index 367416430f8..f1d7de14559 100644 --- a/tests/incremental/span_hash_stable/main.rs +++ b/tests/incremental/span_hash_stable/main.rs @@ -3,7 +3,6 @@ // the spans and this test makes sure that we handle them correctly by hashing // file:line:column instead of raw byte offset. -// ignore-asmjs wasm2js does not support source maps yet // revisions:rpass1 rpass2 // compile-flags: -g -Z query-dep-graph diff --git a/tests/incremental/spans_in_type_debuginfo.rs b/tests/incremental/spans_in_type_debuginfo.rs index f5cae15a4bc..8ed469db6e6 100644 --- a/tests/incremental/spans_in_type_debuginfo.rs +++ b/tests/incremental/spans_in_type_debuginfo.rs @@ -1,7 +1,6 @@ // Test that moving a type definition within a source file does not affect // re-compilation. -// ignore-asmjs wasm2js does not support source maps yet // revisions:rpass1 rpass2 // compile-flags: -Z query-dep-graph -g diff --git a/tests/incremental/spans_significant_w_debuginfo.rs b/tests/incremental/spans_significant_w_debuginfo.rs index 38ab2846191..a036d3e69fe 100644 --- a/tests/incremental/spans_significant_w_debuginfo.rs +++ b/tests/incremental/spans_significant_w_debuginfo.rs @@ -3,7 +3,6 @@ // revisions:rpass1 rpass2 -// ignore-asmjs wasm2js does not support source maps yet // compile-flags: -g -Z query-dep-graph #![feature(rustc_attrs)] diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 396e4a378f6..111dd8e97f9 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -14,7 +14,7 @@ Static, ), source_info: SourceInfo { - span: $DIR/async_await.rs:16:9: 16:14 (#8), + span: $DIR/async_await.rs:16:5: 16:14 (#9), scope: scope[0], }, ignore_for_traits: false, @@ -32,7 +32,7 @@ Static, ), source_info: SourceInfo { - span: $DIR/async_await.rs:17:9: 17:14 (#10), + span: $DIR/async_await.rs:17:5: 17:14 (#11), scope: scope[0], }, ignore_for_traits: false, diff --git a/tests/mir-opt/building/custom/terminators.rs b/tests/mir-opt/building/custom/terminators.rs index 9e442e0f98a..a83a6c07461 100644 --- a/tests/mir-opt/building/custom/terminators.rs +++ b/tests/mir-opt/building/custom/terminators.rs @@ -13,7 +13,7 @@ fn ident<T>(t: T) -> T { fn direct_call(x: i32) -> i32 { mir!( { - Call(RET = ident(x), retblock) + Call(RET = ident(x), retblock, UnwindContinue()) } retblock = { @@ -27,7 +27,7 @@ fn direct_call(x: i32) -> i32 { fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 { mir!( { - Call(RET = f(x), retblock) + Call(RET = f(x), retblock, UnwindContinue()) } retblock = { @@ -49,7 +49,7 @@ impl<'a> Drop for WriteOnDrop<'a> { fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) { mir!( { - Drop(a, retblock) + Drop(a, retblock, UnwindContinue()) } retblock = { @@ -64,7 +64,7 @@ fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) { fn drop_second<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) { mir!( { - Drop(b, retblock) + Drop(b, retblock, UnwindContinue()) } retblock = { diff --git a/tests/mir-opt/building/custom/unwind_action.rs b/tests/mir-opt/building/custom/unwind_action.rs new file mode 100644 index 00000000000..e3c4ffac358 --- /dev/null +++ b/tests/mir-opt/building/custom/unwind_action.rs @@ -0,0 +1,68 @@ +// compile-flags: --crate-type=lib +// edition:2021 +// needs-unwind +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +// CHECK-LABEL: fn a() +// CHECK: bb0: { +// CHECK-NEXT: a() -> [return: bb1, unwind unreachable]; +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn a() { + mir!( + { + Call(RET = a(), bb1, UnwindUnreachable()) + } + bb1 = { + Return() + } + ) +} + +// CHECK-LABEL: fn b() +// CHECK: bb0: { +// CHECK-NEXT: b() -> [return: bb1, unwind continue]; +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn b() { + mir!( + { + Call(RET = b(), bb1, UnwindContinue()) + } + bb1 = { + Return() + } + ) +} + +// CHECK-LABEL: fn c() +// CHECK: bb0: { +// CHECK-NEXT: c() -> [return: bb1, unwind terminate(abi)]; +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn c() { + mir!( + { + Call(RET = c(), bb1, UnwindTerminate(ReasonAbi)) + } + bb1 = { + Return() + } + ) +} + +// CHECK-LABEL: fn d() +// CHECK: bb0: { +// CHECK-NEXT: d() -> [return: bb1, unwind: bb2]; +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn d() { + mir!( + { + Call(RET = d(), bb1, UnwindCleanup(bb2)) + } + bb1 = { + Return() + } + bb2 (cleanup) = { + UnwindResume() + } + ) +} diff --git a/tests/mir-opt/building/custom/unwind_terminate.rs b/tests/mir-opt/building/custom/unwind_terminate.rs new file mode 100644 index 00000000000..efdf2ddb1d0 --- /dev/null +++ b/tests/mir-opt/building/custom/unwind_terminate.rs @@ -0,0 +1,34 @@ +// compile-flags: --crate-type=lib +// edition:2021 +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +// CHECK-LABEL: fn f() +// CHECK: bb1 (cleanup): { +// CHECK-NEXT: terminate(abi); +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn f() { + mir!( + { + Return() + } + bb1(cleanup) = { + UnwindTerminate(ReasonAbi) + } + ) +} + +// CHECK-LABEL: fn g() +// CHECK: bb1 (cleanup): { +// CHECK-NEXT: terminate(cleanup); +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn g() { + mir!( + { + Return() + } + bb1(cleanup) = { + UnwindTerminate(ReasonInCleanup) + } + ) +} diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs index c6b8ad3571f..a44e65164af 100644 --- a/tests/mir-opt/copy-prop/borrowed_local.rs +++ b/tests/mir-opt/copy-prop/borrowed_local.rs @@ -22,11 +22,11 @@ fn f() -> bool { let b = a; // We cannot propagate the place `a`. let r2 = &b; - Call(RET = cmp_ref(r1, r2), next) + Call(RET = cmp_ref(r1, r2), next, UnwindContinue()) } next = { // But we can propagate the value `a`. - Call(RET = opaque(b), ret) + Call(RET = opaque(b), ret, UnwindContinue()) } ret = { Return() diff --git a/tests/mir-opt/copy-prop/calls.rs b/tests/mir-opt/copy-prop/calls.rs index 2970f5f0b8d..bc6760707cc 100644 --- a/tests/mir-opt/copy-prop/calls.rs +++ b/tests/mir-opt/copy-prop/calls.rs @@ -26,7 +26,7 @@ fn multiple_edges(t: bool) -> u8 { match t { true => bbt, _ => ret } } bbt = { - Call(x = dummy(13), ret) + Call(x = dummy(13), ret, UnwindContinue()) } ret = { // `x` is not assigned on the `bb0 -> ret` edge, diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs index 2077874ee9a..8593d9fa9ab 100644 --- a/tests/mir-opt/copy-prop/custom_move_arg.rs +++ b/tests/mir-opt/copy-prop/custom_move_arg.rs @@ -14,11 +14,11 @@ struct NotCopy(bool); fn f(_1: NotCopy) { mir!({ let _2 = _1; - Call(RET = opaque(Move(_1)), bb1) + Call(RET = opaque(Move(_1)), bb1, UnwindContinue()) } bb1 = { let _3 = Move(_2); - Call(RET = opaque(_3), bb2) + Call(RET = opaque(_3), bb2, UnwindContinue()) } bb2 = { Return() diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs index 8629d535bcf..438a90dddd0 100644 --- a/tests/mir-opt/copy-prop/move_projection.rs +++ b/tests/mir-opt/copy-prop/move_projection.rs @@ -18,10 +18,10 @@ fn f(a: Foo) -> bool { let b = a; // This is a move out of a copy, so must become a copy of `a.0`. let c = Move(b.0); - Call(RET = opaque(Move(a)), bb1) + Call(RET = opaque(Move(a)), bb1, UnwindContinue()) } bb1 = { - Call(RET = opaque(Move(c)), ret) + Call(RET = opaque(Move(c)), ret, UnwindContinue()) } ret = { Return() diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index dcd15fb2b09..b2eb64756f9 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -28,7 +28,7 @@ struct Packed { fn move_packed(packed: Packed) { mir!( { - Call(RET = use_both(0, packed.y), ret) + Call(RET = use_both(0, packed.y), ret, UnwindContinue()) } ret = { Return() diff --git a/tests/mir-opt/dead-store-elimination/cycle.rs b/tests/mir-opt/dead-store-elimination/cycle.rs index 8896f5ff345..c9ad06a9da2 100644 --- a/tests/mir-opt/dead-store-elimination/cycle.rs +++ b/tests/mir-opt/dead-store-elimination/cycle.rs @@ -20,7 +20,7 @@ fn cycle(mut x: i32, mut y: i32, mut z: i32) { mir!( let condition: bool; { - Call(condition = cond(), bb1) + Call(condition = cond(), bb1, UnwindContinue()) } bb1 = { match condition { true => bb2, _ => ret } @@ -30,7 +30,7 @@ fn cycle(mut x: i32, mut y: i32, mut z: i32) { z = y; y = x; x = temp; - Call(condition = cond(), bb1) + Call(condition = cond(), bb1, UnwindContinue()) } ret = { Return() diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 10a66ced026..6e082acdbd3 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -529,31 +529,31 @@ fn duplicate_slice() -> (bool, bool) { // CHECK: [[a:_.*]] = (const "a",); // CHECK: [[au:_.*]] = ([[a]].0: &str) as u128 (Transmute); let a = ("a",); - Call(au = transmute::<_, u128>(a.0), bb1) + Call(au = transmute::<_, u128>(a.0), bb1, UnwindContinue()) } bb1 = { // CHECK: [[c:_.*]] = identity::<&str>(([[a]].0: &str)) - Call(c = identity(a.0), bb2) + Call(c = identity(a.0), bb2, UnwindContinue()) } bb2 = { // CHECK: [[cu:_.*]] = [[c]] as u128 (Transmute); - Call(cu = transmute::<_, u128>(c), bb3) + Call(cu = transmute::<_, u128>(c), bb3, UnwindContinue()) } bb3 = { // This slice is different from `a.0`. Hence `bu` is not `au`. // CHECK: [[b:_.*]] = const "a"; // CHECK: [[bu:_.*]] = [[b]] as u128 (Transmute); let b = "a"; - Call(bu = transmute::<_, u128>(b), bb4) + Call(bu = transmute::<_, u128>(b), bb4, UnwindContinue()) } bb4 = { // This returns a copy of `b`, which is not `a`. // CHECK: [[d:_.*]] = identity::<&str>([[b]]) - Call(d = identity(b), bb5) + Call(d = identity(b), bb5, UnwindContinue()) } bb5 = { // CHECK: [[du:_.*]] = [[d]] as u128 (Transmute); - Call(du = transmute::<_, u128>(d), bb6) + Call(du = transmute::<_, u128>(d), bb6, UnwindContinue()) } bb6 = { // `direct` must not fold to `true`, as `indirect` will not. diff --git a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.panic-abort.diff deleted file mode 100644 index 194478560e9..00000000000 --- a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.panic-abort.diff +++ /dev/null @@ -1,48 +0,0 @@ -- // MIR for `option_payload` before LowerIntrinsics -+ // MIR for `option_payload` after LowerIntrinsics - - fn option_payload(_1: &Option<usize>, _2: &Option<String>) -> () { - debug o => _1; - debug p => _2; - let mut _0: (); - let mut _4: *const std::option::Option<usize>; - let mut _6: *const std::option::Option<std::string::String>; - scope 1 { - let _3: *const usize; - scope 2 { - debug _x => _3; - let _5: *const std::string::String; - scope 3 { - debug _y => _5; - } - } - } - - bb0: { - StorageLive(_3); - StorageLive(_4); - _4 = &raw const (*_1); -- _3 = option_payload_ptr::<usize>(move _4) -> [return: bb1, unwind unreachable]; -+ _3 = &raw const (((*_4) as Some).0: usize); -+ goto -> bb1; - } - - bb1: { - StorageDead(_4); - StorageLive(_5); - StorageLive(_6); - _6 = &raw const (*_2); -- _5 = option_payload_ptr::<String>(move _6) -> [return: bb2, unwind unreachable]; -+ _5 = &raw const (((*_6) as Some).0: std::string::String); -+ goto -> bb2; - } - - bb2: { - StorageDead(_6); - _0 = const (); - StorageDead(_5); - StorageDead(_3); - return; - } - } - diff --git a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.panic-unwind.diff deleted file mode 100644 index 194478560e9..00000000000 --- a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.panic-unwind.diff +++ /dev/null @@ -1,48 +0,0 @@ -- // MIR for `option_payload` before LowerIntrinsics -+ // MIR for `option_payload` after LowerIntrinsics - - fn option_payload(_1: &Option<usize>, _2: &Option<String>) -> () { - debug o => _1; - debug p => _2; - let mut _0: (); - let mut _4: *const std::option::Option<usize>; - let mut _6: *const std::option::Option<std::string::String>; - scope 1 { - let _3: *const usize; - scope 2 { - debug _x => _3; - let _5: *const std::string::String; - scope 3 { - debug _y => _5; - } - } - } - - bb0: { - StorageLive(_3); - StorageLive(_4); - _4 = &raw const (*_1); -- _3 = option_payload_ptr::<usize>(move _4) -> [return: bb1, unwind unreachable]; -+ _3 = &raw const (((*_4) as Some).0: usize); -+ goto -> bb1; - } - - bb1: { - StorageDead(_4); - StorageLive(_5); - StorageLive(_6); - _6 = &raw const (*_2); -- _5 = option_payload_ptr::<String>(move _6) -> [return: bb2, unwind unreachable]; -+ _5 = &raw const (((*_6) as Some).0: std::string::String); -+ goto -> bb2; - } - - bb2: { - StorageDead(_6); - _0 = const (); - StorageDead(_5); - StorageDead(_3); - return; - } - } - diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 913605cc2b2..cba2bc18d86 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -222,18 +222,6 @@ pub fn write_via_move_string(r: &mut String, v: String) { pub enum Never {} -// EMIT_MIR lower_intrinsics.option_payload.LowerIntrinsics.diff -pub fn option_payload(o: &Option<usize>, p: &Option<String>) { - // CHECK-LABEL: fn option_payload( - // CHECK: {{_.*}} = &raw const (((*{{_.*}}) as Some).0: usize); - // CHECK: {{_.*}} = &raw const (((*{{_.*}}) as Some).0: std::string::String); - - unsafe { - let _x = core::intrinsics::option_payload_ptr(o); - let _y = core::intrinsics::option_payload_ptr(p); - } -} - // EMIT_MIR lower_intrinsics.ptr_offset.LowerIntrinsics.diff pub unsafe fn ptr_offset(p: *const i32, d: isize) -> *const i32 { // CHECK-LABEL: fn ptr_offset( diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index 36134e019ad..1b9c8fe15c2 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -695,7 +695,7 @@ fn multiple_storage() { // As there are multiple `StorageLive` statements for `x`, we cannot know if this `z`'s // pointer address is the address of `x`, so do nothing. let y = *z; - Call(RET = opaque(y), retblock) + Call(RET = opaque(y), retblock, UnwindContinue()) } retblock = { @@ -723,7 +723,7 @@ fn dominate_storage() { } bb1 = { let c = *r; - Call(RET = opaque(c), bb2) + Call(RET = opaque(c), bb2, UnwindContinue()) } bb2 = { StorageDead(x); @@ -759,18 +759,18 @@ fn maybe_dead(m: bool) { bb1 = { StorageDead(x); StorageDead(y); - Call(RET = opaque(u), bb2) + Call(RET = opaque(u), bb2, UnwindContinue()) } bb2 = { // As `x` may be `StorageDead`, `a` may be dangling, so we do nothing. let z = *a; - Call(RET = opaque(z), bb3) + Call(RET = opaque(z), bb3, UnwindContinue()) } bb3 = { // As `y` may be `StorageDead`, `b` may be dangling, so we do nothing. // This implies that we also do not substitute `b` in `bb0`. let t = *b; - Call(RET = opaque(t), retblock) + Call(RET = opaque(t), retblock, UnwindContinue()) } retblock = { Return() diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs index 1a94649163b..9be0fdccebe 100644 --- a/tests/run-make-fulldeps/issue-19371/foo.rs +++ b/tests/run-make-fulldeps/issue-19371/foo.rs @@ -72,6 +72,6 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) { let ongoing_codegen = queries.ongoing_codegen()?; queries.linker(ongoing_codegen) }); - linker.unwrap().link().unwrap(); + linker.unwrap().link(compiler.session(), compiler.codegen_backend()).unwrap(); }); } diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs index aeb68bf05e1..39980ee7c67 100644 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs +++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs @@ -1,6 +1,4 @@ // Test the `rustc::span_use_eq_ctxt` internal lint -// #[cfg(bootstrap)] -// ignore-stage1 // compile-flags: -Z unstable-options #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr index 3d8a7dd1ec0..b33f6212545 100644 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr +++ b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr @@ -1,11 +1,11 @@ error: use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` - --> $DIR/span_use_eq_ctxt.rs:14:5 + --> $DIR/span_use_eq_ctxt.rs:12:5 | LL | s.ctxt() == t.ctxt() | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/span_use_eq_ctxt.rs:7:9 + --> $DIR/span_use_eq_ctxt.rs:5:9 | LL | #![deny(rustc::span_use_eq_ctxt)] | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index a340877752d..e5a480cd61d 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -49,7 +49,7 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { assert!(generic.iter().all(|item| mir::mono::Instance::try_from(*item).is_err())); for instance in instances { - test_body(instance.body()) + test_body(instance.body().unwrap()) } ControlFlow::Continue(()) } @@ -61,8 +61,10 @@ fn test_body(body: mir::Body) { Call { func, .. } => { let TyKind::RigidTy(ty) = func.ty(body.locals()).kind() else { unreachable!() }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; - let result = Instance::resolve(def, &args); - assert!(result.is_ok()); + let instance = Instance::resolve(def, &args).unwrap(); + let mangled_name = instance.mangled_name(); + let body = instance.body(); + assert!(body.is_some() || mangled_name == "setpwent", "Failed: {func:?}"); } Goto { .. } | Assert { .. } | SwitchInt { .. } | Return | Drop { .. } => { /* Do nothing */ @@ -105,10 +107,16 @@ fn generate_input(path: &str) -> std::io::Result<()> { LEN > 0 && a[0] }} + extern "C" {{ + // Body should not be available. + fn setpwent(); + }} + pub fn monomorphic() {{ let v = vec![10]; let dup = ty_param(&v); assert_eq!(v, dup); + unsafe {{ setpwent() }}; }} pub mod foo {{ diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index ed6b786f5e1..025ed1b6a95 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -22,6 +22,7 @@ extern crate stable_mir; use rustc_hir::def::DefKind; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; +use stable_mir::ItemKind; use stable_mir::mir::mono::Instance; use stable_mir::ty::{RigidTy, TyKind}; use std::assert_matches::assert_matches; @@ -120,13 +121,13 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { let monomorphic = get_item(&items, (DefKind::Fn, "monomorphic")).unwrap(); let instance = Instance::try_from(monomorphic.clone()).unwrap(); - for block in instance.body().blocks { + for block in instance.body().unwrap().blocks { match &block.terminator.kind { stable_mir::mir::TerminatorKind::Call { func, .. } => { let TyKind::RigidTy(ty) = func.ty(&body.locals()).kind() else { unreachable!() }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; let next_func = Instance::resolve(def, &args).unwrap(); - match next_func.body().locals()[1].ty.kind() { + match next_func.body().unwrap().locals()[1].ty.kind() { TyKind::RigidTy(RigidTy::Uint(_)) | TyKind::RigidTy(RigidTy::Tuple(_)) => {} other => panic!("{other:?}"), } @@ -172,7 +173,8 @@ fn get_item<'a>( item: (DefKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { - crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1 + matches!((item.0, crate_item.kind()), (DefKind::Fn, ItemKind::Fn) | (DefKind::Const, + ItemKind::Const)) && crate_item.name() == item.1 }) } diff --git a/tests/ui-fulldeps/stable-mir/projections.rs b/tests/ui-fulldeps/stable-mir/projections.rs new file mode 100644 index 00000000000..d00f17d206b --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/projections.rs @@ -0,0 +1,173 @@ +// run-pass +// Tests the Stable MIR projections API + +// ignore-stage1 +// ignore-cross-compile +// ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +// edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +extern crate rustc_hir; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::mir::{ProjectionElem, Rvalue, StatementKind}; +use stable_mir::ty::{RigidTy, TyKind}; +use stable_mir::ItemKind; +use std::assert_matches::assert_matches; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +/// Tests projections within Place objects +fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { + let items = stable_mir::all_local_items(); + let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().body(); + assert_eq!(body.blocks.len(), 4); + // The first statement assigns `&s.c` to a local. The projections include a deref for `s`, since + // `s` is passed as a reference argument, and a field access for field `c`. + match &body.blocks[0].statements[0].kind { + StatementKind::Assign( + stable_mir::mir::Place { local: _, projection: local_proj }, + Rvalue::Ref(_, _, stable_mir::mir::Place { local: _, projection: r_proj }), + ) => { + // We can't match on vecs, only on slices. Comparing statements for equality wouldn't be + // any easier since we'd then have to add in the expected local and region values + // instead of matching on wildcards. + assert!(local_proj.is_empty()); + match &r_proj[..] { + // Similarly we can't match against a type, only against its kind. + [ProjectionElem::Deref, ProjectionElem::Field(2, ty)] => assert_matches!( + ty.kind(), + TyKind::RigidTy(RigidTy::Uint(stable_mir::ty::UintTy::U8)) + ), + other => panic!( + "Unable to match against expected rvalue projection. Expected the projection \ + for `s.c`, which is a Deref and u8 Field. Got: {:?}", + other + ), + }; + } + other => panic!( + "Unable to match against expected Assign statement with a Ref rvalue. Expected the \ + statement to assign `&s.c` to a local. Got: {:?}", + other + ), + }; + // This statement assigns `slice[1]` to a local. The projections include a deref for `slice`, + // since `slice` is a reference, and an index. + match &body.blocks[2].statements[0].kind { + StatementKind::Assign( + stable_mir::mir::Place { local: _, projection: local_proj }, + Rvalue::Use(stable_mir::mir::Operand::Copy(stable_mir::mir::Place { + local: _, + projection: r_proj, + })), + ) => { + // We can't match on vecs, only on slices. Comparing for equality wouldn't be any easier + // since we'd then have to add in the expected local values instead of matching on + // wildcards. + assert!(local_proj.is_empty()); + assert_matches!(r_proj[..], [ProjectionElem::Deref, ProjectionElem::Index(_)]); + } + other => panic!( + "Unable to match against expected Assign statement with a Use rvalue. Expected the \ + statement to assign `slice[1]` to a local. Got: {:?}", + other + ), + }; + // The first terminator gets a slice of an array via the Index operation. Specifically it + // performs `&vals[1..3]`. There are no projections in this case, the arguments are just locals. + match &body.blocks[0].terminator.kind { + stable_mir::mir::TerminatorKind::Call { args, .. } => + // We can't match on vecs, only on slices. Comparing for equality wouldn't be any easier + // since we'd then have to add in the expected local values instead of matching on + // wildcards. + { + match &args[..] { + [ + stable_mir::mir::Operand::Move(stable_mir::mir::Place { + local: _, + projection: arg1_proj, + }), + stable_mir::mir::Operand::Move(stable_mir::mir::Place { + local: _, + projection: arg2_proj, + }), + ] => { + assert!(arg1_proj.is_empty()); + assert!(arg2_proj.is_empty()); + } + other => { + panic!( + "Unable to match against expected arguments to Index call. Expected two \ + move operands. Got: {:?}", + other + ) + } + } + } + other => panic!( + "Unable to match against expected Call terminator. Expected a terminator that calls \ + the Index operation. Got: {:?}", + other + ), + }; + + ControlFlow::Continue(()) +} + +// Use internal API to find a function in a crate. +fn get_item<'a>( + items: &'a stable_mir::CrateItems, + item: (ItemKind, &str), +) -> Option<&'a stable_mir::CrateItem> { + items.iter().find(|crate_item| { + crate_item.kind() == item.0 && crate_item.name() == item.1 + }) +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, tcx, test_place_projections(tcx)).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + pub struct Struct1 {{ _a: u8, _b: u16, c: u8 }} + + pub fn projections(s: &Struct1) -> u8 {{ + let v = &s.c; + let vals = [1, 2, 3, 4]; + let slice = &vals[1..3]; + v + slice[1] + }}"# + )?; + Ok(()) +} diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs index de5148bb5f4..3ec63efcc06 100644 --- a/tests/ui-fulldeps/stable-mir/smir_visitor.rs +++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs @@ -40,7 +40,7 @@ fn test_visitor(_tcx: TyCtxt<'_>) -> ControlFlow<()> { let exit_fn = main_visitor.calls.last().unwrap(); assert!(exit_fn.mangled_name().contains("exit_fn"), "Unexpected last function: {exit_fn:?}"); - let exit_body = exit_fn.body(); + let exit_body = exit_fn.body().unwrap(); let exit_visitor = TestVisitor::collect(&exit_body); assert!(exit_visitor.ret_val.is_some()); assert_eq!(exit_visitor.args.len(), 1); diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 0cdf229711a..53e1eff9d72 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -231,8 +231,7 @@ macro_rules! test_abi_compatible { }; } -// Compatibility of pointers is probably de-facto guaranteed, -// but that does not seem to be documented. +// Compatibility of pointers. test_abi_compatible!(ptr_mut, *const i32, *mut i32); test_abi_compatible!(ptr_pointee, *const i32, *const Vec<i32>); test_abi_compatible!(ref_mut, &i32, &mut i32); @@ -241,14 +240,15 @@ test_abi_compatible!(box_ptr, Box<i32>, *const i32); test_abi_compatible!(nonnull_ptr, NonNull<i32>, *const i32); test_abi_compatible!(fn_fn, fn(), fn(i32) -> i32); -// Some further guarantees we will likely (have to) make. +// Compatibility of 1-ZST. test_abi_compatible!(zst_unit, Zst, ()); #[cfg(not(any(target_arch = "sparc64")))] test_abi_compatible!(zst_array, Zst, [u8; 0]); test_abi_compatible!(nonzero_int, NonZeroI32, i32); // `DispatchFromDyn` relies on ABI compatibility. -// This is interesting since these types are not `repr(transparent)`. +// This is interesting since these types are not `repr(transparent)`. So this is not part of our +// public ABI guarantees, but is relied on by the compiler. test_abi_compatible!(rc, Rc<i32>, *mut i32); test_abi_compatible!(arc, Arc<i32>, *mut i32); diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index a952ea07793..1862177005f 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -8,11 +8,6 @@ use std::ffi::VaList; extern "C" { fn rust_interesting_average(_: u64, ...) -> f64; - // FIXME: we need to disable this lint for `VaList`, - // since it contains a `MaybeUninit<i32>` on the asmjs target, - // and this type isn't FFI-safe. This is OK for now, - // since the type is layout-compatible with `i32`. - #[cfg_attr(target_arch = "asmjs", allow(improper_ctypes))] fn rust_valist_interesting_average(_: u64, _: VaList) -> f64; } diff --git a/tests/ui/async-await/issue-60709.rs b/tests/ui/async-await/issue-60709.rs index 2cda40e9e11..c206f01b98f 100644 --- a/tests/ui/async-await/issue-60709.rs +++ b/tests/ui/async-await/issue-60709.rs @@ -3,7 +3,6 @@ // compile-flags: -Copt-level=z -Cdebuginfo=2 --edition=2018 // run-pass -// ignore-asmjs wasm2js does not support source maps yet use std::future::Future; use std::task::Poll; diff --git a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs index 4919e0a051d..4a3195174df 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs +++ b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.rs @@ -23,6 +23,7 @@ fn await_on_struct_similar() { let x = S { awai: 42 }; x.await; //~^ ERROR no field `await` on type + //~| NOTE unknown field //~| HELP a field with a similar name exists //~| NOTE to `.await` a `Future`, switch to Rust 2018 //~| HELP set `edition = "2021"` in `Cargo.toml` @@ -41,6 +42,7 @@ fn await_on_63533(x: Pin<&mut dyn Future<Output = ()>>) { fn await_on_apit(x: impl Future<Output = ()>) { x.await; //~^ ERROR no field `await` on type + //~| NOTE unknown field //~| NOTE to `.await` a `Future`, switch to Rust 2018 //~| HELP set `edition = "2021"` in `Cargo.toml` //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide diff --git a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr index 409eb179e83..dd863ca541c 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr +++ b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr @@ -12,14 +12,18 @@ error[E0609]: no field `await` on type `await_on_struct_similar::S` --> $DIR/suggest-switching-edition-on-await-cargo.rs:24:7 | LL | x.await; - | ^^^^^ help: a field with a similar name exists: `awai` + | ^^^^^ unknown field | = note: to `.await` a `Future`, switch to Rust 2018 or later = help: set `edition = "2021"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide +help: a field with a similar name exists + | +LL | x.awai; + | ~~~~ error[E0609]: no field `await` on type `Pin<&mut dyn Future<Output = ()>>` - --> $DIR/suggest-switching-edition-on-await-cargo.rs:33:7 + --> $DIR/suggest-switching-edition-on-await-cargo.rs:34:7 | LL | x.await; | ^^^^^ unknown field @@ -29,10 +33,10 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0609]: no field `await` on type `impl Future<Output = ()>` - --> $DIR/suggest-switching-edition-on-await-cargo.rs:42:7 + --> $DIR/suggest-switching-edition-on-await-cargo.rs:43:7 | LL | x.await; - | ^^^^^ + | ^^^^^ unknown field | = note: to `.await` a `Future`, switch to Rust 2018 or later = help: set `edition = "2021"` in `Cargo.toml` diff --git a/tests/ui/async-await/suggest-switching-edition-on-await.rs b/tests/ui/async-await/suggest-switching-edition-on-await.rs index 9852e8fc918..10e245796ee 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await.rs +++ b/tests/ui/async-await/suggest-switching-edition-on-await.rs @@ -21,6 +21,7 @@ fn await_on_struct_similar() { let x = S { awai: 42 }; x.await; //~^ ERROR no field `await` on type + //~| NOTE unknown field //~| HELP a field with a similar name exists //~| NOTE to `.await` a `Future`, switch to Rust 2018 //~| HELP pass `--edition 2021` to `rustc` @@ -39,6 +40,7 @@ fn await_on_63533(x: Pin<&mut dyn Future<Output = ()>>) { fn await_on_apit(x: impl Future<Output = ()>) { x.await; //~^ ERROR no field `await` on type + //~| NOTE unknown field //~| NOTE to `.await` a `Future`, switch to Rust 2018 //~| HELP pass `--edition 2021` to `rustc` //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide diff --git a/tests/ui/async-await/suggest-switching-edition-on-await.stderr b/tests/ui/async-await/suggest-switching-edition-on-await.stderr index ef3334381b7..0ed256b059f 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await.stderr +++ b/tests/ui/async-await/suggest-switching-edition-on-await.stderr @@ -12,14 +12,18 @@ error[E0609]: no field `await` on type `await_on_struct_similar::S` --> $DIR/suggest-switching-edition-on-await.rs:22:7 | LL | x.await; - | ^^^^^ help: a field with a similar name exists: `awai` + | ^^^^^ unknown field | = note: to `.await` a `Future`, switch to Rust 2018 or later = help: pass `--edition 2021` to `rustc` = note: for more on editions, read https://doc.rust-lang.org/edition-guide +help: a field with a similar name exists + | +LL | x.awai; + | ~~~~ error[E0609]: no field `await` on type `Pin<&mut dyn Future<Output = ()>>` - --> $DIR/suggest-switching-edition-on-await.rs:31:7 + --> $DIR/suggest-switching-edition-on-await.rs:32:7 | LL | x.await; | ^^^^^ unknown field @@ -29,10 +33,10 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0609]: no field `await` on type `impl Future<Output = ()>` - --> $DIR/suggest-switching-edition-on-await.rs:40:7 + --> $DIR/suggest-switching-edition-on-await.rs:41:7 | LL | x.await; - | ^^^^^ + | ^^^^^ unknown field | = note: to `.await` a `Future`, switch to Rust 2018 or later = help: pass `--edition 2021` to `rustc` diff --git a/tests/ui/binding/match-arm-statics.rs b/tests/ui/binding/match-arm-statics.rs index e6d17def147..5f7e357eeb2 100644 --- a/tests/ui/binding/match-arm-statics.rs +++ b/tests/ui/binding/match-arm-statics.rs @@ -1,7 +1,6 @@ // run-pass #![allow(dead_code)] // compile-flags: -g -// ignore-asmjs wasm2js does not support source maps yet #[derive(PartialEq, Eq)] struct NewBool(bool); diff --git a/tests/ui/binop/binary-op-suggest-deref.fixed b/tests/ui/binop/binary-op-suggest-deref.fixed new file mode 100644 index 00000000000..1ff3599137b --- /dev/null +++ b/tests/ui/binop/binary-op-suggest-deref.fixed @@ -0,0 +1,8 @@ +// Issue #52544 +// run-rustfix + +fn main() { + let i: &i64 = &1; + if *i < 0 {} + //~^ ERROR mismatched types [E0308] +} diff --git a/tests/ui/binop/binary-op-suggest-deref.rs b/tests/ui/binop/binary-op-suggest-deref.rs new file mode 100644 index 00000000000..12505a9ac27 --- /dev/null +++ b/tests/ui/binop/binary-op-suggest-deref.rs @@ -0,0 +1,8 @@ +// Issue #52544 +// run-rustfix + +fn main() { + let i: &i64 = &1; + if i < 0 {} + //~^ ERROR mismatched types [E0308] +} diff --git a/tests/ui/binop/binary-op-suggest-deref.stderr b/tests/ui/binop/binary-op-suggest-deref.stderr new file mode 100644 index 00000000000..1b7e45c7724 --- /dev/null +++ b/tests/ui/binop/binary-op-suggest-deref.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/binary-op-suggest-deref.rs:6:12 + | +LL | if i < 0 {} + | ^ expected `&i64`, found integer + | +help: consider dereferencing the borrow + | +LL | if *i < 0 {} + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/cfg/conditional-compile-arch.rs b/tests/ui/cfg/conditional-compile-arch.rs index e59e06f801b..c6ecf480736 100644 --- a/tests/ui/cfg/conditional-compile-arch.rs +++ b/tests/ui/cfg/conditional-compile-arch.rs @@ -28,9 +28,6 @@ pub fn main() { } #[cfg(target_arch = "s390x")] pub fn main() { } -#[cfg(target_arch = "asmjs")] -pub fn main() { } - #[cfg(target_arch = "wasm32")] pub fn main() { } diff --git a/tests/ui/consts/effect_param.rs b/tests/ui/consts/effect_param.rs index f11ec739fce..bfe0a519037 100644 --- a/tests/ui/consts/effect_param.rs +++ b/tests/ui/consts/effect_param.rs @@ -3,9 +3,13 @@ fn main() { i8::checked_sub::<true>(42, 43); //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied + i8::checked_sub::<false>(42, 43); + //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied } const FOO: () = { i8::checked_sub::<false>(42, 43); //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied + i8::checked_sub::<true>(42, 43); + //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied }; diff --git a/tests/ui/consts/effect_param.stderr b/tests/ui/consts/effect_param.stderr index f8c4bfc02e5..dba5d49b792 100644 --- a/tests/ui/consts/effect_param.stderr +++ b/tests/ui/consts/effect_param.stderr @@ -1,5 +1,5 @@ error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/effect_param.rs:9:9 + --> $DIR/effect_param.rs:11:9 | LL | i8::checked_sub::<false>(42, 43); | ^^^^^^^^^^^--------- help: remove these generics @@ -7,6 +7,14 @@ LL | i8::checked_sub::<false>(42, 43); | expected 0 generic arguments error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/effect_param.rs:13:9 + | +LL | i8::checked_sub::<true>(42, 43); + | ^^^^^^^^^^^-------- help: remove these generics + | | + | expected 0 generic arguments + +error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied --> $DIR/effect_param.rs:4:9 | LL | i8::checked_sub::<true>(42, 43); @@ -14,6 +22,14 @@ LL | i8::checked_sub::<true>(42, 43); | | | expected 0 generic arguments -error: aborting due to 2 previous errors +error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/effect_param.rs:6:9 + | +LL | i8::checked_sub::<false>(42, 43); + | ^^^^^^^^^^^--------- help: remove these generics + | | + | expected 0 generic arguments + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/coroutine/issue-58888.rs b/tests/ui/coroutine/issue-58888.rs index af8e60ce460..9c699c7bb82 100644 --- a/tests/ui/coroutine/issue-58888.rs +++ b/tests/ui/coroutine/issue-58888.rs @@ -1,6 +1,5 @@ // run-pass // compile-flags: -g -// ignore-asmjs wasm2js does not support source maps yet #![feature(coroutines, coroutine_trait)] diff --git a/tests/ui/coroutine/size-moved-locals.rs b/tests/ui/coroutine/size-moved-locals.rs index cfbbb9c1b31..10f988cc066 100644 --- a/tests/ui/coroutine/size-moved-locals.rs +++ b/tests/ui/coroutine/size-moved-locals.rs @@ -11,7 +11,6 @@ // edition:2018 // ignore-wasm32 issue #62807 -// ignore-asmjs issue #62807 // needs-unwind Size of Closures change on panic=abort #![feature(coroutines, coroutine_trait)] diff --git a/tests/ui/derived-errors/issue-30580.stderr b/tests/ui/derived-errors/issue-30580.stderr index 7bd0eaf77a9..4e60368c387 100644 --- a/tests/ui/derived-errors/issue-30580.stderr +++ b/tests/ui/derived-errors/issue-30580.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `c` on type `&Foo` --> $DIR/issue-30580.rs:12:11 | LL | b.c; - | ^ help: a field with a similar name exists: `a` + | ^ unknown field + | +help: a field with a similar name exists + | +LL | b.a; + | ~ error: aborting due to previous error diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs index 8410b3eb105..0893f29c4a3 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs @@ -8,6 +8,8 @@ note = "custom note" )] #[diagnostic::on_unimplemented(message = "fallback!!")] +//~^ `message` is ignored due to previous definition of `message` +//~| `message` is ignored due to previous definition of `message` #[diagnostic::on_unimplemented(label = "fallback label")] #[diagnostic::on_unimplemented(note = "fallback note")] trait Foo {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr index 906472beb49..fc4aa8ef7d8 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr @@ -7,6 +7,15 @@ LL | if(Self = "()"), = help: only `message`, `note` and `label` are allowed as options = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default +warning: `message` is ignored due to previous definition of `message` + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32 + | +LL | message = "custom message", + | -------------------------- `message` is first declared here +... +LL | #[diagnostic::on_unimplemented(message = "fallback!!")] + | ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here + warning: malformed `on_unimplemented` attribute --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 | @@ -16,8 +25,19 @@ LL | if(Self = "()"), = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +warning: `message` is ignored due to previous definition of `message` + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32 + | +LL | message = "custom message", + | -------------------------- `message` is first declared here +... +LL | #[diagnostic::on_unimplemented(message = "fallback!!")] + | ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0277]: custom message - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:18:15 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15 | LL | takes_foo(()); | --------- ^^ fallback label @@ -28,16 +48,16 @@ LL | takes_foo(()); = note: custom note = note: fallback note help: this trait has no implementations, consider adding one - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:13:1 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:22 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^ required by this bound in `takes_foo` -error: aborting due to previous error; 2 warnings emitted +error: aborting due to previous error; 4 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs new file mode 100644 index 00000000000..a7becd2f88f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs @@ -0,0 +1,25 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented( + message = "first message", + label = "first label", + note = "custom note" +)] +#[diagnostic::on_unimplemented( + message = "second message", + //~^WARN `message` is ignored due to previous definition of `message` + //~|WARN `message` is ignored due to previous definition of `message` + label = "second label", + //~^WARN `label` is ignored due to previous definition of `label` + //~|WARN `label` is ignored due to previous definition of `label` + note = "second note" +)] +trait Foo {} + + +fn takes_foo(_: impl Foo) {} + +fn main() { + takes_foo(()); + //~^ERROR first message +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr new file mode 100644 index 00000000000..f9395ae85bc --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr @@ -0,0 +1,67 @@ +warning: `message` is ignored due to previous definition of `message` + --> $DIR/report_warning_on_duplicated_options.rs:9:5 + | +LL | message = "first message", + | ------------------------- `message` is first declared here +... +LL | message = "second message", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: `label` is ignored due to previous definition of `label` + --> $DIR/report_warning_on_duplicated_options.rs:12:5 + | +LL | label = "first label", + | --------------------- `label` is first declared here +... +LL | label = "second label", + | ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here + +warning: `message` is ignored due to previous definition of `message` + --> $DIR/report_warning_on_duplicated_options.rs:9:5 + | +LL | message = "first message", + | ------------------------- `message` is first declared here +... +LL | message = "second message", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: `label` is ignored due to previous definition of `label` + --> $DIR/report_warning_on_duplicated_options.rs:12:5 + | +LL | label = "first label", + | --------------------- `label` is first declared here +... +LL | label = "second label", + | ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: first message + --> $DIR/report_warning_on_duplicated_options.rs:23:15 + | +LL | takes_foo(()); + | --------- ^^ first label + | | + | required by a bound introduced by this call + | + = help: the trait `Foo` is not implemented for `()` + = note: custom note + = note: second note +help: this trait has no implementations, consider adding one + --> $DIR/report_warning_on_duplicated_options.rs:17:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required by a bound in `takes_foo` + --> $DIR/report_warning_on_duplicated_options.rs:20:22 + | +LL | fn takes_foo(_: impl Foo) {} + | ^^^ required by this bound in `takes_foo` + +error: aborting due to previous error; 4 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.rs b/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.rs index 6040f3f30a7..ffc37b260a6 100644 --- a/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.rs +++ b/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.rs @@ -29,7 +29,7 @@ fn main() { doc_hidden_fields::B::default().hey; //~^ ERROR no field `hey` on type `B` //~| NOTE unknown field - //~| NOTE available fields are: `bye` + //~| NOTE available field is: `bye` C::default().hey; //~^ ERROR no field `hey` on type `C` diff --git a/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.stderr b/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.stderr index b7fe3b79b47..55f6d0fa323 100644 --- a/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.stderr +++ b/tests/ui/did_you_mean/dont-suggest-doc-hidden-fields.stderr @@ -12,7 +12,7 @@ error[E0609]: no field `hey` on type `B` LL | doc_hidden_fields::B::default().hey; | ^^^ unknown field | - = note: available fields are: `bye` + = note: available field is: `bye` error[E0609]: no field `hey` on type `C` --> $DIR/dont-suggest-doc-hidden-fields.rs:34:18 diff --git a/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr b/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr index 7066d29760e..473c9a339fc 100644 --- a/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr +++ b/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr @@ -5,9 +5,13 @@ LL | environment!(); | -------------- in this macro invocation ... LL | const CRATE: Crate = Crate { fiel: () }; - | ^^^^ help: a field with a similar name exists: `field` + | ^^^^ unknown field | = note: this error originates in the macro `environment` (in Nightly builds, run with -Z macro-backtrace for more info) +help: a field with a similar name exists + | +LL | const CRATE: Crate = Crate { field: () }; + | ~~~~~ error[E0609]: no field `field` on type `Compound` --> $DIR/dont-suggest-hygienic-fields.rs:24:16 diff --git a/tests/ui/did_you_mean/issue-36798.stderr b/tests/ui/did_you_mean/issue-36798.stderr index 98876e305ca..9f82d4c44cf 100644 --- a/tests/ui/did_you_mean/issue-36798.stderr +++ b/tests/ui/did_you_mean/issue-36798.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `baz` on type `Foo` --> $DIR/issue-36798.rs:7:7 | LL | f.baz; - | ^^^ help: a field with a similar name exists: `bar` + | ^^^ unknown field + | +help: a field with a similar name exists + | +LL | f.bar; + | ~~~ error: aborting due to previous error diff --git a/tests/ui/did_you_mean/issue-36798_unknown_field.stderr b/tests/ui/did_you_mean/issue-36798_unknown_field.stderr index 2ed0a092400..4f216568979 100644 --- a/tests/ui/did_you_mean/issue-36798_unknown_field.stderr +++ b/tests/ui/did_you_mean/issue-36798_unknown_field.stderr @@ -4,7 +4,7 @@ error[E0609]: no field `zz` on type `Foo` LL | f.zz; | ^^ unknown field | - = note: available fields are: `bar` + = note: available field is: `bar` error: aborting due to previous error diff --git a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr index c20bbce3f24..d60db01a465 100644 --- a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -2,7 +2,12 @@ error[E0560]: struct `Demo` has no field named `inocently_mispellable` --> $DIR/issue-42599_available_fields_note.rs:16:39 | LL | Self { secret_integer: 2, inocently_mispellable: () } - | ^^^^^^^^^^^^^^^^^^^^^ help: a field with a similar name exists: `innocently_misspellable` + | ^^^^^^^^^^^^^^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | Self { secret_integer: 2, innocently_misspellable: () } + | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0560]: struct `Demo` has no field named `egregiously_nonexistent_field` --> $DIR/issue-42599_available_fields_note.rs:21:39 @@ -16,7 +21,12 @@ error[E0609]: no field `inocently_mispellable` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:32:41 | LL | let innocent_field_misaccess = demo.inocently_mispellable; - | ^^^^^^^^^^^^^^^^^^^^^ help: a field with a similar name exists: `innocently_misspellable` + | ^^^^^^^^^^^^^^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let innocent_field_misaccess = demo.innocently_misspellable; + | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0609]: no field `egregiously_nonexistent_field` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:35:42 diff --git a/tests/ui/error-codes/E0609.stderr b/tests/ui/error-codes/E0609.stderr index 797e95d02dd..cad857553da 100644 --- a/tests/ui/error-codes/E0609.stderr +++ b/tests/ui/error-codes/E0609.stderr @@ -4,7 +4,7 @@ error[E0609]: no field `foo` on type `Foo` LL | let _ = x.foo; | ^^^ unknown field | - = note: available fields are: `x` + = note: available field is: `x` error[E0609]: no field `1` on type `Bar` --> $DIR/E0609.rs:11:7 diff --git a/tests/ui/error-codes/ex-E0612.stderr b/tests/ui/error-codes/ex-E0612.stderr index b21b6fdfcf1..4dd9848cf07 100644 --- a/tests/ui/error-codes/ex-E0612.stderr +++ b/tests/ui/error-codes/ex-E0612.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `1` on type `Foo` --> $DIR/ex-E0612.rs:5:6 | LL | y.1; - | ^ help: a field with a similar name exists: `0` + | ^ unknown field + | +help: a field with a similar name exists + | +LL | y.0; + | ~ error: aborting due to previous error diff --git a/tests/ui/extern/extern-const.fixed b/tests/ui/extern/extern-const.fixed index 9d96b4f63fb..248efc93d00 100644 --- a/tests/ui/extern/extern-const.fixed +++ b/tests/ui/extern/extern-const.fixed @@ -6,7 +6,6 @@ // run-rustfix // ignore-wasm32-bare no external library to link to. -// ignore-asmjs wasm2js does not support source maps yet // compile-flags: -g #![feature(rustc_private)] extern crate libc; diff --git a/tests/ui/extern/extern-const.rs b/tests/ui/extern/extern-const.rs index 7cef5b3497b..d3b3bef6dae 100644 --- a/tests/ui/extern/extern-const.rs +++ b/tests/ui/extern/extern-const.rs @@ -6,7 +6,6 @@ // run-rustfix // ignore-wasm32-bare no external library to link to. -// ignore-asmjs wasm2js does not support source maps yet // compile-flags: -g #![feature(rustc_private)] extern crate libc; diff --git a/tests/ui/extern/extern-const.stderr b/tests/ui/extern/extern-const.stderr index 7f67adbdb19..a296751994e 100644 --- a/tests/ui/extern/extern-const.stderr +++ b/tests/ui/extern/extern-const.stderr @@ -1,5 +1,5 @@ error: extern items cannot be `const` - --> $DIR/extern-const.rs:16:11 + --> $DIR/extern-const.rs:15:11 | LL | const rust_dbg_static_mut: libc::c_int; | ------^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs new file mode 100644 index 00000000000..aee5db83669 --- /dev/null +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs @@ -0,0 +1,14 @@ +fn assert_all<F, T>(_f: F) +where + F: FnMut(&String) -> T, +{ +} + +fn id(x: &String) -> &String { + x +} + +fn main() { + assert_all::<_, &String>(id); + //~^ mismatched types +} diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr new file mode 100644 index 00000000000..890dac16e94 --- /dev/null +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-lifetime-error.rs:12:5 + | +LL | assert_all::<_, &String>(id); + | ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected reference `&String` + found reference `&String` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/auto-trait-coherence.next.stderr b/tests/ui/impl-trait/auto-trait-coherence.next.stderr index 7833ac688ba..cee359997b4 100644 --- a/tests/ui/impl-trait/auto-trait-coherence.next.stderr +++ b/tests/ui/impl-trait/auto-trait-coherence.next.stderr @@ -6,6 +6,8 @@ LL | impl<T: Send> AnotherTrait for T {} ... LL | impl AnotherTrait for D<OpaqueType> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>` + | + = note: upstream crates may add a new impl of trait `std::marker::Send` for type `OpaqueType` in future versions error: aborting due to previous error diff --git a/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr b/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr index 84b61dc5044..4cd4febc4f0 100644 --- a/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr +++ b/tests/ui/impl-trait/erased-regions-in-hidden-ty.current.stderr @@ -1,4 +1,4 @@ -error: {foo<ReEarlyBound(DefId(..), 0, 'a)>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} +error: {foo<ReEarlyParam(DefId(..), 0, 'a)>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} --> $DIR/erased-regions-in-hidden-ty.rs:11:36 | LL | fn foo<'a: 'a>(x: &'a Vec<i32>) -> impl Fn() + 'static { diff --git a/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr b/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr index 84b61dc5044..4cd4febc4f0 100644 --- a/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr +++ b/tests/ui/impl-trait/erased-regions-in-hidden-ty.next.stderr @@ -1,4 +1,4 @@ -error: {foo<ReEarlyBound(DefId(..), 0, 'a)>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} +error: {foo<ReEarlyParam(DefId(..), 0, 'a)>::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=()} --> $DIR/erased-regions-in-hidden-ty.rs:11:36 | LL | fn foo<'a: 'a>(x: &'a Vec<i32>) -> impl Fn() + 'static { diff --git a/tests/ui/impl-trait/recursive-coroutine.stderr b/tests/ui/impl-trait/recursive-coroutine.current.stderr index d36a58a8643..45911b7fc11 100644 --- a/tests/ui/impl-trait/recursive-coroutine.stderr +++ b/tests/ui/impl-trait/recursive-coroutine.current.stderr @@ -1,5 +1,5 @@ error[E0720]: cannot resolve opaque type - --> $DIR/recursive-coroutine.rs:5:13 + --> $DIR/recursive-coroutine.rs:7:13 | LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type diff --git a/tests/ui/impl-trait/recursive-coroutine.next.stderr b/tests/ui/impl-trait/recursive-coroutine.next.stderr new file mode 100644 index 00000000000..45911b7fc11 --- /dev/null +++ b/tests/ui/impl-trait/recursive-coroutine.next.stderr @@ -0,0 +1,12 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-coroutine.rs:7:13 + | +LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type +... +LL | let mut gen = Box::pin(foo()); + | ------- coroutine captures itself here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-coroutine.rs b/tests/ui/impl-trait/recursive-coroutine.rs index 6351cef95a6..f0bee4f120f 100644 --- a/tests/ui/impl-trait/recursive-coroutine.rs +++ b/tests/ui/impl-trait/recursive-coroutine.rs @@ -1,3 +1,5 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next #![feature(coroutines, coroutine_trait)] use std::ops::{Coroutine, CoroutineState}; diff --git a/tests/ui/impl-trait/two_tait_defining_each_other.stderr b/tests/ui/impl-trait/two_tait_defining_each_other.current.stderr index 1a42ac525a6..e5f7e5e5c44 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other.current.stderr @@ -1,16 +1,16 @@ error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/two_tait_defining_each_other.rs:12:5 + --> $DIR/two_tait_defining_each_other.rs:16:5 | LL | x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other | ^ one of the two opaque types used here has to be outside its defining scope | note: opaque type whose hidden type is being assigned - --> $DIR/two_tait_defining_each_other.rs:4:10 + --> $DIR/two_tait_defining_each_other.rs:8:10 | LL | type B = impl Foo; | ^^^^^^^^ note: opaque type being used as hidden type - --> $DIR/two_tait_defining_each_other.rs:3:10 + --> $DIR/two_tait_defining_each_other.rs:7:10 | LL | type A = impl Foo; | ^^^^^^^^ diff --git a/tests/ui/impl-trait/two_tait_defining_each_other.rs b/tests/ui/impl-trait/two_tait_defining_each_other.rs index 6eb2a11b22c..b43a7cabd05 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other.rs @@ -1,3 +1,7 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next +//[next] check-pass + #![feature(type_alias_impl_trait)] type A = impl Foo; @@ -10,7 +14,7 @@ fn muh(x: A) -> B { return Bar; // B's hidden type is Bar } x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other - //~^ ERROR opaque type's hidden type cannot be another opaque type + //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } struct Bar; diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr index 4d8f96de162..33866451c6b 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr @@ -1,5 +1,5 @@ error: unconstrained opaque type - --> $DIR/two_tait_defining_each_other2.rs:3:10 + --> $DIR/two_tait_defining_each_other2.rs:5:10 | LL | type A = impl Foo; | ^^^^^^^^ @@ -7,18 +7,18 @@ LL | type A = impl Foo; = note: `A` must be used in combination with a concrete type within the same module error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/two_tait_defining_each_other2.rs:9:5 + --> $DIR/two_tait_defining_each_other2.rs:11:5 | LL | x // B's hidden type is A (opaquely) | ^ one of the two opaque types used here has to be outside its defining scope | note: opaque type whose hidden type is being assigned - --> $DIR/two_tait_defining_each_other2.rs:4:10 + --> $DIR/two_tait_defining_each_other2.rs:6:10 | LL | type B = impl Foo; | ^^^^^^^^ note: opaque type being used as hidden type - --> $DIR/two_tait_defining_each_other2.rs:3:10 + --> $DIR/two_tait_defining_each_other2.rs:5:10 | LL | type A = impl Foo; | ^^^^^^^^ diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr new file mode 100644 index 00000000000..e3a4797e44c --- /dev/null +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `A <: B` + --> $DIR/two_tait_defining_each_other2.rs:11:5 + | +LL | x // B's hidden type is A (opaquely) + | ^ cannot satisfy `A <: B` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs index 05b09668016..817de109fbe 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs @@ -1,13 +1,16 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next #![feature(type_alias_impl_trait)] -type A = impl Foo; //~ ERROR unconstrained opaque type +type A = impl Foo; //[current]~ ERROR unconstrained opaque type type B = impl Foo; trait Foo {} fn muh(x: A) -> B { x // B's hidden type is A (opaquely) - //~^ ERROR opaque type's hidden type cannot be another opaque type + //[current]~^ ERROR opaque type's hidden type cannot be another opaque type + //[next]~^^ ERROR type annotations needed: cannot satisfy `A <: B` } struct Bar; diff --git a/tests/ui/impl-trait/two_tait_defining_each_other3.stderr b/tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr index b06dc16d5e7..451ba407b71 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other3.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr @@ -1,16 +1,16 @@ error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/two_tait_defining_each_other3.rs:10:16 + --> $DIR/two_tait_defining_each_other3.rs:13:16 | LL | return x; // B's hidden type is A (opaquely) | ^ one of the two opaque types used here has to be outside its defining scope | note: opaque type whose hidden type is being assigned - --> $DIR/two_tait_defining_each_other3.rs:4:10 + --> $DIR/two_tait_defining_each_other3.rs:7:10 | LL | type B = impl Foo; | ^^^^^^^^ note: opaque type being used as hidden type - --> $DIR/two_tait_defining_each_other3.rs:3:10 + --> $DIR/two_tait_defining_each_other3.rs:6:10 | LL | type A = impl Foo; | ^^^^^^^^ diff --git a/tests/ui/impl-trait/two_tait_defining_each_other3.rs b/tests/ui/impl-trait/two_tait_defining_each_other3.rs index 37f8ae1b84b..6fb9e6e7188 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other3.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other3.rs @@ -1,3 +1,6 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next +//[next] check-pass #![feature(type_alias_impl_trait)] type A = impl Foo; @@ -8,7 +11,7 @@ trait Foo {} fn muh(x: A) -> B { if false { return x; // B's hidden type is A (opaquely) - //~^ ERROR opaque type's hidden type cannot be another opaque type + //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } Bar // A's hidden type is `Bar`, because all the return types are compared with each other } diff --git a/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr b/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr new file mode 100644 index 00000000000..a591d0f5d4d --- /dev/null +++ b/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr @@ -0,0 +1,15 @@ +error[E0477]: the type `&'lt u8` does not fulfill the required lifetime + --> $DIR/normalization-placeholder-leak.rs:31:40 + | +LL | fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} + | ^^^^^^^^^^^^ + +error[E0477]: the type `<T as AnotherTrait>::Ty2<'lt>` does not fulfill the required lifetime + --> $DIR/normalization-placeholder-leak.rs:36:44 + | +LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo<T::Ty2::<'lt>>) {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0477`. diff --git a/tests/ui/implied-bounds/normalization-placeholder-leak.rs b/tests/ui/implied-bounds/normalization-placeholder-leak.rs new file mode 100644 index 00000000000..5350dcbb233 --- /dev/null +++ b/tests/ui/implied-bounds/normalization-placeholder-leak.rs @@ -0,0 +1,56 @@ +// Because of #109628, when we compute the implied bounds from `Foo<X>`, +// we incorrectly get `X: placeholder('x)`. +// Make sure we ignore these bogus bounds and not use them for anything useful. +// +// revisions: fail pass +// [fail] check-fail +// [pass] check-pass + +trait Trait { + type Ty<'a> where Self: 'a; +} + +impl<T> Trait for T { + type Ty<'a> = () where Self: 'a; +} + +struct Foo<T: Trait>(T) +where + for<'x> T::Ty<'x>: Sized; + +trait AnotherTrait { + type Ty2<'a>: 'a; +} + +#[cfg(fail)] +mod fail { + use super::*; + + // implied_bound: `'lt: placeholder('x)`. + // don't use the bound to prove `'lt: 'static`. + fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} + //[fail]~^ ERROR `&'lt u8` does not fulfill the required lifetime + + // implied bound: `T::Ty2<'lt>: placeholder('x)`. + // don't use the bound to prove `T::Ty2<'lt>: 'static`. + fn test_alias<'lt, T: AnotherTrait>(_: Foo<T::Ty2::<'lt>>) {} + //[fail]~^ ERROR `<T as AnotherTrait>::Ty2<'lt>` does not fulfill the required lifetime +} + + +mod pass { + use super::*; + + // implied_bound: 'static: placeholder('x). + // don't ice. + fn test_lifetime<T: Trait>(_: Foo<&'static u8>) {} + + // implied bound: T::Ty2<'static>: placeholder('x). + // don't add the bound to the environment, + // otherwise we would fail to infer a value for `'_`. + fn test_alias<T: AnotherTrait>(_: Foo<T::Ty2::<'static>>) { + None::<&'static T::Ty2<'_>>; + } +} + +fn main() {} diff --git a/tests/ui/indexing/indexing-requires-a-uint.stderr b/tests/ui/indexing/indexing-requires-a-uint.stderr index 6ea6bb600e9..3041c2c99a1 100644 --- a/tests/ui/indexing/indexing-requires-a-uint.stderr +++ b/tests/ui/indexing/indexing-requires-a-uint.stderr @@ -8,6 +8,8 @@ LL | [0][0u8]; = help: the trait `SliceIndex<[{integer}]>` is implemented for `usize` = help: for that trait implementation, expected `usize`, found `u8` = note: required for `[{integer}]` to implement `Index<u8>` + = note: 1 redundant requirement hidden + = note: required for `[{integer}; 1]` to implement `Index<u8>` error[E0308]: mismatched types --> $DIR/indexing-requires-a-uint.rs:12:18 diff --git a/tests/ui/indexing/point-at-index-for-obligation-failure.rs b/tests/ui/indexing/point-at-index-for-obligation-failure.rs new file mode 100644 index 00000000000..e9c429b53ce --- /dev/null +++ b/tests/ui/indexing/point-at-index-for-obligation-failure.rs @@ -0,0 +1,7 @@ +fn main() { + let a = std::collections::HashMap::<String,String>::new(); + let s = "hello"; + let _b = a[ + &s //~ ERROR E0277 + ]; +} diff --git a/tests/ui/indexing/point-at-index-for-obligation-failure.stderr b/tests/ui/indexing/point-at-index-for-obligation-failure.stderr new file mode 100644 index 00000000000..3e2fbc2ab6f --- /dev/null +++ b/tests/ui/indexing/point-at-index-for-obligation-failure.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `String: Borrow<&str>` is not satisfied + --> $DIR/point-at-index-for-obligation-failure.rs:5:9 + | +LL | &s + | ^^ the trait `Borrow<&str>` is not implemented for `String` + | + = help: the trait `Borrow<str>` is implemented for `String` + = help: for that trait implementation, expected `str`, found `&str` + = note: required for `HashMap<String, String>` to implement `Index<&&str>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-11004.stderr b/tests/ui/issues/issue-11004.stderr index b5831e42e39..ea141e61df8 100644 --- a/tests/ui/issues/issue-11004.stderr +++ b/tests/ui/issues/issue-11004.stderr @@ -3,7 +3,8 @@ error[E0609]: no field `x` on type `*mut A` | LL | let x : i32 = n.x; | --^ - | | + | | | + | | unknown field | help: `n` is a raw pointer; try dereferencing it: `(*n).x` error[E0609]: no field `y` on type `*mut A` @@ -11,7 +12,8 @@ error[E0609]: no field `y` on type `*mut A` | LL | let y : f64 = n.y; | --^ - | | + | | | + | | unknown field | help: `n` is a raw pointer; try dereferencing it: `(*n).y` error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-13847.stderr b/tests/ui/issues/issue-13847.stderr index 52b8dc04970..ded927693b2 100644 --- a/tests/ui/issues/issue-13847.stderr +++ b/tests/ui/issues/issue-13847.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `is_failure` on type `!` --> $DIR/issue-13847.rs:2:12 | LL | return.is_failure - | ^^^^^^^^^^ + | ^^^^^^^^^^ unknown field error: aborting due to previous error diff --git a/tests/ui/issues/issue-14721.stderr b/tests/ui/issues/issue-14721.stderr index 49ebb2976e8..f07bc7ad4f1 100644 --- a/tests/ui/issues/issue-14721.stderr +++ b/tests/ui/issues/issue-14721.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `desc` on type `&str` --> $DIR/issue-14721.rs:3:24 | LL | println!("{}", foo.desc); - | ^^^^ + | ^^^^ unknown field error: aborting due to previous error diff --git a/tests/ui/issues/issue-18804/main.rs b/tests/ui/issues/issue-18804/main.rs index c36048ea545..47c3f13d23c 100644 --- a/tests/ui/issues/issue-18804/main.rs +++ b/tests/ui/issues/issue-18804/main.rs @@ -2,7 +2,6 @@ // Test for issue #18804, #[linkage] does not propagate through generic // functions. Failure results in a linker error. -// ignore-asmjs no weak symbol support // ignore-emscripten no weak symbol support // ignore-windows no extern_weak linkage // ignore-macos no extern_weak linkage diff --git a/tests/ui/issues/issue-19244-1.stderr b/tests/ui/issues/issue-19244-1.stderr index 1eb530542c0..3358cac82e0 100644 --- a/tests/ui/issues/issue-19244-1.stderr +++ b/tests/ui/issues/issue-19244-1.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `1` on type `(usize,)` --> $DIR/issue-19244-1.rs:4:24 | LL | let a: [isize; TUP.1]; - | ^ + | ^ unknown field error: aborting due to previous error diff --git a/tests/ui/issues/issue-19244-2.stderr b/tests/ui/issues/issue-19244-2.stderr index 54529fdf5ba..71655457f90 100644 --- a/tests/ui/issues/issue-19244-2.stderr +++ b/tests/ui/issues/issue-19244-2.stderr @@ -4,7 +4,7 @@ error[E0609]: no field `nonexistent_field` on type `MyStruct` LL | let a: [isize; STRUCT.nonexistent_field]; | ^^^^^^^^^^^^^^^^^ unknown field | - = note: available fields are: `field` + = note: available field is: `field` error: aborting due to previous error diff --git a/tests/ui/issues/issue-22468.stderr b/tests/ui/issues/issue-22468.stderr index 3fff91acbc2..fb2b9b42859 100644 --- a/tests/ui/issues/issue-22468.stderr +++ b/tests/ui/issues/issue-22468.stderr @@ -7,6 +7,9 @@ LL | let x = foo("baz"); | ^^^------- | | | call expression requires function +... +LL | fn foo(file: &str) -> bool { + | -------------------------- this function of the same name is available here, but it's shadowed by the local binding error: aborting due to previous error diff --git a/tests/ui/issues/issue-23253.stderr b/tests/ui/issues/issue-23253.stderr index be5714cd91a..44f01c1c167 100644 --- a/tests/ui/issues/issue-23253.stderr +++ b/tests/ui/issues/issue-23253.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `a` on type `Foo` --> $DIR/issue-23253.rs:4:14 | LL | Foo::Bar.a; - | ^ + | ^ unknown field error: aborting due to previous error diff --git a/tests/ui/issues/issue-23477.rs b/tests/ui/issues/issue-23477.rs index 988ebe03ccf..1ce05ba390d 100644 --- a/tests/ui/issues/issue-23477.rs +++ b/tests/ui/issues/issue-23477.rs @@ -1,5 +1,4 @@ // build-pass -// ignore-asmjs wasm2js does not support source maps yet // compile-flags: -g pub struct Dst { diff --git a/tests/ui/issues/issue-24365.stderr b/tests/ui/issues/issue-24365.stderr index f9eead8a4f2..3f6ed0231d8 100644 --- a/tests/ui/issues/issue-24365.stderr +++ b/tests/ui/issues/issue-24365.stderr @@ -2,19 +2,19 @@ error[E0609]: no field `b` on type `Foo` --> $DIR/issue-24365.rs:10:22 | LL | println!("{}", a.b); - | ^ + | ^ unknown field error[E0609]: no field `attr_name_idx` on type `&Attribute` --> $DIR/issue-24365.rs:17:18 | LL | let z = (&x).attr_name_idx; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ unknown field error[E0609]: no field `attr_name_idx` on type `Attribute` --> $DIR/issue-24365.rs:18:15 | LL | let y = x.attr_name_idx; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ unknown field error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-24687-embed-debuginfo/main.rs b/tests/ui/issues/issue-24687-embed-debuginfo/main.rs index f08bcdfe6d1..773792c7a3f 100644 --- a/tests/ui/issues/issue-24687-embed-debuginfo/main.rs +++ b/tests/ui/issues/issue-24687-embed-debuginfo/main.rs @@ -1,7 +1,6 @@ // run-pass // aux-build:issue-24687-lib.rs // compile-flags:-g -// ignore-asmjs wasm2js does not support source maps yet extern crate issue_24687_lib as d; diff --git a/tests/ui/issues/issue-24945-repeat-dash-opts.rs b/tests/ui/issues/issue-24945-repeat-dash-opts.rs index 0f92fc2f7f3..cf3834952c6 100644 --- a/tests/ui/issues/issue-24945-repeat-dash-opts.rs +++ b/tests/ui/issues/issue-24945-repeat-dash-opts.rs @@ -3,7 +3,6 @@ // as options to the compiler. // compile-flags:-g -g -O -O -// ignore-asmjs wasm2js does not support source maps yet fn main() { assert_eq!(1, 1); diff --git a/tests/ui/issues/issue-26484.rs b/tests/ui/issues/issue-26484.rs index 2a8750d3e43..3b40b3dd8f0 100644 --- a/tests/ui/issues/issue-26484.rs +++ b/tests/ui/issues/issue-26484.rs @@ -1,6 +1,5 @@ // run-pass // compile-flags:-g -// ignore-asmjs wasm2js does not support source maps yet fn helper<F: FnOnce(usize) -> bool>(_f: F) { print!(""); diff --git a/tests/ui/issues/issue-31011.stderr b/tests/ui/issues/issue-31011.stderr index 58c170409fd..4971e3357b4 100644 --- a/tests/ui/issues/issue-31011.stderr +++ b/tests/ui/issues/issue-31011.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `trace` on type `&T` --> $DIR/issue-31011.rs:3:17 | LL | if $ctx.trace { - | ^^^^^ + | ^^^^^ unknown field ... LL | fn wrap<T>(context: &T) -> () | - type parameter 'T' declared here diff --git a/tests/ui/issues/issue-33096.rs b/tests/ui/issues/issue-33096.rs index 2501e1430b3..f0b472e2fe8 100644 --- a/tests/ui/issues/issue-33096.rs +++ b/tests/ui/issues/issue-33096.rs @@ -1,6 +1,5 @@ // run-pass // compile-flags: -g -// ignore-asmjs wasm2js does not support source maps yet use std::ops::Deref; diff --git a/tests/ui/issues/issue-33525.stderr b/tests/ui/issues/issue-33525.stderr index f8d703dc3b1..ee9f4d4c301 100644 --- a/tests/ui/issues/issue-33525.stderr +++ b/tests/ui/issues/issue-33525.stderr @@ -8,13 +8,13 @@ error[E0609]: no field `lorem` on type `&'static str` --> $DIR/issue-33525.rs:3:8 | LL | "".lorem; - | ^^^^^ + | ^^^^^ unknown field error[E0609]: no field `ipsum` on type `&'static str` --> $DIR/issue-33525.rs:4:8 | LL | "".ipsum; - | ^^^^^ + | ^^^^^ unknown field error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-34569.rs b/tests/ui/issues/issue-34569.rs index 88dcdd41138..1f68560509e 100644 --- a/tests/ui/issues/issue-34569.rs +++ b/tests/ui/issues/issue-34569.rs @@ -1,6 +1,5 @@ // run-pass // compile-flags:-g -// ignore-asmjs wasm2js does not support source maps yet // In this test we just want to make sure that the code below does not lead to // a debuginfo verification assertion during compilation. This was caused by the diff --git a/tests/ui/issues/issue-36856.rs b/tests/ui/issues/issue-36856.rs index 5657ba69f94..f2dfaf3dd36 100644 --- a/tests/ui/issues/issue-36856.rs +++ b/tests/ui/issues/issue-36856.rs @@ -2,7 +2,6 @@ // Regression test for #36856. // compile-flags:-g -// ignore-asmjs wasm2js does not support source maps yet fn g() -> bool { false diff --git a/tests/ui/issues/issue-39848.rs b/tests/ui/issues/issue-39848.rs index 1964d739989..2a059120e81 100644 --- a/tests/ui/issues/issue-39848.rs +++ b/tests/ui/issues/issue-39848.rs @@ -1,6 +1,6 @@ macro_rules! get_opt { ($tgt:expr, $field:ident) => { - if $tgt.has_$field() {} //~ ERROR expected `{`, found `foo` + if $tgt.has_$field() {} //~ ERROR expected `{`, found identifier `foo` } } diff --git a/tests/ui/issues/issue-39848.stderr b/tests/ui/issues/issue-39848.stderr index 387ef0776ff..f98fde43784 100644 --- a/tests/ui/issues/issue-39848.stderr +++ b/tests/ui/issues/issue-39848.stderr @@ -1,4 +1,4 @@ -error: expected `{`, found `foo` +error: expected `{`, found identifier `foo` --> $DIR/issue-39848.rs:3:21 | LL | if $tgt.has_$field() {} diff --git a/tests/ui/issues/issue-42210.rs b/tests/ui/issues/issue-42210.rs index 01a5d563639..318e3099f98 100644 --- a/tests/ui/issues/issue-42210.rs +++ b/tests/ui/issues/issue-42210.rs @@ -2,7 +2,6 @@ // Regression test for #42210. // compile-flags: -g -// ignore-asmjs wasm2js does not support source maps yet trait Foo { fn foo() { } diff --git a/tests/ui/issues/issue-45731.rs b/tests/ui/issues/issue-45731.rs index 5c5ac59873a..d20c07276a8 100644 --- a/tests/ui/issues/issue-45731.rs +++ b/tests/ui/issues/issue-45731.rs @@ -1,7 +1,6 @@ // run-pass #![allow(unused_variables)] // compile-flags:--test -g -// ignore-asmjs wasm2js does not support source maps yet #[cfg(target_os = "macos")] #[test] diff --git a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr b/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr index 5e1b816defd..0aa1ae7222f 100644 --- a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr +++ b/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `00` on type `Verdict` --> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:8:30 | LL | let _condemned = justice.00; - | ^^ help: a field with a similar name exists: `0` + | ^^ unknown field + | +help: a field with a similar name exists + | +LL | let _condemned = justice.0; + | ~ error[E0609]: no field `001` on type `Verdict` --> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:10:31 diff --git a/tests/ui/issues/issue-58463.rs b/tests/ui/issues/issue-58463.rs index af93f76221d..9573c9b703a 100644 --- a/tests/ui/issues/issue-58463.rs +++ b/tests/ui/issues/issue-58463.rs @@ -1,6 +1,5 @@ // run-pass // compile-flags:-C debuginfo=2 -// ignore-asmjs wasm2js does not support source maps yet fn foo() -> impl Copy { foo diff --git a/tests/ui/lto/debuginfo-lto.rs b/tests/ui/lto/debuginfo-lto.rs index 43f75b0344b..e4beee9e737 100644 --- a/tests/ui/lto/debuginfo-lto.rs +++ b/tests/ui/lto/debuginfo-lto.rs @@ -7,7 +7,6 @@ // aux-build:debuginfo-lto-aux.rs // compile-flags: -C lto -g // no-prefer-dynamic -// ignore-asmjs wasm2js does not support source maps yet extern crate debuginfo_lto_aux; diff --git a/tests/ui/macros/nonterminal-matching.stderr b/tests/ui/macros/nonterminal-matching.stderr index c2b047022ed..88c2c1c773d 100644 --- a/tests/ui/macros/nonterminal-matching.stderr +++ b/tests/ui/macros/nonterminal-matching.stderr @@ -1,6 +1,8 @@ error: no rules expected the token `enum E {}` --> $DIR/nonterminal-matching.rs:19:10 | +LL | macro complex_nonterminal($nt_item: item) { + | -------------- LL | macro n(a $nt_item b) { | --------------------- when calling this macro ... diff --git a/tests/ui/macros/syntax-error-recovery.rs b/tests/ui/macros/syntax-error-recovery.rs index ae6de3c5046..f6178c137db 100644 --- a/tests/ui/macros/syntax-error-recovery.rs +++ b/tests/ui/macros/syntax-error-recovery.rs @@ -9,7 +9,7 @@ macro_rules! values { } }; } -//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)` +//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found type `(String)` //~| ERROR macro expansion ignores token `(String)` and any following values!(STRING(1) as (String) => cfg(test),); diff --git a/tests/ui/macros/syntax-error-recovery.stderr b/tests/ui/macros/syntax-error-recovery.stderr index c42ee9b295e..6218bf43a1e 100644 --- a/tests/ui/macros/syntax-error-recovery.stderr +++ b/tests/ui/macros/syntax-error-recovery.stderr @@ -1,4 +1,4 @@ -error: expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)` +error: expected one of `(`, `,`, `=`, `{`, or `}`, found type `(String)` --> $DIR/syntax-error-recovery.rs:7:26 | LL | $token $($inner)? = $value, diff --git a/tests/ui/macros/trace_faulty_macros.rs b/tests/ui/macros/trace_faulty_macros.rs index b2fdd2e1965..00eb7593799 100644 --- a/tests/ui/macros/trace_faulty_macros.rs +++ b/tests/ui/macros/trace_faulty_macros.rs @@ -41,3 +41,14 @@ fn use_bang_macro_as_attr() {} #[derive(Debug)] //~ ERROR `derive` may only be applied to `struct`s fn use_derive_macro_as_attr() {} + +macro_rules! test { + (let $p:pat = $e:expr) => {test!(($p,$e))}; + // this should be expr + // vvv + (($p:pat, $e:pat)) => {let $p = $e;}; //~ ERROR expected expression, found pattern `1 + 1` +} + +fn foo() { + test!(let x = 1+1); +} diff --git a/tests/ui/macros/trace_faulty_macros.stderr b/tests/ui/macros/trace_faulty_macros.stderr index 21e47da0757..6a89d3bfd09 100644 --- a/tests/ui/macros/trace_faulty_macros.stderr +++ b/tests/ui/macros/trace_faulty_macros.stderr @@ -50,7 +50,7 @@ LL | my_recursive_macro!(); = note: expanding `my_recursive_macro! { }` = note: to `my_recursive_macro! () ;` -error: expected expression, found `A { a: a, b: 0, c: _, .. }` +error: expected expression, found pattern `A { a: a, b: 0, c: _, .. }` --> $DIR/trace_faulty_macros.rs:16:9 | LL | $a @@ -69,6 +69,28 @@ LL | #[derive(Debug)] LL | fn use_derive_macro_as_attr() {} | -------------------------------- not a `struct`, `enum` or `union` +error: expected expression, found pattern `1 + 1` + --> $DIR/trace_faulty_macros.rs:49:37 + | +LL | (let $p:pat = $e:expr) => {test!(($p,$e))}; + | ------- -- this is interpreted as expression, but it is expected to be pattern + | | + | this macro fragment matcher is expression +... +LL | (($p:pat, $e:pat)) => {let $p = $e;}; + | ------ ^^ expected expression + | | + | this macro fragment matcher is pattern +... +LL | test!(let x = 1+1); + | ------------------ + | | | + | | this is expected to be expression + | in this macro invocation + | + = note: when forwarding a matched fragment to another macro-by-example, matchers in the second macro will see an opaque AST of the fragment type, not the underlying tokens + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + note: trace_macro --> $DIR/trace_faulty_macros.rs:36:13 | @@ -80,6 +102,17 @@ LL | let a = pat_macro!(); = note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }` = note: to `A { a: a, b: 0, c: _, .. }` -error: aborting due to 4 previous errors +note: trace_macro + --> $DIR/trace_faulty_macros.rs:53:5 + | +LL | test!(let x = 1+1); + | ^^^^^^^^^^^^^^^^^^ + | + = note: expanding `test! { let x = 1 + 1 }` + = note: to `test! ((x, 1 + 1))` + = note: expanding `test! { (x, 1 + 1) }` + = note: to `let x = 1 + 1 ;` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0774`. diff --git a/tests/ui/match/issue-92100.stderr b/tests/ui/match/issue-92100.stderr index 0f694c587fc..d0e50f3ae16 100644 --- a/tests/ui/match/issue-92100.stderr +++ b/tests/ui/match/issue-92100.stderr @@ -3,6 +3,11 @@ error[E0425]: cannot find value `a` in this scope | LL | [a.., a] => {} | ^ not found in this scope + | +help: if you meant to collect the rest of the slice in `a`, use the at operator + | +LL | [a @ .., a] => {} + | + error: aborting due to previous error diff --git a/tests/ui/methods/method-ambiguity-no-rcvr.rs b/tests/ui/methods/method-ambiguity-no-rcvr.rs new file mode 100644 index 00000000000..8f36011d41f --- /dev/null +++ b/tests/ui/methods/method-ambiguity-no-rcvr.rs @@ -0,0 +1,14 @@ +struct Qux; + +trait Foo { + fn foo(); +} + +trait FooBar { + fn foo() {} +} + +fn main() { + Qux.foo(); + //~^ ERROR no method named `foo` found for struct `Qux` in the current scope +} diff --git a/tests/ui/methods/method-ambiguity-no-rcvr.stderr b/tests/ui/methods/method-ambiguity-no-rcvr.stderr new file mode 100644 index 00000000000..95c9d7ebac0 --- /dev/null +++ b/tests/ui/methods/method-ambiguity-no-rcvr.stderr @@ -0,0 +1,32 @@ +error[E0599]: no method named `foo` found for struct `Qux` in the current scope + --> $DIR/method-ambiguity-no-rcvr.rs:12:9 + | +LL | struct Qux; + | ---------- method `foo` not found for this struct +... +LL | Qux.foo(); + | ^^^ this is an associated function, not a method + | + = note: found the following associated functions; to be used as methods, functions must have a `self` parameter +note: candidate #1 is defined in the trait `Foo` + --> $DIR/method-ambiguity-no-rcvr.rs:4:5 + | +LL | fn foo(); + | ^^^^^^^^^ +note: candidate #2 is defined in the trait `FooBar` + --> $DIR/method-ambiguity-no-rcvr.rs:8:5 + | +LL | fn foo() {} + | ^^^^^^^^ +help: disambiguate the associated function for candidate #1 + | +LL | <Qux as Foo>::foo(Qux); + | ~~~~~~~~~~~~~~~~~~~~~~ +help: disambiguate the associated function for candidate #2 + | +LL | <Qux as FooBar>::foo(Qux); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/mir/build-async-error-body-correctly.rs b/tests/ui/mir/build-async-error-body-correctly.rs new file mode 100644 index 00000000000..1787f80c07e --- /dev/null +++ b/tests/ui/mir/build-async-error-body-correctly.rs @@ -0,0 +1,8 @@ +// edition: 2021 + +async fn asyncfn() { + let binding = match true {}; + //~^ ERROR non-exhaustive patterns: type `bool` is non-empty +} + +fn main() {} diff --git a/tests/ui/mir/build-async-error-body-correctly.stderr b/tests/ui/mir/build-async-error-body-correctly.stderr new file mode 100644 index 00000000000..3d18c249afe --- /dev/null +++ b/tests/ui/mir/build-async-error-body-correctly.stderr @@ -0,0 +1,17 @@ +error[E0004]: non-exhaustive patterns: type `bool` is non-empty + --> $DIR/build-async-error-body-correctly.rs:4:25 + | +LL | let binding = match true {}; + | ^^^^ + | + = note: the matched value is of type `bool` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ let binding = match true { +LL + _ => todo!(), +LL ~ }; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/mir/ssa_call_ret.rs b/tests/ui/mir/ssa_call_ret.rs new file mode 100644 index 00000000000..6132a6691de --- /dev/null +++ b/tests/ui/mir/ssa_call_ret.rs @@ -0,0 +1,30 @@ +// Regression test for issue #117331, where variable `a` was misidentified as +// being in SSA form (the definition occurs on the return edge only). +// +// edition:2021 +// compile-flags: --crate-type=lib +// build-pass +// needs-unwind +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn f() -> u32 { + mir!( + let a: u32; + { + Call(a = g(), bb1, UnwindCleanup(bb2)) + } + bb1 = { + RET = a; + Return() + } + bb2 (cleanup) = { + RET = a; + UnwindResume() + } + ) +} + +#[inline(never)] +pub fn g() -> u32 { 0 } diff --git a/tests/ui/mir/validate/noncleanup-cleanup.rs b/tests/ui/mir/validate/noncleanup-cleanup.rs new file mode 100644 index 00000000000..0a1c4528aa6 --- /dev/null +++ b/tests/ui/mir/validate/noncleanup-cleanup.rs @@ -0,0 +1,21 @@ +// Check that validation rejects cleanup edge to a non-cleanup block. +// +// failure-status: 101 +// dont-check-compiler-stderr +// error-pattern: cleanuppad mismatch +#![feature(custom_mir, core_intrinsics)] +extern crate core; +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "built")] +pub fn main() { + mir!( + { + Call(RET = main(), block, UnwindCleanup(block)) + } + block = { + Return() + } + ) + +} diff --git a/tests/ui/mir/validate/noncleanup-resume.rs b/tests/ui/mir/validate/noncleanup-resume.rs new file mode 100644 index 00000000000..e80d09bc90a --- /dev/null +++ b/tests/ui/mir/validate/noncleanup-resume.rs @@ -0,0 +1,17 @@ +// Check that validation rejects resume terminator in a non-cleanup block. +// +// failure-status: 101 +// dont-check-compiler-stderr +// error-pattern: resume on non-cleanup block +#![feature(custom_mir, core_intrinsics)] +extern crate core; +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "built")] +pub fn main() { + mir!( + { + UnwindResume() + } + ) +} diff --git a/tests/ui/mir/validate/noncleanup-terminate.rs b/tests/ui/mir/validate/noncleanup-terminate.rs new file mode 100644 index 00000000000..2a74668370d --- /dev/null +++ b/tests/ui/mir/validate/noncleanup-terminate.rs @@ -0,0 +1,17 @@ +// Check that validation rejects terminate terminator in a non-cleanup block. +// +// failure-status: 101 +// dont-check-compiler-stderr +// error-pattern: terminate on non-cleanup block +#![feature(custom_mir, core_intrinsics)] +extern crate core; +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "built")] +pub fn main() { + mir!( + { + UnwindTerminate(ReasonAbi) + } + ) +} diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index d63cec48917..142a52aef13 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ b/tests/ui/mismatched_types/cast-rfc0401.stderr @@ -18,7 +18,7 @@ error[E0609]: no field `f` on type `fn() {main}` --> $DIR/cast-rfc0401.rs:65:18 | LL | let _ = main.f as *const u32; - | ^ + | ^ unknown field error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:29:13 diff --git a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr index ba885d1b97e..320f529624f 100644 --- a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr +++ b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -1,17 +1,17 @@ -error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyBound(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a), T, ReEarlyBound(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a)])` captures lifetime that does not appear in bounds +error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyParam(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a), T, ReEarlyParam(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a)])` captures lifetime that does not appear in bounds --> $DIR/impl-trait-captures.rs:11:5 | LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { | -- ------------ opaque type defined here | | - | hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) T` captures the anonymous lifetime defined here + | hidden type `&ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) T` captures the anonymous lifetime defined here LL | x | ^ | -help: to declare that `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyBound(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a), T, ReEarlyBound(DefId(0:14 ~ impl_trait_captures[aeb9]::foo::{opaque#0}::'a), 2, 'a)])` captures `ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` lifetime bound +help: to declare that `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [ReEarlyParam(DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a), 0, 'a), T, ReEarlyParam(DefId(0:14 ~ impl_trait_captures[aeb9]::foo::{opaque#0}::'a), 2, 'a)])` captures `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))`, you can add an explicit `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` lifetime bound | -LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) { - | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) { + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to previous error diff --git a/tests/ui/offset-of/offset-of-self.stderr b/tests/ui/offset-of/offset-of-self.stderr index df555463f98..2dc17189a70 100644 --- a/tests/ui/offset-of/offset-of-self.stderr +++ b/tests/ui/offset-of/offset-of-self.stderr @@ -54,6 +54,8 @@ error[E0609]: no field `Self` on type `S` | LL | offset_of!(S, Self); | ^^^^ + | + = note: available fields are: `v`, `w` error[E0616]: field `v` of struct `T` is private --> $DIR/offset-of-self.rs:41:30 @@ -66,6 +68,8 @@ error[E0609]: no field `self` on type `S` | LL | offset_of!(S, self); | ^^^^ + | + = note: available fields are: `v`, `w` error[E0609]: no field `self` on type `u8` --> $DIR/offset-of-self.rs:56:21 diff --git a/tests/ui/parser/float-field-interpolated.rs b/tests/ui/parser/float-field-interpolated.rs index a3053203536..990f2926dc8 100644 --- a/tests/ui/parser/float-field-interpolated.rs +++ b/tests/ui/parser/float-field-interpolated.rs @@ -6,9 +6,9 @@ macro_rules! generate_field_accesses { s.$a; // OK { s.$b; } //~ ERROR unexpected token: `1.1` - //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1` + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found literal `1.1` { s.$c; } //~ ERROR unexpected token: `1.1` - //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1` + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found expression `1.1` }; } diff --git a/tests/ui/parser/float-field-interpolated.stderr b/tests/ui/parser/float-field-interpolated.stderr index 664adb35818..2a1a4926cb3 100644 --- a/tests/ui/parser/float-field-interpolated.stderr +++ b/tests/ui/parser/float-field-interpolated.stderr @@ -9,7 +9,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1); | = note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1` +error: expected one of `.`, `;`, `?`, `}`, or an operator, found literal `1.1` --> $DIR/float-field-interpolated.rs:8:13 | LL | { s.$b; } @@ -31,7 +31,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1); | = note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1` +error: expected one of `.`, `;`, `?`, `}`, or an operator, found expression `1.1` --> $DIR/float-field-interpolated.rs:10:13 | LL | { s.$c; } diff --git a/tests/ui/parser/float-field.stderr b/tests/ui/parser/float-field.stderr index 7090efc5014..d67d270ef7b 100644 --- a/tests/ui/parser/float-field.stderr +++ b/tests/ui/parser/float-field.stderr @@ -274,7 +274,7 @@ error[E0609]: no field `1e1` on type `(u8, u8)` --> $DIR/float-field.rs:9:9 | LL | s.1.1e1; - | ^^^ + | ^^^ unknown field error[E0609]: no field `0x1e1` on type `S` --> $DIR/float-field.rs:24:7 @@ -336,13 +336,13 @@ error[E0609]: no field `f32` on type `(u8, u8)` --> $DIR/float-field.rs:44:9 | LL | s.1.f32; - | ^^^ + | ^^^ unknown field error[E0609]: no field `1e1` on type `(u8, u8)` --> $DIR/float-field.rs:46:7 | LL | s.1.1e1f32; - | ^^^^^^^^ + | ^^^^^^^^ unknown field error: aborting due to 55 previous errors diff --git a/tests/ui/parser/issues/issue-32501.stderr b/tests/ui/parser/issues/issue-32501.stderr index d53302449a8..df12f7768d4 100644 --- a/tests/ui/parser/issues/issue-32501.stderr +++ b/tests/ui/parser/issues/issue-32501.stderr @@ -2,7 +2,7 @@ error: `mut` must be followed by a named binding --> $DIR/issue-32501.rs:7:9 | LL | let mut _ = 0; - | ^^^^^ help: remove the `mut` prefix: `_` + | ^^^^ help: remove the `mut` prefix | = note: `mut` may be followed by `variable` and `variable @ pattern` diff --git a/tests/ui/parser/issues/issue-48508.rs b/tests/ui/parser/issues/issue-48508.rs index 1e7db9df814..b66e09620f4 100644 --- a/tests/ui/parser/issues/issue-48508.rs +++ b/tests/ui/parser/issues/issue-48508.rs @@ -7,7 +7,6 @@ // issue-48508-aux.rs // compile-flags:-g -// ignore-asmjs wasm2js does not support source maps yet #![allow(uncommon_codepoints)] diff --git a/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr b/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr index 8c032e588e3..2bd87ee0c38 100644 --- a/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr +++ b/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr @@ -2,7 +2,7 @@ error: `mut` must be followed by a named binding --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:6:13 | LL | let mut $eval = (); - | ^^^^^^^^^ help: remove the `mut` prefix: `does_not_exist!()` + | ^^^^ help: remove the `mut` prefix ... LL | mac1! { does_not_exist!() } | --------------------------- in this macro invocation @@ -25,7 +25,7 @@ error: `mut` must be followed by a named binding --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:13:13 | LL | let mut $eval = (); - | ^^^ help: remove the `mut` prefix: `does_not_exist!()` + | ^^^ help: remove the `mut` prefix ... LL | mac2! { does_not_exist!() } | --------------------------- in this macro invocation diff --git a/tests/ui/parser/issues/recover-ge-as-fat-arrow.fixed b/tests/ui/parser/issues/recover-ge-as-fat-arrow.fixed new file mode 100644 index 00000000000..7b73dfb02df --- /dev/null +++ b/tests/ui/parser/issues/recover-ge-as-fat-arrow.fixed @@ -0,0 +1,7 @@ +// run-rustfix +fn main() { + match 1 { + 1 => {} //~ ERROR + _ => { let _: u16 = 2u16; } //~ ERROR + } +} diff --git a/tests/ui/parser/issues/recover-ge-as-fat-arrow.rs b/tests/ui/parser/issues/recover-ge-as-fat-arrow.rs new file mode 100644 index 00000000000..92143fcf3f7 --- /dev/null +++ b/tests/ui/parser/issues/recover-ge-as-fat-arrow.rs @@ -0,0 +1,7 @@ +// run-rustfix +fn main() { + match 1 { + 1 >= {} //~ ERROR + _ => { let _: u16 = 2u8; } //~ ERROR + } +} diff --git a/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr b/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr new file mode 100644 index 00000000000..2df5cca24f0 --- /dev/null +++ b/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr @@ -0,0 +1,25 @@ +error: expected one of `...`, `..=`, `..`, `=>`, `if`, or `|`, found `>=` + --> $DIR/recover-ge-as-fat-arrow.rs:4:11 + | +LL | 1 >= {} + | ^^ + | | + | expected one of `...`, `..=`, `..`, `=>`, `if`, or `|` + | help: use a fat arrow to start a match arm: `=>` + +error[E0308]: mismatched types + --> $DIR/recover-ge-as-fat-arrow.rs:5:29 + | +LL | _ => { let _: u16 = 2u8; } + | --- ^^^ expected `u16`, found `u8` + | | + | expected due to this + | +help: change the type of the numeric literal from `u8` to `u16` + | +LL | _ => { let _: u16 = 2u16; } + | ~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/mut-patterns.stderr b/tests/ui/parser/mut-patterns.stderr index f179d8c9e0a..66985c9f5e4 100644 --- a/tests/ui/parser/mut-patterns.stderr +++ b/tests/ui/parser/mut-patterns.stderr @@ -2,7 +2,7 @@ error: `mut` must be followed by a named binding --> $DIR/mut-patterns.rs:9:9 | LL | let mut _ = 0; - | ^^^^^ help: remove the `mut` prefix: `_` + | ^^^^ help: remove the `mut` prefix | = note: `mut` may be followed by `variable` and `variable @ pattern` @@ -10,7 +10,7 @@ error: `mut` must be followed by a named binding --> $DIR/mut-patterns.rs:10:9 | LL | let mut (_, _) = (0, 0); - | ^^^^^^^^^^ help: remove the `mut` prefix: `(_, _)` + | ^^^^ help: remove the `mut` prefix | = note: `mut` may be followed by `variable` and `variable @ pattern` diff --git a/tests/ui/parser/recover-assoc-const-constraint.rs b/tests/ui/parser/recover/recover-assoc-const-constraint.rs index 1453e6cb5cd..1453e6cb5cd 100644 --- a/tests/ui/parser/recover-assoc-const-constraint.rs +++ b/tests/ui/parser/recover/recover-assoc-const-constraint.rs diff --git a/tests/ui/parser/recover-assoc-const-constraint.stderr b/tests/ui/parser/recover/recover-assoc-const-constraint.stderr index 2d36ce4e986..2d36ce4e986 100644 --- a/tests/ui/parser/recover-assoc-const-constraint.stderr +++ b/tests/ui/parser/recover/recover-assoc-const-constraint.stderr diff --git a/tests/ui/parser/recover-assoc-eq-missing-term.rs b/tests/ui/parser/recover/recover-assoc-eq-missing-term.rs index 4b42c44dc64..4b42c44dc64 100644 --- a/tests/ui/parser/recover-assoc-eq-missing-term.rs +++ b/tests/ui/parser/recover/recover-assoc-eq-missing-term.rs diff --git a/tests/ui/parser/recover-assoc-eq-missing-term.stderr b/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr index 152f7f2fb06..152f7f2fb06 100644 --- a/tests/ui/parser/recover-assoc-eq-missing-term.stderr +++ b/tests/ui/parser/recover/recover-assoc-eq-missing-term.stderr diff --git a/tests/ui/parser/recover-assoc-lifetime-constraint.rs b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.rs index 558fcdfe177..558fcdfe177 100644 --- a/tests/ui/parser/recover-assoc-lifetime-constraint.rs +++ b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.rs diff --git a/tests/ui/parser/recover-assoc-lifetime-constraint.stderr b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.stderr index 79437533d7c..79437533d7c 100644 --- a/tests/ui/parser/recover-assoc-lifetime-constraint.stderr +++ b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.stderr diff --git a/tests/ui/parser/recover-const-async-fn-ptr.rs b/tests/ui/parser/recover/recover-const-async-fn-ptr.rs index 25af8772ced..25af8772ced 100644 --- a/tests/ui/parser/recover-const-async-fn-ptr.rs +++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.rs diff --git a/tests/ui/parser/recover-const-async-fn-ptr.stderr b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr index 7012096b644..7012096b644 100644 --- a/tests/ui/parser/recover-const-async-fn-ptr.stderr +++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr diff --git a/tests/ui/parser/recover-enum.rs b/tests/ui/parser/recover/recover-enum.rs index 08dd939e2c0..08dd939e2c0 100644 --- a/tests/ui/parser/recover-enum.rs +++ b/tests/ui/parser/recover/recover-enum.rs diff --git a/tests/ui/parser/recover-enum.stderr b/tests/ui/parser/recover/recover-enum.stderr index a2b650e4f4e..a2b650e4f4e 100644 --- a/tests/ui/parser/recover-enum.stderr +++ b/tests/ui/parser/recover/recover-enum.stderr diff --git a/tests/ui/parser/recover-enum2.rs b/tests/ui/parser/recover/recover-enum2.rs index 0c942088955..0c942088955 100644 --- a/tests/ui/parser/recover-enum2.rs +++ b/tests/ui/parser/recover/recover-enum2.rs diff --git a/tests/ui/parser/recover-enum2.stderr b/tests/ui/parser/recover/recover-enum2.stderr index 7634bca921c..7634bca921c 100644 --- a/tests/ui/parser/recover-enum2.stderr +++ b/tests/ui/parser/recover/recover-enum2.stderr diff --git a/tests/ui/parser/recover-field-extra-angle-brackets-in-struct-with-a-field.rs b/tests/ui/parser/recover/recover-field-extra-angle-brackets-in-struct-with-a-field.rs index e815c7611c0..e815c7611c0 100644 --- a/tests/ui/parser/recover-field-extra-angle-brackets-in-struct-with-a-field.rs +++ b/tests/ui/parser/recover/recover-field-extra-angle-brackets-in-struct-with-a-field.rs diff --git a/tests/ui/parser/recover-field-extra-angle-brackets-in-struct-with-a-field.stderr b/tests/ui/parser/recover/recover-field-extra-angle-brackets-in-struct-with-a-field.stderr index 17237c93097..17237c93097 100644 --- a/tests/ui/parser/recover-field-extra-angle-brackets-in-struct-with-a-field.stderr +++ b/tests/ui/parser/recover/recover-field-extra-angle-brackets-in-struct-with-a-field.stderr diff --git a/tests/ui/parser/recover-field-extra-angle-brackets.rs b/tests/ui/parser/recover/recover-field-extra-angle-brackets.rs index 5e0e00bcb5e..5e0e00bcb5e 100644 --- a/tests/ui/parser/recover-field-extra-angle-brackets.rs +++ b/tests/ui/parser/recover/recover-field-extra-angle-brackets.rs diff --git a/tests/ui/parser/recover-field-extra-angle-brackets.stderr b/tests/ui/parser/recover/recover-field-extra-angle-brackets.stderr index 318e55f6e99..318e55f6e99 100644 --- a/tests/ui/parser/recover-field-extra-angle-brackets.stderr +++ b/tests/ui/parser/recover/recover-field-extra-angle-brackets.stderr diff --git a/tests/ui/parser/recover-field-semi.rs b/tests/ui/parser/recover/recover-field-semi.rs index b703578860e..b703578860e 100644 --- a/tests/ui/parser/recover-field-semi.rs +++ b/tests/ui/parser/recover/recover-field-semi.rs diff --git a/tests/ui/parser/recover-field-semi.stderr b/tests/ui/parser/recover/recover-field-semi.stderr index 3cf4847488c..3cf4847488c 100644 --- a/tests/ui/parser/recover-field-semi.stderr +++ b/tests/ui/parser/recover/recover-field-semi.stderr diff --git a/tests/ui/parser/recover-fn-ptr-with-generics.rs b/tests/ui/parser/recover/recover-fn-ptr-with-generics.rs index 31de418be5f..31de418be5f 100644 --- a/tests/ui/parser/recover-fn-ptr-with-generics.rs +++ b/tests/ui/parser/recover/recover-fn-ptr-with-generics.rs diff --git a/tests/ui/parser/recover-fn-ptr-with-generics.stderr b/tests/ui/parser/recover/recover-fn-ptr-with-generics.stderr index 069fcffe9a0..069fcffe9a0 100644 --- a/tests/ui/parser/recover-fn-ptr-with-generics.stderr +++ b/tests/ui/parser/recover/recover-fn-ptr-with-generics.stderr diff --git a/tests/ui/parser/recover-fn-trait-from-fn-kw.rs b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.rs index b6611e6273d..b6611e6273d 100644 --- a/tests/ui/parser/recover-fn-trait-from-fn-kw.rs +++ b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.rs diff --git a/tests/ui/parser/recover-fn-trait-from-fn-kw.stderr b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.stderr index 3681a796c53..3681a796c53 100644 --- a/tests/ui/parser/recover-fn-trait-from-fn-kw.stderr +++ b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.stderr diff --git a/tests/ui/parser/recover-for-loop-parens-around-head.rs b/tests/ui/parser/recover/recover-for-loop-parens-around-head.rs index 053b428bd12..053b428bd12 100644 --- a/tests/ui/parser/recover-for-loop-parens-around-head.rs +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.rs diff --git a/tests/ui/parser/recover-for-loop-parens-around-head.stderr b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr index 3bad29f20af..3bad29f20af 100644 --- a/tests/ui/parser/recover-for-loop-parens-around-head.stderr +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr diff --git a/tests/ui/parser/recover-from-bad-variant.rs b/tests/ui/parser/recover/recover-from-bad-variant.rs index e8887147cbc..e8887147cbc 100644 --- a/tests/ui/parser/recover-from-bad-variant.rs +++ b/tests/ui/parser/recover/recover-from-bad-variant.rs diff --git a/tests/ui/parser/recover-from-bad-variant.stderr b/tests/ui/parser/recover/recover-from-bad-variant.stderr index 04968bbdf99..04968bbdf99 100644 --- a/tests/ui/parser/recover-from-bad-variant.stderr +++ b/tests/ui/parser/recover/recover-from-bad-variant.stderr diff --git a/tests/ui/parser/recover-from-homoglyph.rs b/tests/ui/parser/recover/recover-from-homoglyph.rs index 99ce0d1a630..99ce0d1a630 100644 --- a/tests/ui/parser/recover-from-homoglyph.rs +++ b/tests/ui/parser/recover/recover-from-homoglyph.rs diff --git a/tests/ui/parser/recover-from-homoglyph.stderr b/tests/ui/parser/recover/recover-from-homoglyph.stderr index f11ca9fd584..f11ca9fd584 100644 --- a/tests/ui/parser/recover-from-homoglyph.stderr +++ b/tests/ui/parser/recover/recover-from-homoglyph.stderr diff --git a/tests/ui/parser/recover-labeled-non-block-expr.fixed b/tests/ui/parser/recover/recover-labeled-non-block-expr.fixed index c2e76444d11..c2e76444d11 100644 --- a/tests/ui/parser/recover-labeled-non-block-expr.fixed +++ b/tests/ui/parser/recover/recover-labeled-non-block-expr.fixed diff --git a/tests/ui/parser/recover-labeled-non-block-expr.rs b/tests/ui/parser/recover/recover-labeled-non-block-expr.rs index fc11c646a8c..fc11c646a8c 100644 --- a/tests/ui/parser/recover-labeled-non-block-expr.rs +++ b/tests/ui/parser/recover/recover-labeled-non-block-expr.rs diff --git a/tests/ui/parser/recover-labeled-non-block-expr.stderr b/tests/ui/parser/recover/recover-labeled-non-block-expr.stderr index d66ce695090..d66ce695090 100644 --- a/tests/ui/parser/recover-labeled-non-block-expr.stderr +++ b/tests/ui/parser/recover/recover-labeled-non-block-expr.stderr diff --git a/tests/ui/parser/recover-missing-semi-before-item.fixed b/tests/ui/parser/recover/recover-missing-semi-before-item.fixed index acb846373cb..acb846373cb 100644 --- a/tests/ui/parser/recover-missing-semi-before-item.fixed +++ b/tests/ui/parser/recover/recover-missing-semi-before-item.fixed diff --git a/tests/ui/parser/recover-missing-semi-before-item.rs b/tests/ui/parser/recover/recover-missing-semi-before-item.rs index ef6cfe3c4ed..ef6cfe3c4ed 100644 --- a/tests/ui/parser/recover-missing-semi-before-item.rs +++ b/tests/ui/parser/recover/recover-missing-semi-before-item.rs diff --git a/tests/ui/parser/recover-missing-semi-before-item.stderr b/tests/ui/parser/recover/recover-missing-semi-before-item.stderr index 61c43f2f189..61c43f2f189 100644 --- a/tests/ui/parser/recover-missing-semi-before-item.stderr +++ b/tests/ui/parser/recover/recover-missing-semi-before-item.stderr diff --git a/tests/ui/parser/recover-missing-semi.rs b/tests/ui/parser/recover/recover-missing-semi.rs index f47d5e6805f..f47d5e6805f 100644 --- a/tests/ui/parser/recover-missing-semi.rs +++ b/tests/ui/parser/recover/recover-missing-semi.rs diff --git a/tests/ui/parser/recover-missing-semi.stderr b/tests/ui/parser/recover/recover-missing-semi.stderr index ba479828538..ba479828538 100644 --- a/tests/ui/parser/recover-missing-semi.stderr +++ b/tests/ui/parser/recover/recover-missing-semi.stderr diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs new file mode 100644 index 00000000000..9ed733bf079 --- /dev/null +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.rs @@ -0,0 +1,14 @@ +fn main() { + let val = 42; + let x = match val { + (0 if true) => { + //~^ ERROR expected identifier, found keyword `if` + //~| ERROR expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` + //~| ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `true` + //~| ERROR mismatched types + 42u8 + } + _ => 0u8, + }; + let _y: u32 = x; //~ ERROR mismatched types +} diff --git a/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr new file mode 100644 index 00000000000..6542f440e4b --- /dev/null +++ b/tests/ui/parser/recover/recover-parens-around-match-arm-head.stderr @@ -0,0 +1,49 @@ +error: expected identifier, found keyword `if` + --> $DIR/recover-parens-around-match-arm-head.rs:4:12 + | +LL | (0 if true) => { + | ^^ expected identifier, found keyword + +error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found keyword `if` + --> $DIR/recover-parens-around-match-arm-head.rs:4:12 + | +LL | (0 if true) => { + | -^^ expected one of `)`, `,`, `...`, `..=`, `..`, or `|` + | | + | help: missing `,` + +error: expected one of `)`, `,`, `@`, or `|`, found keyword `true` + --> $DIR/recover-parens-around-match-arm-head.rs:4:15 + | +LL | (0 if true) => { + | -^^^^ expected one of `)`, `,`, `@`, or `|` + | | + | help: missing `,` + +error[E0308]: mismatched types + --> $DIR/recover-parens-around-match-arm-head.rs:4:9 + | +LL | let x = match val { + | --- this expression has type `{integer}` +LL | (0 if true) => { + | ^^^^^^^^^^^ expected integer, found `(_, _, _)` + | + = note: expected type `{integer}` + found tuple `(_, _, _)` + +error[E0308]: mismatched types + --> $DIR/recover-parens-around-match-arm-head.rs:13:19 + | +LL | let _y: u32 = x; + | --- ^ expected `u32`, found `u8` + | | + | expected due to this + | +help: you can convert a `u8` to a `u32` + | +LL | let _y: u32 = x.into(); + | +++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/recover-quantified-closure.rs b/tests/ui/parser/recover/recover-quantified-closure.rs index 10af39b7007..10af39b7007 100644 --- a/tests/ui/parser/recover-quantified-closure.rs +++ b/tests/ui/parser/recover/recover-quantified-closure.rs diff --git a/tests/ui/parser/recover-quantified-closure.stderr b/tests/ui/parser/recover/recover-quantified-closure.stderr index 37e93cbee7b..37e93cbee7b 100644 --- a/tests/ui/parser/recover-quantified-closure.stderr +++ b/tests/ui/parser/recover/recover-quantified-closure.stderr diff --git a/tests/ui/parser/recover-range-pats.rs b/tests/ui/parser/recover/recover-range-pats.rs index 156c7ad94d3..156c7ad94d3 100644 --- a/tests/ui/parser/recover-range-pats.rs +++ b/tests/ui/parser/recover/recover-range-pats.rs diff --git a/tests/ui/parser/recover-range-pats.stderr b/tests/ui/parser/recover/recover-range-pats.stderr index 5b69ca5cd6d..5b69ca5cd6d 100644 --- a/tests/ui/parser/recover-range-pats.stderr +++ b/tests/ui/parser/recover/recover-range-pats.stderr diff --git a/tests/ui/parser/recover-ref-dyn-mut.rs b/tests/ui/parser/recover/recover-ref-dyn-mut.rs index 3016275cc0f..3016275cc0f 100644 --- a/tests/ui/parser/recover-ref-dyn-mut.rs +++ b/tests/ui/parser/recover/recover-ref-dyn-mut.rs diff --git a/tests/ui/parser/recover-ref-dyn-mut.stderr b/tests/ui/parser/recover/recover-ref-dyn-mut.stderr index c048c8ea1b0..c048c8ea1b0 100644 --- a/tests/ui/parser/recover-ref-dyn-mut.stderr +++ b/tests/ui/parser/recover/recover-ref-dyn-mut.stderr diff --git a/tests/ui/parser/recover-struct.rs b/tests/ui/parser/recover/recover-struct.rs index bfa5b454c0a..bfa5b454c0a 100644 --- a/tests/ui/parser/recover-struct.rs +++ b/tests/ui/parser/recover/recover-struct.rs diff --git a/tests/ui/parser/recover-struct.stderr b/tests/ui/parser/recover/recover-struct.stderr index 9f6fb06caa3..9f6fb06caa3 100644 --- a/tests/ui/parser/recover-struct.stderr +++ b/tests/ui/parser/recover/recover-struct.stderr diff --git a/tests/ui/parser/recover-tuple-pat.rs b/tests/ui/parser/recover/recover-tuple-pat.rs index 7fded752d67..7fded752d67 100644 --- a/tests/ui/parser/recover-tuple-pat.rs +++ b/tests/ui/parser/recover/recover-tuple-pat.rs diff --git a/tests/ui/parser/recover-tuple-pat.stderr b/tests/ui/parser/recover/recover-tuple-pat.stderr index 93a6a66a630..93a6a66a630 100644 --- a/tests/ui/parser/recover-tuple-pat.stderr +++ b/tests/ui/parser/recover/recover-tuple-pat.stderr diff --git a/tests/ui/parser/recover-tuple.rs b/tests/ui/parser/recover/recover-tuple.rs index 59e2695dec6..59e2695dec6 100644 --- a/tests/ui/parser/recover-tuple.rs +++ b/tests/ui/parser/recover/recover-tuple.rs diff --git a/tests/ui/parser/recover-tuple.stderr b/tests/ui/parser/recover/recover-tuple.stderr index 88891b54bb2..88891b54bb2 100644 --- a/tests/ui/parser/recover-tuple.stderr +++ b/tests/ui/parser/recover/recover-tuple.stderr diff --git a/tests/ui/parser/recover-unticked-labels.fixed b/tests/ui/parser/recover/recover-unticked-labels.fixed index 159d995b8da..159d995b8da 100644 --- a/tests/ui/parser/recover-unticked-labels.fixed +++ b/tests/ui/parser/recover/recover-unticked-labels.fixed diff --git a/tests/ui/parser/recover-unticked-labels.rs b/tests/ui/parser/recover/recover-unticked-labels.rs index 56034de6844..56034de6844 100644 --- a/tests/ui/parser/recover-unticked-labels.rs +++ b/tests/ui/parser/recover/recover-unticked-labels.rs diff --git a/tests/ui/parser/recover-unticked-labels.stderr b/tests/ui/parser/recover/recover-unticked-labels.stderr index fbd108ca613..fbd108ca613 100644 --- a/tests/ui/parser/recover-unticked-labels.stderr +++ b/tests/ui/parser/recover/recover-unticked-labels.stderr diff --git a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed index 227c40e97c0..227c40e97c0 100644 --- a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.fixed +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed diff --git a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs index 3699e6fe572..3699e6fe572 100644 --- a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.rs +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs diff --git a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr index 18aa5fadb6b..18aa5fadb6b 100644 --- a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.stderr +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr diff --git a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-1.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-1.rs index f515ae81e51..f515ae81e51 100644 --- a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-1.rs +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-1.rs diff --git a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-1.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-1.stderr index 2219c2a7316..2219c2a7316 100644 --- a/tests/ui/parser/recover-where-clause-before-tuple-struct-body-1.stderr +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-1.stderr diff --git a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs new file mode 100644 index 00000000000..a619fcafc86 --- /dev/null +++ b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.rs @@ -0,0 +1,9 @@ +fn main() { + match &[1, 2, 3][..] { + [1, rest..] => println!("{rest:?}"), + //~^ ERROR cannot find value `rest` in this scope + //~| ERROR cannot find value `rest` in this scope + //~| ERROR `X..` patterns in slices are experimental + _ => {} + } +} diff --git a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr new file mode 100644 index 00000000000..cddd0121279 --- /dev/null +++ b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr @@ -0,0 +1,30 @@ +error[E0425]: cannot find value `rest` in this scope + --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 + | +LL | [1, rest..] => println!("{rest:?}"), + | ^^^^ not found in this scope + | +help: if you meant to collect the rest of the slice in `rest`, use the at operator + | +LL | [1, rest @ ..] => println!("{rest:?}"), + | + + +error[E0425]: cannot find value `rest` in this scope + --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35 + | +LL | [1, rest..] => println!("{rest:?}"), + | ^^^^ not found in this scope + +error[E0658]: `X..` patterns in slices are experimental + --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 + | +LL | [1, rest..] => println!("{rest:?}"), + | ^^^^^^ + | + = note: see issue #67264 <https://github.com/rust-lang/rust/issues/67264> for more information + = help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/rmeta/rmeta_meta_main.stderr b/tests/ui/rmeta/rmeta_meta_main.stderr index 0c6ed9afd35..a4af319e339 100644 --- a/tests/ui/rmeta/rmeta_meta_main.stderr +++ b/tests/ui/rmeta/rmeta_meta_main.stderr @@ -2,7 +2,12 @@ error[E0560]: struct `Foo` has no field named `field2` --> $DIR/rmeta_meta_main.rs:13:19 | LL | let _ = Foo { field2: 42 }; - | ^^^^^^ help: a field with a similar name exists: `field` + | ^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let _ = Foo { field: 42 }; + | ~~~~~ error: aborting due to previous error diff --git a/tests/ui/self/self_type_keyword.stderr b/tests/ui/self/self_type_keyword.stderr index 6e65fae808d..fed853a7e1f 100644 --- a/tests/ui/self/self_type_keyword.stderr +++ b/tests/ui/self/self_type_keyword.stderr @@ -14,7 +14,7 @@ error: `mut` must be followed by a named binding --> $DIR/self_type_keyword.rs:16:9 | LL | mut Self => (), - | ^^^^^^^^ help: remove the `mut` prefix: `Self` + | ^^^^ help: remove the `mut` prefix | = note: `mut` may be followed by `variable` and `variable @ pattern` diff --git a/tests/ui/sepcomp/sepcomp-lib-lto.rs b/tests/ui/sepcomp/sepcomp-lib-lto.rs index 51a572899f8..164ae79c254 100644 --- a/tests/ui/sepcomp/sepcomp-lib-lto.rs +++ b/tests/ui/sepcomp/sepcomp-lib-lto.rs @@ -4,7 +4,6 @@ // aux-build:sepcomp_lib.rs // compile-flags: -C lto -g -// ignore-asmjs wasm2js does not support source maps yet // no-prefer-dynamic extern crate sepcomp_lib; diff --git a/tests/ui/structs/struct-fields-hints-no-dupe.stderr b/tests/ui/structs/struct-fields-hints-no-dupe.stderr index 1a88f269347..e109980e0bb 100644 --- a/tests/ui/structs/struct-fields-hints-no-dupe.stderr +++ b/tests/ui/structs/struct-fields-hints-no-dupe.stderr @@ -2,7 +2,12 @@ error[E0560]: struct `A` has no field named `bar` --> $DIR/struct-fields-hints-no-dupe.rs:10:9 | LL | bar : 42, - | ^^^ help: a field with a similar name exists: `barr` + | ^^^ unknown field + | +help: a field with a similar name exists + | +LL | barr : 42, + | ~~~~ error: aborting due to previous error diff --git a/tests/ui/structs/struct-fields-hints.stderr b/tests/ui/structs/struct-fields-hints.stderr index 3b8a2b5c7ba..ed3650e5298 100644 --- a/tests/ui/structs/struct-fields-hints.stderr +++ b/tests/ui/structs/struct-fields-hints.stderr @@ -2,7 +2,12 @@ error[E0560]: struct `A` has no field named `bar` --> $DIR/struct-fields-hints.rs:10:9 | LL | bar : 42, - | ^^^ help: a field with a similar name exists: `car` + | ^^^ unknown field + | +help: a field with a similar name exists + | +LL | car : 42, + | ~~~ error: aborting due to previous error diff --git a/tests/ui/structs/struct-fields-typo.stderr b/tests/ui/structs/struct-fields-typo.stderr index 6949a0a4a68..aef0e0e8e5c 100644 --- a/tests/ui/structs/struct-fields-typo.stderr +++ b/tests/ui/structs/struct-fields-typo.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `baa` on type `BuildData` --> $DIR/struct-fields-typo.rs:11:17 | LL | let x = foo.baa; - | ^^^ help: a field with a similar name exists: `bar` + | ^^^ unknown field + | +help: a field with a similar name exists + | +LL | let x = foo.bar; + | ~~~ error: aborting due to previous error diff --git a/tests/ui/structs/struct-pat-derived-error.stderr b/tests/ui/structs/struct-pat-derived-error.stderr index a91e47657ab..78bb018cb4b 100644 --- a/tests/ui/structs/struct-pat-derived-error.stderr +++ b/tests/ui/structs/struct-pat-derived-error.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `d` on type `&A` --> $DIR/struct-pat-derived-error.rs:8:31 | LL | let A { x, y } = self.d; - | ^ help: a field with a similar name exists: `b` + | ^ unknown field + | +help: a field with a similar name exists + | +LL | let A { x, y } = self.b; + | ~ error[E0026]: struct `A` does not have fields named `x`, `y` --> $DIR/struct-pat-derived-error.rs:8:17 diff --git a/tests/ui/structs/struct-path-self-type-mismatch.stderr b/tests/ui/structs/struct-path-self-type-mismatch.stderr index cddc1356194..bbe5bae29bb 100644 --- a/tests/ui/structs/struct-path-self-type-mismatch.stderr +++ b/tests/ui/structs/struct-path-self-type-mismatch.stderr @@ -24,7 +24,9 @@ error[E0308]: mismatched types --> $DIR/struct-path-self-type-mismatch.rs:13:9 | LL | impl<T> Foo<T> { - | - found type parameter + | - ------ this is the type of the `Self` literal + | | + | found type parameter LL | fn new<U>(u: U) -> Foo<U> { | - ------ expected `Foo<U>` because of return type | | @@ -40,6 +42,10 @@ LL | | } found struct `Foo<T>` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +help: use the type name directly + | +LL | Foo::<U> { + | ~~~~~~~~ error: aborting due to 3 previous errors diff --git a/tests/ui/structs/suggest-private-fields.stderr b/tests/ui/structs/suggest-private-fields.stderr index d628bd16208..f67a4ed78e2 100644 --- a/tests/ui/structs/suggest-private-fields.stderr +++ b/tests/ui/structs/suggest-private-fields.stderr @@ -2,7 +2,12 @@ error[E0560]: struct `B` has no field named `aa` --> $DIR/suggest-private-fields.rs:15:9 | LL | aa: 20, - | ^^ help: a field with a similar name exists: `a` + | ^^ unknown field + | +help: a field with a similar name exists + | +LL | a: 20, + | ~ error[E0560]: struct `B` has no field named `bb` --> $DIR/suggest-private-fields.rs:17:9 @@ -16,13 +21,23 @@ error[E0560]: struct `A` has no field named `aa` --> $DIR/suggest-private-fields.rs:22:9 | LL | aa: 20, - | ^^ help: a field with a similar name exists: `a` + | ^^ unknown field + | +help: a field with a similar name exists + | +LL | a: 20, + | ~ error[E0560]: struct `A` has no field named `bb` --> $DIR/suggest-private-fields.rs:24:9 | LL | bb: 20, - | ^^ help: a field with a similar name exists: `b` + | ^^ unknown field + | +help: a field with a similar name exists + | +LL | b: 20, + | ~ error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/call-on-missing.stderr b/tests/ui/suggestions/call-on-missing.stderr index ca9abc7e906..1bab075dc9c 100644 --- a/tests/ui/suggestions/call-on-missing.stderr +++ b/tests/ui/suggestions/call-on-missing.stderr @@ -13,7 +13,7 @@ error[E0609]: no field `i` on type `fn() -> Foo {foo}` --> $DIR/call-on-missing.rs:16:9 | LL | foo.i; - | ^ + | ^ unknown field | help: use parentheses to call this function | @@ -62,7 +62,7 @@ LL | fn type_param<T: Fn() -> Foo>(t: T) { | - type parameter 'T' declared here ... LL | t.i; - | ^ + | ^ unknown field | help: use parentheses to call this type parameter | diff --git a/tests/ui/suggestions/crate-or-module-typo.rs b/tests/ui/suggestions/crate-or-module-typo.rs index 2471b11c61e..b12ad495e9f 100644 --- a/tests/ui/suggestions/crate-or-module-typo.rs +++ b/tests/ui/suggestions/crate-or-module-typo.rs @@ -3,7 +3,7 @@ use st::cell::Cell; //~ ERROR failed to resolve: use of undeclared crate or module `st` mod bar { - pub fn bar() { bar::baz(); } //~ ERROR failed to resolve: use of undeclared crate or module `bar` + pub fn bar() { bar::baz(); } //~ ERROR failed to resolve: function `bar` is not a crate or module fn baz() {} } diff --git a/tests/ui/suggestions/crate-or-module-typo.stderr b/tests/ui/suggestions/crate-or-module-typo.stderr index 9ece31e76f0..457d7790646 100644 --- a/tests/ui/suggestions/crate-or-module-typo.stderr +++ b/tests/ui/suggestions/crate-or-module-typo.stderr @@ -42,11 +42,11 @@ LL - bar: st::cell::Cell<bool> LL + bar: cell::Cell<bool> | -error[E0433]: failed to resolve: use of undeclared crate or module `bar` +error[E0433]: failed to resolve: function `bar` is not a crate or module --> $DIR/crate-or-module-typo.rs:6:20 | LL | pub fn bar() { bar::baz(); } - | ^^^ use of undeclared crate or module `bar` + | ^^^ function `bar` is not a crate or module error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/issue-117669.rs b/tests/ui/suggestions/issue-117669.rs new file mode 100644 index 00000000000..09b4f2bd47c --- /dev/null +++ b/tests/ui/suggestions/issue-117669.rs @@ -0,0 +1,4 @@ +fn main() { + let abs: i32 = 3i32.checked_abs(); + //~^ ERROR mismatched types +} diff --git a/tests/ui/suggestions/issue-117669.stderr b/tests/ui/suggestions/issue-117669.stderr new file mode 100644 index 00000000000..f09675fcc5c --- /dev/null +++ b/tests/ui/suggestions/issue-117669.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/issue-117669.rs:2:20 + | +LL | let abs: i32 = 3i32.checked_abs(); + | --- ^^^^^^^^^^^^^^^^^^ expected `i32`, found `Option<i32>` + | | + | expected due to this + | + = note: expected type `i32` + found enum `Option<i32>` +help: consider using `Option::expect` to unwrap the `Option<i32>` value, panicking if the value is an `Option::None` + | +LL | let abs: i32 = 3i32.checked_abs().expect("REASON"); + | +++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.fixed b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.fixed new file mode 100644 index 00000000000..84315ad9173 --- /dev/null +++ b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.fixed @@ -0,0 +1,11 @@ +// run-rustfix +use std::any::Any; + +fn foo<T: Any>(value: &T) -> Box<dyn Any + '_> { + Box::new(value) as Box<dyn Any> + //~^ ERROR lifetime may not live long enough +} + +fn main() { + let _ = foo(&5); +} diff --git a/tests/ui/issues/issue-16922.rs b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.rs index bbbbf72dbd5..fa7e72ff2a7 100644 --- a/tests/ui/issues/issue-16922.rs +++ b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.rs @@ -1,3 +1,4 @@ +// run-rustfix use std::any::Any; fn foo<T: Any>(value: &T) -> Box<dyn Any> { diff --git a/tests/ui/issues/issue-16922.stderr b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.stderr index 9d9f32a97c0..dc5817bfe0f 100644 --- a/tests/ui/issues/issue-16922.stderr +++ b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/issue-16922.rs:4:5 + --> $DIR/suggest-using-tick-underscore-lifetime-in-return-trait-object.rs:5:5 | LL | fn foo<T: Any>(value: &T) -> Box<dyn Any> { | - let's call the lifetime of this reference `'1` diff --git a/tests/ui/suggestions/non-existent-field-present-in-subfield.stderr b/tests/ui/suggestions/non-existent-field-present-in-subfield.stderr index cc991b915d3..65353ed923e 100644 --- a/tests/ui/suggestions/non-existent-field-present-in-subfield.stderr +++ b/tests/ui/suggestions/non-existent-field-present-in-subfield.stderr @@ -4,7 +4,6 @@ error[E0609]: no field `c` on type `Foo` LL | let _test = &fooer.c; | ^ unknown field | - = note: available fields are: `first`, `_second`, `_third` help: one of the expressions' fields has a field of the same name | LL | let _test = &fooer.first.bar.c; @@ -16,7 +15,6 @@ error[E0609]: no field `test` on type `Foo` LL | let _test2 = fooer.test; | ^^^^ unknown field | - = note: available fields are: `first`, `_second`, `_third` help: one of the expressions' fields has a field of the same name | LL | let _test2 = fooer.first.bar.c.test; diff --git a/tests/ui/suggestions/parenthesized-deref-suggestion.stderr b/tests/ui/suggestions/parenthesized-deref-suggestion.stderr index cafddbe2624..9f185f5dd52 100644 --- a/tests/ui/suggestions/parenthesized-deref-suggestion.stderr +++ b/tests/ui/suggestions/parenthesized-deref-suggestion.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `opts` on type `*const Session` --> $DIR/parenthesized-deref-suggestion.rs:7:30 | LL | (sess as *const Session).opts; - | ^^^^ + | ^^^^ unknown field | help: `(sess as *const Session)` is a raw pointer; try dereferencing it | @@ -14,7 +14,8 @@ error[E0609]: no field `0` on type `[u32; 1]` | LL | (x as [u32; 1]).0; | ----------------^ - | | + | | | + | | unknown field | help: instead of using tuple indexing, use array indexing: `(x as [u32; 1])[0]` error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/private-field.stderr b/tests/ui/suggestions/private-field.stderr index c38c795e07a..0db426588bd 100644 --- a/tests/ui/suggestions/private-field.stderr +++ b/tests/ui/suggestions/private-field.stderr @@ -4,7 +4,7 @@ error[E0609]: no field `cap` on type `S` LL | dbg!(s.cap) | ^^^ unknown field | - = note: available fields are: `val` + = note: available field is: `val` error: aborting due to previous error diff --git a/tests/ui/suggestions/suggest-dereferencing-index.stderr b/tests/ui/suggestions/suggest-dereferencing-index.stderr index adf01339972..23f6657f092 100644 --- a/tests/ui/suggestions/suggest-dereferencing-index.stderr +++ b/tests/ui/suggestions/suggest-dereferencing-index.stderr @@ -8,6 +8,8 @@ LL | let one_item_please: i32 = [1, 2, 3][i]; = help: the trait `SliceIndex<[{integer}]>` is implemented for `usize` = help: for that trait implementation, expected `usize`, found `&usize` = note: required for `[{integer}]` to implement `Index<&usize>` + = note: 1 redundant requirement hidden + = note: required for `[{integer}; 3]` to implement `Index<&usize>` help: dereference this index | LL | let one_item_please: i32 = [1, 2, 3][*i]; diff --git a/tests/ui/suggestions/suggest-field-through-deref.fixed b/tests/ui/suggestions/suggest-field-through-deref.fixed new file mode 100644 index 00000000000..07ba3aa911f --- /dev/null +++ b/tests/ui/suggestions/suggest-field-through-deref.fixed @@ -0,0 +1,21 @@ +// run-rustfix +#![allow(dead_code)] +use std::sync::Arc; +struct S { + long_name: (), + foo: (), +} +fn main() { + let x = Arc::new(S { long_name: (), foo: () }); + let _ = x.long_name; //~ ERROR no field `longname` + let y = S { long_name: (), foo: () }; + let _ = y.long_name; //~ ERROR no field `longname` + let a = Some(Arc::new(S { long_name: (), foo: () })); + let _ = a.unwrap().long_name; //~ ERROR no field `longname` + let b = Some(S { long_name: (), foo: () }); + let _ = b.unwrap().long_name; //~ ERROR no field `long_name` + let c = Ok::<_, ()>(Arc::new(S { long_name: (), foo: () })); + let _ = c.unwrap().long_name; //~ ERROR no field `longname` + let d = Ok::<_, ()>(S { long_name: (), foo: () }); + let _ = d.unwrap().long_name; //~ ERROR no field `long_name` +} diff --git a/tests/ui/suggestions/suggest-field-through-deref.rs b/tests/ui/suggestions/suggest-field-through-deref.rs new file mode 100644 index 00000000000..6e24b425e95 --- /dev/null +++ b/tests/ui/suggestions/suggest-field-through-deref.rs @@ -0,0 +1,21 @@ +// run-rustfix +#![allow(dead_code)] +use std::sync::Arc; +struct S { + long_name: (), + foo: (), +} +fn main() { + let x = Arc::new(S { long_name: (), foo: () }); + let _ = x.longname; //~ ERROR no field `longname` + let y = S { long_name: (), foo: () }; + let _ = y.longname; //~ ERROR no field `longname` + let a = Some(Arc::new(S { long_name: (), foo: () })); + let _ = a.longname; //~ ERROR no field `longname` + let b = Some(S { long_name: (), foo: () }); + let _ = b.long_name; //~ ERROR no field `long_name` + let c = Ok::<_, ()>(Arc::new(S { long_name: (), foo: () })); + let _ = c.longname; //~ ERROR no field `longname` + let d = Ok::<_, ()>(S { long_name: (), foo: () }); + let _ = d.long_name; //~ ERROR no field `long_name` +} diff --git a/tests/ui/suggestions/suggest-field-through-deref.stderr b/tests/ui/suggestions/suggest-field-through-deref.stderr new file mode 100644 index 00000000000..cc9fe2044c9 --- /dev/null +++ b/tests/ui/suggestions/suggest-field-through-deref.stderr @@ -0,0 +1,69 @@ +error[E0609]: no field `longname` on type `Arc<S>` + --> $DIR/suggest-field-through-deref.rs:10:15 + | +LL | let _ = x.longname; + | ^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let _ = x.long_name; + | ~~~~~~~~~ + +error[E0609]: no field `longname` on type `S` + --> $DIR/suggest-field-through-deref.rs:12:15 + | +LL | let _ = y.longname; + | ^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let _ = y.long_name; + | ~~~~~~~~~ + +error[E0609]: no field `longname` on type `Option<Arc<S>>` + --> $DIR/suggest-field-through-deref.rs:14:15 + | +LL | let _ = a.longname; + | ^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let _ = a.unwrap().long_name; + | ~~~~~~~~~~~~~~~~~~ + +error[E0609]: no field `long_name` on type `Option<S>` + --> $DIR/suggest-field-through-deref.rs:16:15 + | +LL | let _ = b.long_name; + | ^^^^^^^^^ unknown field + | +help: one of the expressions' fields has a field of the same name + | +LL | let _ = b.unwrap().long_name; + | +++++++++ + +error[E0609]: no field `longname` on type `Result<Arc<S>, ()>` + --> $DIR/suggest-field-through-deref.rs:18:15 + | +LL | let _ = c.longname; + | ^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let _ = c.unwrap().long_name; + | ~~~~~~~~~~~~~~~~~~ + +error[E0609]: no field `long_name` on type `Result<S, ()>` + --> $DIR/suggest-field-through-deref.rs:20:15 + | +LL | let _ = d.long_name; + | ^^^^^^^^^ unknown field + | +help: one of the expressions' fields has a field of the same name + | +LL | let _ = d.unwrap().long_name; + | +++++++++ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/suggestions/too-many-field-suggestions.stderr b/tests/ui/suggestions/too-many-field-suggestions.stderr index 63ad6fdb169..ac5c8cb60cc 100644 --- a/tests/ui/suggestions/too-many-field-suggestions.stderr +++ b/tests/ui/suggestions/too-many-field-suggestions.stderr @@ -25,7 +25,6 @@ error[E0609]: no field `field` on type `Thing` LL | t.field; | ^^^^^ unknown field | - = note: available fields are: `a0`, `a1`, `a2`, `a3`, `a4` ... and 5 others help: some of the expressions' fields have a field of the same name | LL | t.a0.field; diff --git a/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr b/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr index 12466868f00..fb3573ee2a4 100644 --- a/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr +++ b/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr @@ -24,7 +24,12 @@ error[E0560]: struct `RGB` has no field named `c` --> $DIR/type-mismatch-struct-field-shorthand-2.rs:5:25 | LL | let _ = RGB { r, g, c }; - | ^ help: a field with a similar name exists: `b` + | ^ unknown field + | +help: a field with a similar name exists + | +LL | let _ = RGB { r, g, b }; + | ~ error: aborting due to 3 previous errors diff --git a/tests/ui/traits/new-solver/alias-relate/deeply-nested-no-hang.rs b/tests/ui/traits/new-solver/alias-relate/deeply-nested-no-hang.rs new file mode 100644 index 00000000000..4abce7b57d5 --- /dev/null +++ b/tests/ui/traits/new-solver/alias-relate/deeply-nested-no-hang.rs @@ -0,0 +1,22 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// regression test for trait-system-refactor-initiative#68 +trait Identity { + type Assoc: ?Sized; +} + +impl<T: ?Sized> Identity for T { + type Assoc = T; +} + +type Id<T> = <T as Identity>::Assoc; + +type Five<T> = Id<Id<Id<Id<Id<T>>>>>; +type Ty<T> = Five<Five<Five<Five<Five<T>>>>>; + +trait Trait<T> {} + +impl<T> Trait<T> for Ty<T> {} +impl Trait<u32> for Ty<i32> {} + +fn main() {} diff --git a/tests/ui/traits/new-solver/alias-relate/opaque-hidden-ty-is-rigid-alias.rs b/tests/ui/traits/new-solver/alias-relate/opaque-hidden-ty-is-rigid-alias.rs new file mode 100644 index 00000000000..29a73e1a967 --- /dev/null +++ b/tests/ui/traits/new-solver/alias-relate/opaque-hidden-ty-is-rigid-alias.rs @@ -0,0 +1,8 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +fn test<T: Iterator>(x: T::Item) -> impl Sized { + x +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr index 73d46c4df96..4207c2f80b8 100644 --- a/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr +++ b/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr @@ -1,7 +1,3 @@ -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized> error[E0119]: conflicting implementations of trait `Trait` for type `<LocalTy as Overflow>::Assoc` --> $DIR/trait_ref_is_knowable-norm-overflow.rs:17:1 | @@ -11,7 +7,8 @@ LL | struct LocalTy; LL | impl Trait for <LocalTy as Overflow>::Assoc {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `<LocalTy as Overflow>::Assoc` | - = note: upstream crates may add a new impl of trait `std::marker::Copy` for type `<LocalTy as Overflow>::Assoc` in future versions + = note: downstream crates may implement trait `std::marker::Sized` for type `<LocalTy as Overflow>::Assoc` + = note: downstream crates may implement trait `std::marker::Copy` for type `<LocalTy as Overflow>::Assoc` error: aborting due to previous error diff --git a/tests/ui/tuple/index-invalid.stderr b/tests/ui/tuple/index-invalid.stderr index 8d22f458a6c..ae2c275f52c 100644 --- a/tests/ui/tuple/index-invalid.stderr +++ b/tests/ui/tuple/index-invalid.stderr @@ -2,19 +2,19 @@ error[E0609]: no field `1` on type `(((),),)` --> $DIR/index-invalid.rs:2:22 | LL | let _ = (((),),).1.0; - | ^ + | ^ unknown field error[E0609]: no field `1` on type `((),)` --> $DIR/index-invalid.rs:4:24 | LL | let _ = (((),),).0.1; - | ^ + | ^ unknown field error[E0609]: no field `000` on type `(((),),)` --> $DIR/index-invalid.rs:6:22 | LL | let _ = (((),),).000.000; - | ^^^ + | ^^^ unknown field error: aborting due to 3 previous errors diff --git a/tests/ui/tuple/tuple-index-not-tuple.stderr b/tests/ui/tuple/tuple-index-not-tuple.stderr index a1bcdfaedbc..a267e41b1bc 100644 --- a/tests/ui/tuple/tuple-index-not-tuple.stderr +++ b/tests/ui/tuple/tuple-index-not-tuple.stderr @@ -2,7 +2,12 @@ error[E0609]: no field `0` on type `Point` --> $DIR/tuple-index-not-tuple.rs:6:12 | LL | origin.0; - | ^ help: a field with a similar name exists: `x` + | ^ unknown field + | +help: a field with a similar name exists + | +LL | origin.x; + | ~ error[E0609]: no field `0` on type `Empty` --> $DIR/tuple-index-not-tuple.rs:8:11 diff --git a/tests/ui/tuple/tuple-index-out-of-bounds.stderr b/tests/ui/tuple/tuple-index-out-of-bounds.stderr index 7d7c5cd7892..96090435d06 100644 --- a/tests/ui/tuple/tuple-index-out-of-bounds.stderr +++ b/tests/ui/tuple/tuple-index-out-of-bounds.stderr @@ -2,13 +2,18 @@ error[E0609]: no field `2` on type `Point` --> $DIR/tuple-index-out-of-bounds.rs:7:12 | LL | origin.2; - | ^ help: a field with a similar name exists: `0` + | ^ unknown field + | +help: a field with a similar name exists + | +LL | origin.0; + | ~ error[E0609]: no field `2` on type `({integer}, {integer})` --> $DIR/tuple-index-out-of-bounds.rs:12:11 | LL | tuple.2; - | ^ + | ^ unknown field error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/assoc-type-const.rs b/tests/ui/type-alias-impl-trait/assoc-type-const.rs index 62f66914ee3..6632a3450e5 100644 --- a/tests/ui/type-alias-impl-trait/assoc-type-const.rs +++ b/tests/ui/type-alias-impl-trait/assoc-type-const.rs @@ -1,7 +1,9 @@ // Tests that we properly detect defining usages when using // const generics in an associated opaque type -// check-pass +// check-pass +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next #![feature(impl_trait_in_assoc_type)] trait UnwrapItemsExt<'a, const C: usize> { diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs index f6cbbf04d82..6a2ee761e19 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs @@ -1,6 +1,5 @@ // edition: 2021 // build-fail -//~^^ ERROR cycle detected when computing layout of #![feature(impl_trait_in_assoc_type)] @@ -21,6 +20,7 @@ impl Recur for () { fn recur(self) -> Self::Recur { async move { recur(self).await; } + //~^ ERROR cycle detected when computing layout of } } diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index 2063becdb08..80b6aaaf919 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -1,14 +1,22 @@ -error[E0391]: cycle detected when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:23:9: 23:42}` +error[E0391]: cycle detected when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}` + --> $DIR/indirect-recursion-issue-112047.rs:22:22 | - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:15:31: 17:2}>`... - = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:15:31: 17:2}>`... - = note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:15:31: 17:2}`... +LL | async move { recur(self).await; } + | ^^^^^^^^^^^^^^^^^ + | + = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`... + = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`... +note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}`... + --> $DIR/indirect-recursion-issue-112047.rs:15:5 + | +LL | t.recur().await; + | ^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<() as Recur>::Recur>`... - = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async block@$DIR/indirect-recursion-issue-112047.rs:23:9: 23:42}>`... - = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async block@$DIR/indirect-recursion-issue-112047.rs:23:9: 23:42}>`... - = note: ...which again requires computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:23:9: 23:42}`, completing the cycle -note: cycle used when elaborating drops for `<impl at $DIR/indirect-recursion-issue-112047.rs:19:1: 19:18>::recur` - --> $DIR/indirect-recursion-issue-112047.rs:22:5 + = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`... + = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`... + = note: ...which again requires computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}`, completing the cycle +note: cycle used when elaborating drops for `<impl at $DIR/indirect-recursion-issue-112047.rs:18:1: 18:18>::recur` + --> $DIR/indirect-recursion-issue-112047.rs:21:5 | LL | fn recur(self) -> Self::Recur { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-78450.rs b/tests/ui/type-alias-impl-trait/issue-78450.rs index 2a984c1ed71..236e9f4e88c 100644 --- a/tests/ui/type-alias-impl-trait/issue-78450.rs +++ b/tests/ui/type-alias-impl-trait/issue-78450.rs @@ -1,4 +1,6 @@ // check-pass +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next #![feature(impl_trait_in_assoc_type)] diff --git a/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr b/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr index 7d72c9f811a..c4ad8434ed1 100644 --- a/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr +++ b/tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr @@ -1,5 +1,5 @@ error[E0309]: the parameter type `T` may not live long enough - --> $DIR/wf-in-associated-type.rs:36:23 + --> $DIR/wf-in-associated-type.rs:38:23 | LL | impl<'a, T> Trait<'a, T> for () { | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... @@ -12,7 +12,7 @@ LL | impl<'a, T: 'a> Trait<'a, T> for () { | ++++ error[E0309]: the parameter type `T` may not live long enough - --> $DIR/wf-in-associated-type.rs:36:23 + --> $DIR/wf-in-associated-type.rs:38:23 | LL | impl<'a, T> Trait<'a, T> for () { | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... diff --git a/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs b/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs index 31fbef9f78f..b966ca4bff0 100644 --- a/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs +++ b/tests/ui/type-alias-impl-trait/wf-in-associated-type.rs @@ -1,14 +1,16 @@ // WF check for impl Trait in associated type position. // -// revisions: pass fail +// revisions: pass pass_next fail // [pass] check-pass +// [pass_next] compile-flags: -Ztrait-solver=next +// [pass_next] check-pass // [fail] check-fail #![feature(impl_trait_in_assoc_type)] // The hidden type here (`&'a T`) requires proving `T: 'a`. // We know it holds because of implied bounds from the impl header. -#[cfg(pass)] +#[cfg(any(pass, pass_next))] mod pass { trait Trait<Req> { type Opaque1; diff --git a/tests/ui/typeck/issue-105946.stderr b/tests/ui/typeck/issue-105946.stderr index 26c3b7fbc84..2220271e581 100644 --- a/tests/ui/typeck/issue-105946.stderr +++ b/tests/ui/typeck/issue-105946.stderr @@ -3,6 +3,11 @@ error[E0425]: cannot find value `_y` in this scope | LL | let [_y..] = [Box::new(1), Box::new(2)]; | ^^ not found in this scope + | +help: if you meant to collect the rest of the slice in `_y`, use the at operator + | +LL | let [_y @ ..] = [Box::new(1), Box::new(2)]; + | + error[E0658]: `X..` patterns in slices are experimental --> $DIR/issue-105946.rs:6:10 diff --git a/tests/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr b/tests/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr index 4be4c91dfc2..e1091c8f5ca 100644 --- a/tests/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr +++ b/tests/ui/typeck/issue-52082-type-param-shadows-existing-type.stderr @@ -5,7 +5,7 @@ LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `x` on type `&Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:18 @@ -14,7 +14,7 @@ LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `y` on type `&Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:25 @@ -23,7 +23,7 @@ LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `y` on type `&Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:31:32 @@ -32,7 +32,7 @@ LL | fn equals_ref<Point>(a: &Point, b: &Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `x` on type `Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:11 @@ -41,7 +41,7 @@ LL | fn equals_val<Point>(a: Point, b: Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `x` on type `Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:18 @@ -50,7 +50,7 @@ LL | fn equals_val<Point>(a: Point, b: Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `y` on type `Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:25 @@ -59,7 +59,7 @@ LL | fn equals_val<Point>(a: Point, b: Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error[E0609]: no field `y` on type `Point` --> $DIR/issue-52082-type-param-shadows-existing-type.rs:39:32 @@ -68,7 +68,7 @@ LL | fn equals_val<Point>(a: Point, b: Point) -> bool | ----- type parameter 'Point' declared here LL | { LL | a.x == b.x && a.y == b.y - | ^ + | ^ unknown field error: aborting due to 8 previous errors diff --git a/tests/ui/typeck/issue-53712.stderr b/tests/ui/typeck/issue-53712.stderr index db85919afcb..7ed9cb10379 100644 --- a/tests/ui/typeck/issue-53712.stderr +++ b/tests/ui/typeck/issue-53712.stderr @@ -3,7 +3,8 @@ error[E0609]: no field `0` on type `[{integer}; 5]` | LL | arr.0; | ----^ - | | + | | | + | | unknown field | help: instead of using tuple indexing, use array indexing: `arr[0]` error: aborting due to previous error diff --git a/tests/ui/typeck/issue-65611.stderr b/tests/ui/typeck/issue-65611.stderr index 003c630790d..2278450a6d8 100644 --- a/tests/ui/typeck/issue-65611.stderr +++ b/tests/ui/typeck/issue-65611.stderr @@ -8,7 +8,7 @@ error[E0609]: no field `0` on type `&_` --> $DIR/issue-65611.rs:59:36 | LL | let x = buffer.last().unwrap().0.clone(); - | ^ + | ^ unknown field error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-87181/tuple-field.stderr b/tests/ui/typeck/issue-87181/tuple-field.stderr index 0a7d30b615a..16afac4bd6b 100644 --- a/tests/ui/typeck/issue-87181/tuple-field.stderr +++ b/tests/ui/typeck/issue-87181/tuple-field.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}` --> $DIR/tuple-field.rs:12:15 | LL | thing.bar.0; - | ^ + | ^ unknown field | help: use parentheses to construct this tuple struct | diff --git a/tests/ui/typeck/issue-96738.stderr b/tests/ui/typeck/issue-96738.stderr index 547cffffa2e..2bc8453dda7 100644 --- a/tests/ui/typeck/issue-96738.stderr +++ b/tests/ui/typeck/issue-96738.stderr @@ -8,7 +8,7 @@ error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option:: --> $DIR/issue-96738.rs:3:10 | LL | Some.nonexistent_field; - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ unknown field error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/no-type-for-node-ice.stderr b/tests/ui/typeck/no-type-for-node-ice.stderr index b50241fb1a0..b990b5f951f 100644 --- a/tests/ui/typeck/no-type-for-node-ice.stderr +++ b/tests/ui/typeck/no-type-for-node-ice.stderr @@ -2,7 +2,7 @@ error[E0609]: no field `homura` on type `&'static str` --> $DIR/no-type-for-node-ice.rs:4:8 | LL | "".homura[""]; - | ^^^^^^ + | ^^^^^^ unknown field error: aborting due to previous error diff --git a/tests/ui/unboxed-closures/unboxed-closures-unique-type-id.rs b/tests/ui/unboxed-closures/unboxed-closures-unique-type-id.rs index 4b7016def9d..94bb44d2cf5 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-unique-type-id.rs +++ b/tests/ui/unboxed-closures/unboxed-closures-unique-type-id.rs @@ -10,7 +10,6 @@ // This is a regression test for issue #17021. // // compile-flags: -g -// ignore-asmjs wasm2js does not support source maps yet use std::ptr; diff --git a/tests/ui/union/union-suggest-field.mirunsafeck.stderr b/tests/ui/union/union-suggest-field.mirunsafeck.stderr index 58b1f5cb078..efe4987bd02 100644 --- a/tests/ui/union/union-suggest-field.mirunsafeck.stderr +++ b/tests/ui/union/union-suggest-field.mirunsafeck.stderr @@ -2,13 +2,23 @@ error[E0560]: union `U` has no field named `principle` --> $DIR/union-suggest-field.rs:13:17 | LL | let u = U { principle: 0 }; - | ^^^^^^^^^ help: a field with a similar name exists: `principal` + | ^^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let u = U { principal: 0 }; + | ~~~~~~~~~ error[E0609]: no field `principial` on type `U` --> $DIR/union-suggest-field.rs:17:15 | LL | let w = u.principial; - | ^^^^^^^^^^ help: a field with a similar name exists: `principal` + | ^^^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let w = u.principal; + | ~~~~~~~~~ error[E0615]: attempted to take value of method `calculate` on type `U` --> $DIR/union-suggest-field.rs:21:15 diff --git a/tests/ui/union/union-suggest-field.thirunsafeck.stderr b/tests/ui/union/union-suggest-field.thirunsafeck.stderr index 58b1f5cb078..efe4987bd02 100644 --- a/tests/ui/union/union-suggest-field.thirunsafeck.stderr +++ b/tests/ui/union/union-suggest-field.thirunsafeck.stderr @@ -2,13 +2,23 @@ error[E0560]: union `U` has no field named `principle` --> $DIR/union-suggest-field.rs:13:17 | LL | let u = U { principle: 0 }; - | ^^^^^^^^^ help: a field with a similar name exists: `principal` + | ^^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let u = U { principal: 0 }; + | ~~~~~~~~~ error[E0609]: no field `principial` on type `U` --> $DIR/union-suggest-field.rs:17:15 | LL | let w = u.principial; - | ^^^^^^^^^^ help: a field with a similar name exists: `principal` + | ^^^^^^^^^^ unknown field + | +help: a field with a similar name exists + | +LL | let w = u.principal; + | ~~~~~~~~~ error[E0615]: attempted to take value of method `calculate` on type `U` --> $DIR/union-suggest-field.rs:21:15 diff --git a/tests/ui/unsafe/unsafe-fn-autoderef.stderr b/tests/ui/unsafe/unsafe-fn-autoderef.stderr index 20a88c35610..f563118e8ac 100644 --- a/tests/ui/unsafe/unsafe-fn-autoderef.stderr +++ b/tests/ui/unsafe/unsafe-fn-autoderef.stderr @@ -3,7 +3,8 @@ error[E0609]: no field `f` on type `*const Rec` | LL | return p.f; | --^ - | | + | | | + | | unknown field | help: `p` is a raw pointer; try dereferencing it: `(*p).f` error: aborting due to previous error diff --git a/triagebot.toml b/triagebot.toml index 4c8c1c59beb..a72338d1950 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -713,6 +713,13 @@ style-team = [ "@yaahc", ] +project-stable-mir = [ + "@celinval", + "@oli-obk", + "@spastorino", + "@ouz-a", +] + [assign.owners] "/.github/workflows" = ["infra-ci"] "/Cargo.lock" = ["@Mark-Simulacrum"] @@ -729,6 +736,7 @@ style-team = [ "/compiler/rustc_const_eval/src/interpret" = ["compiler", "mir"] "/compiler/rustc_const_eval/src/transform" = ["compiler", "mir-opt"] "/compiler/rustc_mir_build/src/build" = ["compiler", "mir"] +"/compiler/rustc_smir" = ["project-stable-mir"] "/compiler/rustc_parse" = ["compiler", "parser"] "/compiler/rustc_parse/src/lexer" = ["compiler", "lexer"] "/compiler/rustc_query_impl" = ["compiler", "query-system"] @@ -736,6 +744,7 @@ style-team = [ "/compiler/rustc_trait_selection" = ["compiler", "types"] "/compiler/rustc_traits" = ["compiler", "types"] "/compiler/rustc_type_ir" = ["compiler", "types"] +"/compiler/stable_mir" = ["project-stable-mir"] "/library/alloc" = ["libs"] "/library/core" = ["libs"] "/library/panic_abort" = ["libs"] |
