diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfaa70bb9db0..eeff563d8ecd 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 d3ecadf6e172..ad6dac97a0af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,12 +193,6 @@ dependencies = [ "object", ] -[[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" @@ -237,17 +231,6 @@ version = "0.10.3" 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" @@ -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", @@ -1247,26 +1228,13 @@ dependencies = [ "cfg-if", ] -[[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", @@ -1677,12 +1645,6 @@ dependencies = [ "thiserror", ] -[[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" @@ -1702,15 +1664,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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" @@ -1802,15 +1755,6 @@ dependencies = [ "libm", ] -[[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" @@ -2016,16 +1960,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" 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" @@ -2033,7 +1967,7 @@ 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", ] @@ -2659,15 +2591,15 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" 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", @@ -3106,12 +3038,6 @@ version = "0.4.1" 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" @@ -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", @@ -4043,11 +3970,22 @@ name = "rustc_index" version = "0.0.0" dependencies = [ "arrayvec", + "rustc_index_macros", "rustc_macros", "rustc_serialize", "smallvec", ] +[[package]] +name = "rustc_index_macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", + "synstructure", +] + [[package]] name = "rustc_infer" version = "0.0.0" @@ -4504,7 +4442,7 @@ dependencies = [ name = "rustc_serialize" version = "0.0.0" dependencies = [ - "indexmap 2.0.0", + "indexmap", "rustc_macros", "smallvec", "tempfile", @@ -4554,7 +4492,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ - "indexmap 2.0.0", + "indexmap", "md-5", "rustc_arena", "rustc_data_structures", @@ -4713,7 +4651,7 @@ dependencies = [ "arrayvec", "askama", "expect-test", - "indexmap 2.0.0", + "indexmap", "itertools", "minifier", "once_cell", @@ -4903,9 +4841,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 = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" +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 = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" [[package]] name = "semver" @@ -4942,7 +4889,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 +5069,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 +5377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ "gimli", - "hashbrown 0.14.2", + "hashbrown", "object", "tracing", ] @@ -5602,7 +5549,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 a0f6b1203fca..5bc302305c71 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -47,8 +47,8 @@ Stabilized APIs - [`core::num::Saturating`](https://doc.rust-lang.org/stable/std/num/struct.Saturating.html) - [`impl From for std::process::Stdio`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CStdout%3E-for-Stdio) - [`impl From for std::process::Stdio`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CStderr%3E-for-Stdio) -- [`impl From 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 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 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 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) @@ -71,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/) @@ -200,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/) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index be7d1b207bcf..152ef4bcbfd3 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 34303cbbc9a1..7e713a49a8cf 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -59,9 +59,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: - rustc_type_ir::HashStableContext + rustc_span::HashStableContext -{ +pub trait HashStableContext: rustc_span::HashStableContext { fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7c0a78253a21..541b98729224 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: &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 a6ee93e8a6ba..3189fcf7ff9b 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), + 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 23b8f9c12d8f..48854bbae24c 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 c07dbbc9d67e..2c13e1523f15 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( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b8e18d3434a1..3db9d0b31e0e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -499,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 { - 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 @@ -542,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_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 48421ff71409..0962cb1b8d48 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 + std::ops::Dere } token::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_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a8f1c97edf4..875a71145fa2 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/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 8a930ca59a33..78e9e104bdae 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -5,12 +5,13 @@ use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir::{ Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::{self, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; use rustc_trait_selection::traits::error_reporting::FindExprBySpan; @@ -290,12 +291,69 @@ impl<'tcx> BorrowExplanation<'tcx> { } } + if let ConstraintCategory::Cast { unsize_to: Some(unsize_ty) } = category { + self.add_object_lifetime_default_note(tcx, err, unsize_ty); + } self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} } } + fn add_object_lifetime_default_note( + &self, + tcx: TyCtxt<'tcx>, + err: &mut Diagnostic, + unsize_ty: Ty<'tcx>, + ) { + if let ty::Adt(def, args) = unsize_ty.kind() { + // We try to elaborate the object lifetime defaults and present those to the user. This should + // make it clear where the region constraint is coming from. + let generics = tcx.generics_of(def.did()); + + let mut has_dyn = false; + let mut failed = false; + + let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| { + if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) { + let default = tcx.object_lifetime_default(param.def_id); + + let re_static = tcx.lifetimes.re_static; + + let implied_region = match default { + // This is not entirely precise. + ObjectLifetimeDefault::Empty => re_static, + ObjectLifetimeDefault::Ambiguous => { + failed = true; + re_static + } + ObjectLifetimeDefault::Param(param_def_id) => { + let index = generics.param_def_id_to_index[¶m_def_id] as usize; + args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(|| { + failed = true; + re_static + }) + } + ObjectLifetimeDefault::Static => re_static, + }; + + has_dyn = true; + + Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into() + } else { + arg + } + }); + let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args)); + + if has_dyn && !failed { + err.note(format!( + "due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`" + )); + } + } + } + fn add_lifetime_bound_suggestion_to_diagnostic( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 76c5a06c80db..3f702c9d9a09 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -35,7 +35,7 @@ use crate::session_diagnostics::{ LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, }; -use super::{OutlivesSuggestionBuilder, RegionName}; +use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::region_infer::{BlameConstraint, ExtraConstraintInfo}; use crate::{ nll::ConstraintDescription, @@ -53,7 +53,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Yield => "yielding this value ", ConstraintCategory::UseAsConst => "using this value as a constant ", ConstraintCategory::UseAsStatic => "using this value as a static ", - ConstraintCategory::Cast => "cast ", + ConstraintCategory::Cast { .. } => "cast ", ConstraintCategory::CallArgument(_) => "argument ", ConstraintCategory::TypeAnnotation => "type annotation ", ConstraintCategory::ClosureBounds => "closure body ", @@ -763,7 +763,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let err = LifetimeOutliveErr { span: *span }; let mut diag = self.infcx.tcx.sess.create_err(err); - let fr_name = self.give_region_a_name(*fr).unwrap(); + // In certain scenarios, such as the one described in issue #118021, + // we might encounter a lifetime that cannot be named. + // These situations are bound to result in errors. + // To prevent an immediate ICE, we opt to create a dummy name instead. + let fr_name = self.give_region_a_name(*fr).unwrap_or(RegionName { + name: kw::UnderscoreLifetime, + source: RegionNameSource::Static, + }); fr_name.highlight_region_name(&mut diag); let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap(); outlived_fr_name.highlight_region_name(&mut diag); 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 c84256f8ff32..478eedc26d30 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 b7ce2f3cca4c..bb9ce5daba26 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -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 = || { @@ -1934,7 +1934,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -1959,7 +1959,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -1988,7 +1988,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2013,7 +2013,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_trait_ref( trait_ref, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { + unsize_to: Some(tcx.fold_regions(ty, |r, _| { + if let ty::ReVar(_) = r.kind() { + tcx.lifetimes.re_erased + } else { + r + } + })), + }, ); } @@ -2033,7 +2041,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .iter() .map(|predicate| predicate.with_self_ty(tcx, self_ty)), location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ); let outlives_predicate = tcx.mk_predicate(Binder::dummy( @@ -2044,7 +2052,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_predicate( outlives_predicate, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ); } @@ -2065,7 +2073,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty_from, *ty_to, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2131,7 +2139,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty_elem, *ty_to, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 02f94e5b9728..abeb021adc81 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -738,13 +738,13 @@ trait InferCtxtExt<'tcx> { where T: TypeFoldable>; - 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>, @@ -780,7 +780,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { where T: TypeFoldable>, { - 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_late_param(self.tcx, all_outlive_scope.to_def_id(), br.kind); @@ -810,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>, @@ -830,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>, diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index 12aa69d3c795..bd3b051185b4 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 47d9a3b93f72..05dc28d07453 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 b49dc3aff7aa..cb5dd51fee31 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 1a2b2bbc5881..ca6ecdf1d0e8 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/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index b832b06e0ffb..80ef1e49f231 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 e62788f2e507..355282911c25 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 bbb8a010d965..731828caae2c 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 a8f6d7a20248..791d457993de 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 c4572e035258..0ff1473da431 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 91b1547cb6ea..71557d49ef2c 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 b0853d30e03b..cf68a3857c58 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> { +) -> Option { 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 ce0eecca8a8b..25d14319f579 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, ) { - // 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::>(); - 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 e9b7daf14924..659e6c133ef5 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, + 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 4c536048626e..8dd2b6ed014e 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, + 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 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 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 = 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 ea137c4ca1e8..0bd211fd614f 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/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index 65e7a697ab0d..308bc55ead71 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -99,8 +99,10 @@ jobs: - name: Build run: | ./y.sh prepare --only-libcore - ./y.sh build - cargo test + # TODO: remove --features master when it is back to the default. + ./y.sh build --features master + # TODO: remove --features master when it is back to the default. + cargo test --features master ./clean_all.sh - name: Prepare dependencies @@ -121,7 +123,8 @@ jobs: - name: Run tests run: | - ./test.sh --release --clean --build-sysroot ${{ matrix.commands }} + # TODO: remove --features master when it is back to the default. + ./test.sh --features master --release --clean --build-sysroot ${{ matrix.commands }} duplicates: runs-on: ubuntu-latest diff --git a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index 27864dcadd0f..ae8de79b773d 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml @@ -21,11 +21,14 @@ jobs: libgccjit_version: - gcc: "libgccjit.so" artifacts_branch: "master" + # TODO: switch back to --no-default-features in the case of libgccjit 12 when the default is to enable + # master again. + extra: "--features master" - gcc: "libgccjit_without_int128.so" artifacts_branch: "master-without-128bit-integers" + extra: "--features master" - gcc: "libgccjit12.so" artifacts_branch: "gcc12" - extra: "--no-default-features" # FIXME(antoyo): we need to set GCC_EXEC_PREFIX so that the linker can find the linker plugin. # Not sure why it's not found otherwise. env_extra: "TEST_FLAGS='-Cpanic=abort -Zpanic-abort-tests' GCC_EXEC_PREFIX=/usr/lib/gcc/" diff --git a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml index 55ee0a212142..4d9d7e23dc2b 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml @@ -114,8 +114,10 @@ jobs: - name: Build run: | ./y.sh prepare --only-libcore --cross - ./y.sh build --target-triple m68k-unknown-linux-gnu - CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu cargo test + # TODO: remove --features master when it is back to the default. + ./y.sh build --target-triple m68k-unknown-linux-gnu --features master + # TODO: remove --features master when it is back to the default. + CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu cargo test --features master ./clean_all.sh - name: Prepare dependencies @@ -136,4 +138,5 @@ jobs: - name: Run tests run: | - ./test.sh --release --clean --build-sysroot ${{ matrix.commands }} + # TODO: remove --features master when it is back to the default. + ./test.sh --release --features master --clean --build-sysroot ${{ matrix.commands }} diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml index ae1134177a77..43b90fcec933 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml @@ -78,8 +78,10 @@ jobs: - name: Build run: | ./y.sh prepare --only-libcore - EMBED_LTO_BITCODE=1 ./y.sh build --release --release-sysroot - cargo test + # TODO: remove --features master when it is back to the default. + EMBED_LTO_BITCODE=1 ./y.sh build --release --release-sysroot --features master + # TODO: remove --features master when it is back to the default. + cargo test --features master ./clean_all.sh - name: Prepare dependencies @@ -102,4 +104,5 @@ jobs: - name: Run tests run: | - EMBED_LTO_BITCODE=1 ./test.sh --release --clean --release-sysroot --build-sysroot ${{ matrix.commands }} + # TODO: remove --features master when it is back to the default. + EMBED_LTO_BITCODE=1 ./test.sh --release --clean --release-sysroot --build-sysroot ${{ matrix.commands }} --features master diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index 28ac3cb65422..42109ba3e024 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -92,8 +92,10 @@ jobs: - name: Build run: | ./y.sh prepare --only-libcore - ./y.sh build --release --release-sysroot - cargo test + # TODO: remove `--features master` when it is back to the default. + ./y.sh build --release --release-sysroot --features master + # TODO: remove --features master when it is back to the default. + cargo test --features master - name: Clean if: ${{ !matrix.cargo_runner }} @@ -111,12 +113,14 @@ jobs: uses: actions-rs/cargo@v1.0.3 with: command: build - args: --release + # TODO: remove `--features master` when it is back to the default. + args: --release --features master - name: Run tests if: ${{ !matrix.cargo_runner }} run: | - ./test.sh --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore + # TODO: remove `--features master` when it is back to the default. + ./test.sh --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore --features master - name: Run stdarch tests if: ${{ !matrix.cargo_runner }} diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index b8e2e5d80804..7c1863369278 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -74,7 +74,7 @@ dependencies = [ [[package]] name = "gccjit" version = "1.0.0" -source = "git+https://github.com/antoyo/gccjit.rs#c52a218f5529321285b4489e5562a00e5428e033" +source = "git+https://github.com/antoyo/gccjit.rs#6e290f25b1d1edab5ae9ace486fd2dc8c08d6421" dependencies = [ "gccjit_sys", ] @@ -82,7 +82,7 @@ dependencies = [ [[package]] name = "gccjit_sys" version = "0.0.1" -source = "git+https://github.com/antoyo/gccjit.rs#c52a218f5529321285b4489e5562a00e5428e033" +source = "git+https://github.com/antoyo/gccjit.rs#6e290f25b1d1edab5ae9ace486fd2dc8c08d6421" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh b/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh index 116fd36e7a7b..ebc7dc375b12 100755 --- a/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh +++ b/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh @@ -28,3 +28,7 @@ fi # Copy files to sysroot mkdir -p sysroot/lib/rustlib/$TARGET_TRIPLE/lib/ cp -r target/$TARGET_TRIPLE/$sysroot_channel/deps/* sysroot/lib/rustlib/$TARGET_TRIPLE/lib/ +# Copy the source files to the sysroot (Rust for Linux needs this). +source_dir=sysroot/lib/rustlib/src/rust +mkdir -p $source_dir +cp -r sysroot_src/library/ $source_dir diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index eaca7a987d6b..f1c3701a946e 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -194,6 +194,12 @@ fn build_sysroot( copier, )?; + // Copy the source files to the sysroot (Rust for Linux needs this). + let sysroot_src_path = "sysroot/lib/rustlib/src/rust"; + fs::create_dir_all(&sysroot_src_path) + .map_err(|error| format!("Failed to create directory `{}`: {:?}", sysroot_src_path, error))?; + run_command(&[&"cp", &"-r", &"sysroot_src/library/", &sysroot_src_path], None)?; + Ok(()) } diff --git a/compiler/rustc_codegen_gcc/failing-ui-tests12.txt b/compiler/rustc_codegen_gcc/failing-ui-tests12.txt index f91aa9253184..4af93939b064 100644 --- a/compiler/rustc_codegen_gcc/failing-ui-tests12.txt +++ b/compiler/rustc_codegen_gcc/failing-ui-tests12.txt @@ -38,3 +38,5 @@ tests/ui/target-feature/missing-plusminus.rs tests/ui/sse2.rs tests/ui/codegen/issue-79865-llvm-miscompile.rs tests/ui/intrinsics/intrinsics-integer.rs +tests/ui/std-backtrace.rs +tests/ui/mir/alignment/packed.rs diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch index 9520a5a39edc..914ae986b50e 100644 --- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch +++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch @@ -1,6 +1,6 @@ -From 7bcd24ec6d4a96121874cb1ae5a23ea274aeff34 Mon Sep 17 00:00:00 2001 +From a5663265f797a43c502915c356fe7899c16cee92 Mon Sep 17 00:00:00 2001 From: None -Date: Thu, 19 Oct 2023 13:12:51 -0400 +Date: Sat, 18 Nov 2023 10:50:36 -0500 Subject: [PATCH] [core] Disable portable-simd test --- @@ -8,18 +8,18 @@ Subject: [PATCH] [core] Disable portable-simd test 1 file changed, 2 deletions(-) diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index 5814ed4..194ad4c 100644 +index d0a119c..76fdece 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs -@@ -90,7 +90,6 @@ +@@ -89,7 +89,6 @@ + #![feature(never_type)] #![feature(unwrap_infallible)] - #![feature(pointer_byte_offsets)] #![feature(pointer_is_aligned)] -#![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(lazy_cell)] #![feature(unsized_tuple_coercion)] -@@ -157,7 +156,6 @@ mod pin; +@@ -155,7 +154,6 @@ mod pin; mod pin_macro; mod ptr; mod result; @@ -28,5 +28,5 @@ index 5814ed4..194ad4c 100644 mod str; mod str_lossy; -- -2.42.0 +2.42.1 diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 205ec53b425b..1962c217258a 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-10-21" +channel = "nightly-2023-11-17" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 5073066c1383..b0788718da4d 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -3,7 +3,6 @@ use std::env; use std::time::Instant; use gccjit::{ - Context, FunctionType, GlobalKind, }; @@ -18,8 +17,9 @@ use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::DebugInfoMethods; use rustc_session::config::DebugInfo; use rustc_span::Symbol; +use rustc_target::spec::PanicStrategy; -use crate::{LockedTargetInfo, gcc_util}; +use crate::{LockedTargetInfo, gcc_util, new_context}; use crate::GccContext; use crate::builder::Builder; use crate::context::CodegenCx; @@ -88,20 +88,18 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: Lock fn module_codegen(tcx: TyCtxt<'_>, (cgu_name, target_info): (Symbol, LockedTargetInfo)) -> ModuleCodegen { let cgu = tcx.codegen_unit(cgu_name); // Instantiate monomorphizations without filling out definitions yet... - let context = Context::default(); + let context = new_context(tcx); - context.add_command_line_option("-fexceptions"); - context.add_driver_option("-fexceptions"); + if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + context.add_command_line_option("-fexceptions"); + context.add_driver_option("-fexceptions"); + } let disabled_features: HashSet<_> = tcx.sess.opts.cg.target_feature.split(',') .filter(|feature| feature.starts_with('-')) .map(|string| &string[1..]) .collect(); - if tcx.sess.target.arch == "x86" || tcx.sess.target.arch == "x86_64" { - context.add_command_line_option("-masm=intel"); - } - if !disabled_features.contains("avx") && tcx.sess.target.arch == "x86_64" { // NOTE: we always enable AVX because the equivalent of llvm.x86.sse2.cmp.pd in GCC for // SSE2 is multiple builtins, so we use the AVX __builtin_ia32_cmppd instead. diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index ea8550d20f36..9b9b3ea4f870 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -76,6 +76,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { a >> b } } + else if a_type.is_vector() && a_type.is_vector() { + a >> b + } else if a_native && !b_native { self.gcc_lshr(a, self.gcc_int_cast(b, a_type)) } @@ -144,7 +147,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fn additive_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); - if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { + if (self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type)) || (a_type.is_vector() && b_type.is_vector()) { if a_type != b_type { if a_type.is_vector() { // Vector types need to be bitcast. @@ -158,6 +161,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.context.new_binary_op(None, operation, a_type, a, b) } else { + debug_assert!(a_type.dyncast_array().is_some()); + debug_assert!(b_type.dyncast_array().is_some()); let signed = a_type.is_compatible_with(self.i128_type); let func_name = match (operation, signed) { @@ -189,10 +194,12 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fn multiplicative_operation(&self, operation: BinaryOp, operation_name: &str, signed: bool, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); - if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { + if (self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type)) || (a_type.is_vector() && b_type.is_vector()) { self.context.new_binary_op(None, operation, a_type, a, b) } else { + debug_assert!(a_type.dyncast_array().is_some()); + debug_assert!(b_type.dyncast_array().is_some()); let sign = if signed { "" @@ -337,6 +344,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { pub fn operation_with_overflow(&self, func_name: &str, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) { let a_type = lhs.get_type(); let b_type = rhs.get_type(); + debug_assert!(a_type.dyncast_array().is_some()); + debug_assert!(b_type.dyncast_array().is_some()); let param_a = self.context.new_parameter(None, a_type, "a"); let param_b = self.context.new_parameter(None, b_type, "b"); let result_field = self.context.new_field(None, a_type, "result"); @@ -496,7 +505,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { pub fn gcc_xor(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); - if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { + if a_type.is_vector() && b_type.is_vector() { + let b = self.bitcast_if_needed(b, a_type); + a ^ b + } + else if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { a ^ b } else { @@ -527,6 +540,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { a << b } } + else if a_type.is_vector() && a_type.is_vector() { + a << b + } else if a_native && !b_native { self.gcc_shl(a, self.gcc_int_cast(b, a_type)) } @@ -690,6 +706,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let a_native = self.is_native_int_type_or_bool(a_type); let b_native = self.is_native_int_type_or_bool(b_type); if a_type.is_vector() && b_type.is_vector() { + let b = self.bitcast_if_needed(b, a_type); self.context.new_binary_op(None, operation, a_type, a, b) } else if a_native && b_native { @@ -748,6 +765,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { return self.context.new_cast(None, value, dest_typ); } + debug_assert!(value_type.dyncast_array().is_some()); let name_suffix = match self.type_kind(dest_typ) { TypeKind::Float => "tisf", @@ -781,6 +799,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { return self.context.new_cast(None, value, dest_typ); } + debug_assert!(value_type.dyncast_array().is_some()); let name_suffix = match self.type_kind(value_type) { TypeKind::Float => "sfti", diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index fd4af984bc03..8c7bae0c8866 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -39,6 +39,8 @@ extern crate rustc_errors; extern crate rustc_fluent_macro; extern crate rustc_fs_util; extern crate rustc_hir; +#[cfg(feature="master")] +extern crate rustc_interface; extern crate rustc_macros; extern crate rustc_metadata; extern crate rustc_middle; @@ -86,7 +88,7 @@ use std::sync::atomic::Ordering; use gccjit::{Context, OptimizationLevel}; #[cfg(feature="master")] -use gccjit::TargetInfo; +use gccjit::{TargetInfo, Version}; #[cfg(not(feature="master"))] use gccjit::CType; use errors::LTONotSupported; @@ -244,17 +246,33 @@ impl CodegenBackend for GccCodegenBackend { } } +fn new_context<'gcc, 'tcx>(tcx: TyCtxt<'tcx>) -> Context<'gcc> { + let context = Context::default(); + if tcx.sess.target.arch == "x86" || tcx.sess.target.arch == "x86_64" { + context.add_command_line_option("-masm=intel"); + } + #[cfg(feature="master")] + { + let version = Version::get(); + let version = format!("{}.{}.{}", version.major, version.minor, version.patch); + context.set_output_ident(&format!("rustc version {} with libgccjit {}", + rustc_interface::util::rustc_version_str().unwrap_or("unknown version"), + version, + )); + } + // TODO(antoyo): check if this should only be added when using -Cforce-unwind-tables=n. + context.add_command_line_option("-fno-asynchronous-unwind-tables"); + context +} + impl ExtraBackendMethods for GccCodegenBackend { fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind) -> Self::Module { let mut mods = GccContext { - context: Context::default(), + context: new_context(tcx), should_combine_object_files: false, temp_dir: None, }; - if tcx.sess.target.arch == "x86" || tcx.sess.target.arch == "x86_64" { - mods.context.add_command_line_option("-masm=intel"); - } unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind); } mods } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 6e3a4cae2f62..a5ffe0650a8a 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -348,50 +348,18 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Direct(_) => { // ABI-compatible Rust types have the same `layout.abi` (up to validity ranges), // and for Scalar ABIs the LLVM type is fully determined by `layout.abi`, - // guarnateeing that we generate ABI-compatible LLVM IR. Things get tricky for - // aggregates... - if matches!(arg.layout.abi, abi::Abi::Aggregate { .. }) { - assert!( - arg.layout.is_sized(), - "`PassMode::Direct` for unsized type: {}", - arg.layout.ty - ); - // This really shouldn't happen, since `immediate_llvm_type` will use - // `layout.fields` to turn this Rust type into an LLVM type. This means all - // sorts of Rust type details leak into the ABI. However wasm sadly *does* - // currently use this mode so we have to allow it -- but we absolutely - // shouldn't let any more targets do that. - // (Also see .) - // - // The unstable abi `PtxKernel` also uses Direct for now. - // It needs to switch to something else before stabilization can happen. - // (See issue: https://github.com/rust-lang/rust/issues/117271) - assert!( - matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64") - || self.conv == Conv::PtxKernel, - "`PassMode::Direct` for aggregates only allowed on wasm and `extern \"ptx-kernel\"` fns\nProblematic type: {:#?}", - arg.layout, - ); - } + // guaranteeing that we generate ABI-compatible LLVM IR. arg.layout.immediate_llvm_type(cx) } PassMode::Pair(..) => { // ABI-compatible Rust types have the same `layout.abi` (up to validity ranges), // so for ScalarPair we can easily be sure that we are generating ABI-compatible // LLVM IR. - assert!( - matches!(arg.layout.abi, abi::Abi::ScalarPair(..)), - "PassMode::Pair for type {}", - arg.layout.ty - ); llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true)); llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack } => { - // `Indirect` with metadata is only for unsized types, and doesn't work with - // on-stack passing. - assert!(arg.layout.is_unsized() && !on_stack); + PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { // Construct the type of a (wide) pointer to `ty`, and pass its two fields. // Any two ABI-compatible unsized types have the same metadata type and // moreover the same metadata value leads to the same dynamic size and @@ -402,13 +370,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true)); continue; } - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { - assert!(arg.layout.is_sized()); - cx.type_ptr() - } + PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => cx.type_ptr(), PassMode::Cast { cast, pad_i32 } => { - // `Cast` means "transmute to `CastType`"; that only makes sense for sized types. - assert!(arg.layout.is_sized()); // add padding if *pad_i32 { llargument_tys.push(Reg::i32().llvm_type(cx)); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 9d5204034def..8d335ff17183 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_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 4dae49f81a38..d385d367f396 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -46,7 +46,7 @@ libc = "0.2.50" # tidy-alphabetical-end [dependencies.object] -version = "0.32.0" +version = "0.32.1" default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index dd9d277fb775..33e8f352cd8a 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/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index cb60ed729c1b..d716774690aa 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -226,6 +226,10 @@ pub(crate) fn create_object_file(sess: &Session) -> Option object::write::Mach build_version } +/// Is Apple's CPU subtype `arm64e`s +fn macho_is_arm64e(target: &Target) -> bool { + return target.llvm_target.starts_with("arm64e"); +} + pub enum MetadataPosition { First, Last, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 198e5696357a..23054975548c 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -322,8 +322,13 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if lhs_sz < rhs_sz { bx.trunc(rhs, lhs_llty) } else if lhs_sz > rhs_sz { - // FIXME (#1877: If in the future shifting by negative - // values is no longer undefined then this is wrong. + // We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the + // RHS to `255i32`. But then we mask the shift amount to be within the size of the LHS + // anyway so the result is `31` as it should be. All the extra bits introduced by zext + // are masked off so their value does not matter. + // FIXME: if we ever support 512bit integers, this will be wrong! For such large integers, + // the extra bits introduced by zext are *not* all masked away any more. + assert!(lhs_sz <= 256); bx.zext(rhs, lhs_llty) } else { rhs diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 1a85eb8dd797..3c7e8873b4d0 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_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4b447229c5f6..739dbcc5aba6 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -107,6 +107,14 @@ impl interpret::AllocMap for FxIndexMap { FxIndexMap::contains_key(self, k) } + #[inline(always)] + fn contains_key_ref(&self, k: &Q) -> bool + where + K: Borrow, + { + FxIndexMap::contains_key(self, k) + } + #[inline(always)] fn insert(&mut self, k: K, v: V) -> Option { FxIndexMap::insert(self, k, v) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index ed2d81727f73..2bee4b45ad02 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -11,7 +11,7 @@ use rustc_middle::mir; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::{Abi, VariantIdx}; #[instrument(skip(ecx), level = "debug")] fn branches<'tcx>( @@ -101,11 +101,16 @@ pub(crate) fn const_to_valtree_inner<'tcx>( // Not all raw pointers are allowed, as we cannot properly test them for // equality at compile-time (see `ptr_guaranteed_cmp`). // However we allow those that are just integers in disguise. - // (We could allow wide raw pointers where both sides are integers in the future, - // but for now we reject them.) - let Ok(val) = ecx.read_scalar(place) else { + // First, get the pointer. Remember it might be wide! + let Ok(val) = ecx.read_immediate(place) else { return Err(ValTreeCreationError::Other); }; + // We could allow wide raw pointers where both sides are integers in the future, + // but for now we reject them. + if matches!(val.layout.abi, Abi::ScalarPair(..)) { + return Err(ValTreeCreationError::Other); + } + let val = val.to_scalar(); // We are in the CTFE machine, so ptr-to-int casts will fail. // This can only be `Ok` if `val` already is an integer. let Ok(val) = val.try_to_int() else { diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 61fe9151d8b4..6617f6f2ffb6 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -49,6 +49,14 @@ pub trait AllocMap { where K: Borrow; + /// Callers should prefer [`AllocMap::contains_key`] when it is possible to call because it may + /// be more efficient. This function exists for callers that only have a shared reference + /// (which might make it slightly less efficient than `contains_key`, e.g. if + /// the data is stored inside a `RefCell`). + fn contains_key_ref(&self, k: &Q) -> bool + where + K: Borrow; + /// Inserts a new entry into the map. fn insert(&mut self, k: K, v: V) -> Option; diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 16905e93bf1f..d5b165a7415b 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -692,6 +692,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok((&mut alloc.extra, machine)) } + /// Check whether an allocation is live. This is faster than calling + /// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is + /// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics. + pub fn is_alloc_live(&self, id: AllocId) -> bool { + self.tcx.try_get_global_alloc(id).is_some() + || self.memory.alloc_map.contains_key_ref(&id) + || self.memory.extra_fn_ptr_map.contains_key(&id) + } + /// Obtain the size and alignment of an allocation, even if that allocation has /// been deallocated. pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) { diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 128b548f43d4..538077a0b777 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -156,41 +156,35 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Shift ops can have an RHS with a different numeric type. if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) { - let size = u128::from(left_layout.size.bits()); - // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its - // zero-extended form). This matches the codegen backend: - // . - // The overflow check is also ignorant to the sign: - // . - // This would behave rather strangely if we had integer types of size 256: a shift by - // -1i8 would actually shift by 255, but that would *not* be considered overflowing. A - // shift by -1i16 though would be considered overflowing. If we had integers of size - // 512, then a shift by -1i8 would even produce a different result than one by -1i16: - // the first shifts by 255, the latter by u16::MAX % 512 = 511. Lucky enough, our - // integers are maximally 128bits wide, so negative shifts *always* overflow and we have - // consistent results for the same value represented at different bit widths. - assert!(size <= 128); - let original_r = r; - let overflow = r >= size; - // The shift offset is implicitly masked to the type size, to make sure this operation - // is always defined. This is the one MIR operator that does *not* directly map to a - // single LLVM operation. See - // - // for the corresponding truncation in our codegen backends. - let r = r % size; - let r = u32::try_from(r).unwrap(); // we masked so this will always fit + let size = left_layout.size.bits(); + // The shift offset is implicitly masked to the type size. (This is the one MIR operator + // that does *not* directly map to a single LLVM operation.) Compute how much we + // actually shift and whether there was an overflow due to shifting too much. + let (shift_amount, overflow) = if right_layout.abi.is_signed() { + let shift_amount = self.sign_extend(r, right_layout) as i128; + let overflow = shift_amount < 0 || shift_amount >= i128::from(size); + let masked_amount = (shift_amount as u128) % u128::from(size); + debug_assert_eq!(overflow, shift_amount != (masked_amount as i128)); + (masked_amount, overflow) + } else { + let shift_amount = r; + let masked_amount = shift_amount % u128::from(size); + (masked_amount, shift_amount != masked_amount) + }; + let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit + // Compute the shifted result. let result = if left_layout.abi.is_signed() { let l = self.sign_extend(l, left_layout) as i128; let result = match bin_op { - Shl | ShlUnchecked => l.checked_shl(r).unwrap(), - Shr | ShrUnchecked => l.checked_shr(r).unwrap(), + Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(), + Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(), _ => bug!(), }; result as u128 } else { match bin_op { - Shl | ShlUnchecked => l.checked_shl(r).unwrap(), - Shr | ShrUnchecked => l.checked_shr(r).unwrap(), + Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(), + Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(), _ => bug!(), } }; @@ -199,7 +193,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { throw_ub_custom!( fluent::const_eval_overflow_shift, - val = original_r, + val = if right_layout.abi.is_signed() { + (self.sign_extend(r, right_layout) as i128).to_string() + } else { + r.to_string() + }, name = intrinsic_name ); } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 32af537e271d..b3d6b891b118 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_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 5922922d47b6..3e8a0a2b7df0 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -285,6 +285,12 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> { UnwindAction::Unreachable | UnwindAction::Terminate(UnwindTerminateReason::Abi) => (), } } + + fn is_critical_call_edge(&self, target: Option, unwind: UnwindAction) -> bool { + let Some(target) = target else { return false }; + matches!(unwind, UnwindAction::Cleanup(_) | UnwindAction::Terminate(_)) + && self.body.basic_blocks.predecessors()[target].len() > 1 + } } impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { @@ -425,6 +431,22 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } self.check_unwind_edge(location, *unwind); + // The code generation assumes that there are no critical call edges. The assumption + // is used to simplify inserting code that should be executed along the return edge + // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, + // the code generation should be responsible for handling it. + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) + && self.is_critical_call_edge(*target, *unwind) + { + self.fail( + location, + format!( + "encountered critical edge in `Call` terminator {:?}", + terminator.kind, + ), + ); + } + // The call destination place and Operand::Move place used as an argument might be // passed by a reference to the callee. Consequently they must be non-overlapping // and cannot be packed. Currently this simply checks for duplicate places. diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index da7c2440faad..545ff32e598a 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 b323ae8d29e0..6ee7213a19d8 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -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, ) -> 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 should_stop == Compilation::Stop { + 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 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,7 +471,7 @@ fn run_compiler( if let Some(linker) = linker { let _timer = sess.timer("link"); - linker.link()? + linker.link(sess, codegen_backend)? } if sess.opts.unstable_opts.print_fuel.is_some() { @@ -562,15 +540,6 @@ pub enum Compilation { Continue, } -impl Compilation { - fn and_then 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 = @@ -675,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 {}) } } @@ -720,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( @@ -991,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: @@ -1017,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); @@ -1094,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."); } diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index cc533b9941ab..7cd63bc6422c 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_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 470f318eb33e..0aaa8ae69e16 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -777,17 +777,15 @@ impl Diagnostic { applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - let mut suggestions: Vec<_> = suggestions.into_iter().collect(); - suggestions.sort(); - - debug_assert!( - !(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())), - "Span must not be empty and have no suggestion" - ); - let substitutions = suggestions .into_iter() - .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }) + .map(|snippet| { + debug_assert!( + !(sp.is_empty() && snippet.is_empty()), + "Span must not be empty and have no suggestion" + ); + Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] } + }) .collect(); self.push_suggestion(CodeSuggestion { substitutions, diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 0cb75c71b734..aa3749334d97 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -145,6 +145,25 @@ impl JsonEmitter { pub fn ignored_directories_in_source_blocks(self, value: Vec) -> Self { Self { ignored_directories_in_source_blocks: value, ..self } } + + fn emit(&mut self, val: EmitTyped<'_>) -> io::Result<()> { + if self.pretty { + serde_json::to_writer_pretty(&mut *self.dst, &val)? + } else { + serde_json::to_writer(&mut *self.dst, &val)? + }; + self.dst.write_all(b"\n")?; + self.dst.flush() + } +} + +#[derive(Serialize)] +#[serde(tag = "$message_type", rename_all = "snake_case")] +enum EmitTyped<'a> { + Diagnostic(Diagnostic), + Artifact(ArtifactNotification<'a>), + FutureIncompat(FutureIncompatReport<'a>), + UnusedExtern(UnusedExterns<'a, 'a, 'a>), } impl Translate for JsonEmitter { @@ -160,12 +179,7 @@ impl Translate for JsonEmitter { impl Emitter for JsonEmitter { fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) { let data = Diagnostic::from_errors_diagnostic(diag, self); - let result = if self.pretty { - writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap()) - } else { - writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap()) - } - .and_then(|_| self.dst.flush()); + let result = self.emit(EmitTyped::Diagnostic(data)); if let Err(e) = result { panic!("failed to print diagnostics: {e:?}"); } @@ -173,34 +187,28 @@ impl Emitter for JsonEmitter { fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { let data = ArtifactNotification { artifact: path, emit: artifact_type }; - let result = if self.pretty { - writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap()) - } else { - writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap()) - } - .and_then(|_| self.dst.flush()); + let result = self.emit(EmitTyped::Artifact(data)); if let Err(e) = result { panic!("failed to print notification: {e:?}"); } } fn emit_future_breakage_report(&mut self, diags: Vec) { - let data: Vec = diags + let data: Vec> = diags .into_iter() .map(|mut diag| { if diag.level == crate::Level::Allow { diag.level = crate::Level::Warning(None); } - FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) } + FutureBreakageItem { + diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( + &diag, self, + )), + } }) .collect(); let report = FutureIncompatReport { future_incompat_report: data }; - let result = if self.pretty { - writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&report).unwrap()) - } else { - writeln!(&mut self.dst, "{}", serde_json::to_string(&report).unwrap()) - } - .and_then(|_| self.dst.flush()); + let result = self.emit(EmitTyped::FutureIncompat(report)); if let Err(e) = result { panic!("failed to print future breakage report: {e:?}"); } @@ -209,12 +217,7 @@ impl Emitter for JsonEmitter { fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) { let lint_level = lint_level.as_str(); let data = UnusedExterns { lint_level, unused_extern_names: unused_externs }; - let result = if self.pretty { - writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap()) - } else { - writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap()) - } - .and_then(|_| self.dst.flush()); + let result = self.emit(EmitTyped::UnusedExtern(data)); if let Err(e) = result { panic!("failed to print unused externs: {e:?}"); } @@ -313,13 +316,15 @@ struct ArtifactNotification<'a> { } #[derive(Serialize)] -struct FutureBreakageItem { - diagnostic: Diagnostic, +struct FutureBreakageItem<'a> { + // Always EmitTyped::Diagnostic, but we want to make sure it gets serialized + // with "$message_type". + diagnostic: EmitTyped<'a>, } #[derive(Serialize)] -struct FutureIncompatReport { - future_incompat_report: Vec, +struct FutureIncompatReport<'a> { + future_incompat_report: Vec>, } // NOTE: Keep this in sync with the equivalent structs in rustdoc's diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index e060375646c2..64cf9ced9c16 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 for more information"); diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 7e85beaadcbc..1c78232a0f34 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), + 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 c617cd76e3cb..39a16259fa6e 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 aa4c7a531355..41ec0ed84f70 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 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_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index e213623e06d9..f1f624269aee 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -5,7 +5,6 @@ macro_rules! arena_types { ($macro:path) => ( $macro!([ // HIR types - [] hir_krate: rustc_hir::Crate<'tcx>, [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::Attribute, [] owner_info: rustc_hir::OwnerInfo<'tcx>, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index ed1dc751fbab..e901eba35b78 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -13,8 +13,7 @@ use std::array::IntoIter; use std::fmt::Debug; /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct. -#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum CtorOf { /// This `DefKind::Ctor` is a synthesized constructor of a tuple or unit struct. Struct, @@ -23,8 +22,7 @@ pub enum CtorOf { } /// What kind of constructor something is. -#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum CtorKind { /// Constructor function automatically created by a tuple struct/variant. Fn, @@ -33,8 +31,7 @@ pub enum CtorKind { } /// An attribute that is not a macro; e.g., `#[inline]` or `#[rustfmt::skip]`. -#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum NonMacroAttrKind { /// Single-segment attribute defined by the language (`#[inline]`) Builtin(Symbol), @@ -48,8 +45,7 @@ pub enum NonMacroAttrKind { } /// What kind of definition something is; e.g., `mod` vs `struct`. -#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum DefKind { // Type namespace Mod, @@ -299,8 +295,7 @@ impl DefKind { /// - the call to `str_to_string` will resolve to [`Res::Def`], with the [`DefId`] /// pointing to the definition of `str_to_string` in the current crate. // -#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum Res { /// Definition having a unique ID (`DefId`), corresponds to something defined in user code. /// @@ -591,6 +586,8 @@ impl NonMacroAttrKind { } } + // Currently trivial, but exists in case a new kind is added in the future whose name starts + // with a vowel. pub fn article(self) -> &'static str { "a" } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c4e44a6a4e38..1d7e8dc5eaa3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -13,7 +13,6 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_error_messages::MultiSpan; use rustc_index::IndexVec; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; @@ -76,13 +75,6 @@ impl ParamName { ParamName::Fresh | ParamName::Error => Ident::with_dummy_span(kw::UnderscoreLifetime), } } - - pub fn normalize_to_macros_2_0(&self) -> ParamName { - match *self { - ParamName::Plain(ident) => ParamName::Plain(ident.normalize_to_macros_2_0()), - param_name => param_name, - } - } } #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] @@ -116,7 +108,7 @@ pub enum LifetimeName { } impl LifetimeName { - pub fn is_elided(&self) -> bool { + fn is_elided(&self) -> bool { match self { LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, @@ -289,10 +281,6 @@ impl GenericArg<'_> { } } - pub fn is_synthetic(&self) -> bool { - matches!(self, GenericArg::Lifetime(lifetime) if lifetime.ident == Ident::empty()) - } - pub fn descr(&self) -> &'static str { match self { GenericArg::Lifetime(_) => "lifetime", @@ -368,11 +356,6 @@ impl<'hir> GenericArgs<'hir> { panic!("GenericArgs::inputs: not a `Fn(T) -> U`"); } - #[inline] - pub fn has_type_params(&self) -> bool { - self.args.iter().any(|arg| matches!(arg, GenericArg::Type(_))) - } - pub fn has_err(&self) -> bool { self.args.iter().any(|arg| match arg { GenericArg::Type(ty) => matches!(ty.kind, TyKind::Err(_)), @@ -383,11 +366,6 @@ impl<'hir> GenericArgs<'hir> { }) } - #[inline] - pub fn num_type_params(&self) -> usize { - self.args.iter().filter(|arg| matches!(arg, GenericArg::Type(_))).count() - } - #[inline] pub fn num_lifetime_params(&self) -> usize { self.args.iter().filter(|arg| matches!(arg, GenericArg::Lifetime(_))).count() @@ -589,14 +567,6 @@ impl<'hir> Generics<'hir> { self.params.iter().find(|¶m| name == param.name.ident().name) } - pub fn spans(&self) -> MultiSpan { - if self.params.is_empty() { - self.span.into() - } else { - self.params.iter().map(|p| p.span).collect::>().into() - } - } - /// If there are generic parameters, return where to introduce a new one. pub fn span_for_lifetime_suggestion(&self) -> Option { if let Some(first) = self.params.first() @@ -679,7 +649,7 @@ impl<'hir> Generics<'hir> { ) } - pub fn span_for_predicate_removal(&self, pos: usize) -> Span { + fn span_for_predicate_removal(&self, pos: usize) -> Span { let predicate = &self.predicates[pos]; let span = predicate.span(); @@ -812,7 +782,7 @@ pub struct WhereRegionPredicate<'hir> { impl<'hir> WhereRegionPredicate<'hir> { /// Returns `true` if `param_def_id` matches the `lifetime` of this predicate. - pub fn is_param_bound(&self, param_def_id: LocalDefId) -> bool { + fn is_param_bound(&self, param_def_id: LocalDefId) -> bool { self.lifetime.res == LifetimeName::Param(param_def_id) } } @@ -869,7 +839,7 @@ pub struct OwnerNodes<'tcx> { } impl<'tcx> OwnerNodes<'tcx> { - pub fn node(&self) -> OwnerNode<'tcx> { + fn node(&self) -> OwnerNode<'tcx> { use rustc_index::Idx; let node = self.nodes[ItemLocalId::new(0)].as_ref().unwrap().node; let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode. @@ -1272,10 +1242,6 @@ impl BinOpKind { matches!(self, BinOpKind::And | BinOpKind::Or) } - pub fn is_shift(self) -> bool { - matches!(self, BinOpKind::Shl | BinOpKind::Shr) - } - pub fn is_comparison(self) -> bool { match self { BinOpKind::Eq @@ -1516,8 +1482,7 @@ impl<'hir> Body<'hir> { } /// The type of source expression that caused this coroutine to be created. -#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash)] -#[derive(HashStable_Generic, Encodable, Decodable)] +#[derive(Clone, PartialEq, Eq, Debug, Copy, Hash, HashStable_Generic, Encodable, Decodable)] pub enum CoroutineKind { /// An explicit `async` block or the body of an async function. Async(CoroutineSource), @@ -1558,8 +1523,7 @@ impl fmt::Display for CoroutineKind { /// /// This helps error messages but is also used to drive coercions in /// type-checking (see #60424). -#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)] -#[derive(HashStable_Generic, Encodable, Decodable)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy, HashStable_Generic, Encodable, Decodable)] pub enum CoroutineSource { /// An explicit `async`/`gen` block written by the user. Block, @@ -2117,16 +2081,6 @@ impl<'hir> QPath<'hir> { QPath::LangItem(_, span, _) => span, } } - - /// Returns the span of the last segment of this `QPath`. For example, `method` in - /// `<() as Trait>::method`. - pub fn last_segment_span(&self) -> Span { - match *self { - QPath::Resolved(_, path) => path.segments.last().unwrap().ident.span, - QPath::TypeRelative(_, segment) => segment.ident.span, - QPath::LangItem(_, span, _) => span, - } - } } /// Hints at the original code for a let statement. @@ -2153,8 +2107,7 @@ pub enum LocalSource { } /// Hints at the original code for a `match _ { .. }`. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[derive(HashStable_Generic, Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic, Encodable, Decodable)] pub enum MatchSource { /// A `match _ { .. }`. Normal, @@ -2314,6 +2267,35 @@ pub struct TraitItem<'hir> { pub defaultness: Defaultness, } +macro_rules! expect_methods_self_kind { + ( $( $name:ident, $ret_ty:ty, $pat:pat, $ret_val:expr; )* ) => { + $( + #[track_caller] + pub fn $name(&self) -> $ret_ty { + let $pat = &self.kind else { expect_failed(stringify!($ident), self) }; + $ret_val + } + )* + } +} + +macro_rules! expect_methods_self { + ( $( $name:ident, $ret_ty:ty, $pat:pat, $ret_val:expr; )* ) => { + $( + #[track_caller] + pub fn $name(&self) -> $ret_ty { + let $pat = self else { expect_failed(stringify!($ident), self) }; + $ret_val + } + )* + } +} + +#[track_caller] +fn expect_failed(ident: &'static str, found: T) -> ! { + panic!("{ident}: found {found:?}") +} + impl<'hir> TraitItem<'hir> { #[inline] pub fn hir_id(&self) -> HirId { @@ -2325,30 +2307,15 @@ impl<'hir> TraitItem<'hir> { TraitItemId { owner_id: self.owner_id } } - /// Expect an [`TraitItemKind::Const`] or panic. - #[track_caller] - pub fn expect_const(&self) -> (&'hir Ty<'hir>, Option) { - let TraitItemKind::Const(ty, body) = self.kind else { self.expect_failed("a constant") }; - (ty, body) - } + expect_methods_self_kind! { + expect_const, (&'hir Ty<'hir>, Option), + TraitItemKind::Const(ty, body), (ty, *body); - /// Expect an [`TraitItemKind::Fn`] or panic. - #[track_caller] - pub fn expect_fn(&self) -> (&FnSig<'hir>, &TraitFn<'hir>) { - let TraitItemKind::Fn(ty, trfn) = &self.kind else { self.expect_failed("a function") }; - (ty, trfn) - } + expect_fn, (&FnSig<'hir>, &TraitFn<'hir>), + TraitItemKind::Fn(ty, trfn), (ty, trfn); - /// Expect an [`TraitItemKind::Type`] or panic. - #[track_caller] - pub fn expect_type(&self) -> (GenericBounds<'hir>, Option<&'hir Ty<'hir>>) { - let TraitItemKind::Type(bounds, ty) = self.kind else { self.expect_failed("a type") }; - (bounds, ty) - } - - #[track_caller] - fn expect_failed(&self, expected: &'static str) -> ! { - panic!("expected {expected} item, found {self:?}") + expect_type, (GenericBounds<'hir>, Option<&'hir Ty<'hir>>), + TraitItemKind::Type(bounds, ty), (bounds, *ty); } } @@ -2413,30 +2380,10 @@ impl<'hir> ImplItem<'hir> { ImplItemId { owner_id: self.owner_id } } - /// Expect an [`ImplItemKind::Const`] or panic. - #[track_caller] - pub fn expect_const(&self) -> (&'hir Ty<'hir>, BodyId) { - let ImplItemKind::Const(ty, body) = self.kind else { self.expect_failed("a constant") }; - (ty, body) - } - - /// Expect an [`ImplItemKind::Fn`] or panic. - #[track_caller] - pub fn expect_fn(&self) -> (&FnSig<'hir>, BodyId) { - let ImplItemKind::Fn(ty, body) = &self.kind else { self.expect_failed("a function") }; - (ty, *body) - } - - /// Expect an [`ImplItemKind::Type`] or panic. - #[track_caller] - pub fn expect_type(&self) -> &'hir Ty<'hir> { - let ImplItemKind::Type(ty) = self.kind else { self.expect_failed("a type") }; - ty - } - - #[track_caller] - fn expect_failed(&self, expected: &'static str) -> ! { - panic!("expected {expected} item, found {self:?}") + expect_methods_self_kind! { + expect_const, (&'hir Ty<'hir>, BodyId), ImplItemKind::Const(ty, body), (ty, *body); + expect_fn, (&FnSig<'hir>, BodyId), ImplItemKind::Fn(ty, body), (ty, *body); + expect_type, &'hir Ty<'hir>, ImplItemKind::Type(ty), ty; } } @@ -2579,8 +2526,7 @@ impl<'hir> Ty<'hir> { } /// Not represented directly in the AST; referred to by name through a `ty_path`. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum PrimTy { Int(IntTy), Uint(UintTy), @@ -2860,8 +2806,7 @@ impl ImplicitSelfKind { } } -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)] -#[derive(HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum IsAsync { Async(Span), NotAsync, @@ -3124,134 +3069,51 @@ impl<'hir> Item<'hir> { ItemId { owner_id: self.owner_id } } - /// Expect an [`ItemKind::ExternCrate`] or panic. - #[track_caller] - pub fn expect_extern_crate(&self) -> Option { - let ItemKind::ExternCrate(s) = self.kind else { self.expect_failed("an extern crate") }; - s - } + expect_methods_self_kind! { + expect_extern_crate, Option, ItemKind::ExternCrate(s), *s; - /// Expect an [`ItemKind::Use`] or panic. - #[track_caller] - pub fn expect_use(&self) -> (&'hir UsePath<'hir>, UseKind) { - let ItemKind::Use(p, uk) = self.kind else { self.expect_failed("a use") }; - (p, uk) - } + expect_use, (&'hir UsePath<'hir>, UseKind), ItemKind::Use(p, uk), (p, *uk); - /// Expect an [`ItemKind::Static`] or panic. - #[track_caller] - pub fn expect_static(&self) -> (&'hir Ty<'hir>, Mutability, BodyId) { - let ItemKind::Static(ty, mutbl, body) = self.kind else { self.expect_failed("a static") }; - (ty, mutbl, body) - } - /// Expect an [`ItemKind::Const`] or panic. - #[track_caller] - pub fn expect_const(&self) -> (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId) { - let ItemKind::Const(ty, gen, body) = self.kind else { self.expect_failed("a constant") }; - (ty, gen, body) - } - /// Expect an [`ItemKind::Fn`] or panic. - #[track_caller] - pub fn expect_fn(&self) -> (&FnSig<'hir>, &'hir Generics<'hir>, BodyId) { - let ItemKind::Fn(sig, gen, body) = &self.kind else { self.expect_failed("a function") }; - (sig, gen, *body) - } + expect_static, (&'hir Ty<'hir>, Mutability, BodyId), + ItemKind::Static(ty, mutbl, body), (ty, *mutbl, *body); - /// Expect an [`ItemKind::Macro`] or panic. - #[track_caller] - pub fn expect_macro(&self) -> (&ast::MacroDef, MacroKind) { - let ItemKind::Macro(def, mk) = &self.kind else { self.expect_failed("a macro") }; - (def, *mk) - } + expect_const, (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), + ItemKind::Const(ty, gen, body), (ty, gen, *body); - /// Expect an [`ItemKind::Mod`] or panic. - #[track_caller] - pub fn expect_mod(&self) -> &'hir Mod<'hir> { - let ItemKind::Mod(m) = self.kind else { self.expect_failed("a module") }; - m - } + expect_fn, (&FnSig<'hir>, &'hir Generics<'hir>, BodyId), + ItemKind::Fn(sig, gen, body), (sig, gen, *body); - /// Expect an [`ItemKind::ForeignMod`] or panic. - #[track_caller] - pub fn expect_foreign_mod(&self) -> (Abi, &'hir [ForeignItemRef]) { - let ItemKind::ForeignMod { abi, items } = self.kind else { - self.expect_failed("a foreign module") - }; - (abi, items) - } + expect_macro, (&ast::MacroDef, MacroKind), ItemKind::Macro(def, mk), (def, *mk); - /// Expect an [`ItemKind::GlobalAsm`] or panic. - #[track_caller] - pub fn expect_global_asm(&self) -> &'hir InlineAsm<'hir> { - let ItemKind::GlobalAsm(asm) = self.kind else { self.expect_failed("a global asm") }; - asm - } + expect_mod, &'hir Mod<'hir>, ItemKind::Mod(m), m; - /// Expect an [`ItemKind::TyAlias`] or panic. - #[track_caller] - pub fn expect_ty_alias(&self) -> (&'hir Ty<'hir>, &'hir Generics<'hir>) { - let ItemKind::TyAlias(ty, gen) = self.kind else { self.expect_failed("a type alias") }; - (ty, gen) - } + expect_foreign_mod, (Abi, &'hir [ForeignItemRef]), + ItemKind::ForeignMod { abi, items }, (*abi, items); - /// Expect an [`ItemKind::OpaqueTy`] or panic. - #[track_caller] - pub fn expect_opaque_ty(&self) -> &OpaqueTy<'hir> { - let ItemKind::OpaqueTy(ty) = &self.kind else { self.expect_failed("an opaque type") }; - ty - } + expect_global_asm, &'hir InlineAsm<'hir>, ItemKind::GlobalAsm(asm), asm; - /// Expect an [`ItemKind::Enum`] or panic. - #[track_caller] - pub fn expect_enum(&self) -> (&EnumDef<'hir>, &'hir Generics<'hir>) { - let ItemKind::Enum(def, gen) = &self.kind else { self.expect_failed("an enum") }; - (def, gen) - } + expect_ty_alias, (&'hir Ty<'hir>, &'hir Generics<'hir>), + ItemKind::TyAlias(ty, gen), (ty, gen); - /// Expect an [`ItemKind::Struct`] or panic. - #[track_caller] - pub fn expect_struct(&self) -> (&VariantData<'hir>, &'hir Generics<'hir>) { - let ItemKind::Struct(data, gen) = &self.kind else { self.expect_failed("a struct") }; - (data, gen) - } + expect_opaque_ty, &OpaqueTy<'hir>, ItemKind::OpaqueTy(ty), ty; - /// Expect an [`ItemKind::Union`] or panic. - #[track_caller] - pub fn expect_union(&self) -> (&VariantData<'hir>, &'hir Generics<'hir>) { - let ItemKind::Union(data, gen) = &self.kind else { self.expect_failed("a union") }; - (data, gen) - } + expect_enum, (&EnumDef<'hir>, &'hir Generics<'hir>), ItemKind::Enum(def, gen), (def, gen); - /// Expect an [`ItemKind::Trait`] or panic. - #[track_caller] - pub fn expect_trait( - self, - ) -> (IsAuto, Unsafety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]) { - let ItemKind::Trait(is_auto, unsafety, gen, bounds, items) = self.kind else { - self.expect_failed("a trait") - }; - (is_auto, unsafety, gen, bounds, items) - } + expect_struct, (&VariantData<'hir>, &'hir Generics<'hir>), + ItemKind::Struct(data, gen), (data, gen); - /// Expect an [`ItemKind::TraitAlias`] or panic. - #[track_caller] - pub fn expect_trait_alias(&self) -> (&'hir Generics<'hir>, GenericBounds<'hir>) { - let ItemKind::TraitAlias(gen, bounds) = self.kind else { - self.expect_failed("a trait alias") - }; - (gen, bounds) - } + expect_union, (&VariantData<'hir>, &'hir Generics<'hir>), + ItemKind::Union(data, gen), (data, gen); - /// Expect an [`ItemKind::Impl`] or panic. - #[track_caller] - pub fn expect_impl(&self) -> &'hir Impl<'hir> { - let ItemKind::Impl(imp) = self.kind else { self.expect_failed("an impl") }; - imp - } + expect_trait, + (IsAuto, Unsafety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]), + ItemKind::Trait(is_auto, unsafety, gen, bounds, items), + (*is_auto, *unsafety, gen, bounds, items); - #[track_caller] - fn expect_failed(&self, expected: &'static str) -> ! { - panic!("expected {expected} item, found {self:?}") + expect_trait_alias, (&'hir Generics<'hir>, GenericBounds<'hir>), + ItemKind::TraitAlias(gen, bounds), (gen, bounds); + + expect_impl, &'hir Impl<'hir>, ItemKind::Impl(imp), imp; } } @@ -3280,8 +3142,7 @@ impl fmt::Display for Unsafety { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, HashStable_Generic)] pub enum Constness { Const, NotConst, @@ -3624,32 +3485,11 @@ impl<'hir> OwnerNode<'hir> { } } - pub fn expect_item(self) -> &'hir Item<'hir> { - match self { - OwnerNode::Item(n) => n, - _ => panic!(), - } - } - - pub fn expect_foreign_item(self) -> &'hir ForeignItem<'hir> { - match self { - OwnerNode::ForeignItem(n) => n, - _ => panic!(), - } - } - - pub fn expect_impl_item(self) -> &'hir ImplItem<'hir> { - match self { - OwnerNode::ImplItem(n) => n, - _ => panic!(), - } - } - - pub fn expect_trait_item(self) -> &'hir TraitItem<'hir> { - match self { - OwnerNode::TraitItem(n) => n, - _ => panic!(), - } + expect_methods_self! { + expect_item, &'hir Item<'hir>, OwnerNode::Item(n), n; + expect_foreign_item, &'hir ForeignItem<'hir>, OwnerNode::ForeignItem(n), n; + expect_impl_item, &'hir ImplItem<'hir>, OwnerNode::ImplItem(n), n; + expect_trait_item, &'hir TraitItem<'hir>, OwnerNode::TraitItem(n), n; } } @@ -3902,196 +3742,33 @@ impl<'hir> Node<'hir> { } } - /// Get the fields for the tuple-constructor, - /// if this node is a tuple constructor, otherwise None - pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> { - if let Node::Ctor(&VariantData::Tuple(fields, _, _)) = self { Some(fields) } else { None } - } - - /// Expect a [`Node::Param`] or panic. - #[track_caller] - pub fn expect_param(self) -> &'hir Param<'hir> { - let Node::Param(this) = self else { self.expect_failed("a parameter") }; - this - } - - /// Expect a [`Node::Item`] or panic. - #[track_caller] - pub fn expect_item(self) -> &'hir Item<'hir> { - let Node::Item(this) = self else { self.expect_failed("a item") }; - this - } - - /// Expect a [`Node::ForeignItem`] or panic. - #[track_caller] - pub fn expect_foreign_item(self) -> &'hir ForeignItem<'hir> { - let Node::ForeignItem(this) = self else { self.expect_failed("a foreign item") }; - this - } - - /// Expect a [`Node::TraitItem`] or panic. - #[track_caller] - pub fn expect_trait_item(self) -> &'hir TraitItem<'hir> { - let Node::TraitItem(this) = self else { self.expect_failed("a trait item") }; - this - } - - /// Expect a [`Node::ImplItem`] or panic. - #[track_caller] - pub fn expect_impl_item(self) -> &'hir ImplItem<'hir> { - let Node::ImplItem(this) = self else { self.expect_failed("an implementation item") }; - this - } - - /// Expect a [`Node::Variant`] or panic. - #[track_caller] - pub fn expect_variant(self) -> &'hir Variant<'hir> { - let Node::Variant(this) = self else { self.expect_failed("a variant") }; - this - } - - /// Expect a [`Node::Field`] or panic. - #[track_caller] - pub fn expect_field(self) -> &'hir FieldDef<'hir> { - let Node::Field(this) = self else { self.expect_failed("a field definition") }; - this - } - - /// Expect a [`Node::AnonConst`] or panic. - #[track_caller] - pub fn expect_anon_const(self) -> &'hir AnonConst { - let Node::AnonConst(this) = self else { self.expect_failed("an anonymous constant") }; - this - } - - /// Expect a [`Node::ConstBlock`] or panic. - #[track_caller] - pub fn expect_inline_const(self) -> &'hir ConstBlock { - let Node::ConstBlock(this) = self else { self.expect_failed("an inline constant") }; - this - } - - /// Expect a [`Node::Expr`] or panic. - #[track_caller] - pub fn expect_expr(self) -> &'hir Expr<'hir> { - let Node::Expr(this) = self else { self.expect_failed("an expression") }; - this - } - /// Expect a [`Node::ExprField`] or panic. - #[track_caller] - pub fn expect_expr_field(self) -> &'hir ExprField<'hir> { - let Node::ExprField(this) = self else { self.expect_failed("an expression field") }; - this - } - - /// Expect a [`Node::Stmt`] or panic. - #[track_caller] - pub fn expect_stmt(self) -> &'hir Stmt<'hir> { - let Node::Stmt(this) = self else { self.expect_failed("a statement") }; - this - } - - /// Expect a [`Node::PathSegment`] or panic. - #[track_caller] - pub fn expect_path_segment(self) -> &'hir PathSegment<'hir> { - let Node::PathSegment(this) = self else { self.expect_failed("a path segment") }; - this - } - - /// Expect a [`Node::Ty`] or panic. - #[track_caller] - pub fn expect_ty(self) -> &'hir Ty<'hir> { - let Node::Ty(this) = self else { self.expect_failed("a type") }; - this - } - - /// Expect a [`Node::TypeBinding`] or panic. - #[track_caller] - pub fn expect_type_binding(self) -> &'hir TypeBinding<'hir> { - let Node::TypeBinding(this) = self else { self.expect_failed("a type binding") }; - this - } - - /// Expect a [`Node::TraitRef`] or panic. - #[track_caller] - pub fn expect_trait_ref(self) -> &'hir TraitRef<'hir> { - let Node::TraitRef(this) = self else { self.expect_failed("a trait reference") }; - this - } - - /// Expect a [`Node::Pat`] or panic. - #[track_caller] - pub fn expect_pat(self) -> &'hir Pat<'hir> { - let Node::Pat(this) = self else { self.expect_failed("a pattern") }; - this - } - - /// Expect a [`Node::PatField`] or panic. - #[track_caller] - pub fn expect_pat_field(self) -> &'hir PatField<'hir> { - let Node::PatField(this) = self else { self.expect_failed("a pattern field") }; - this - } - - /// Expect a [`Node::Arm`] or panic. - #[track_caller] - pub fn expect_arm(self) -> &'hir Arm<'hir> { - let Node::Arm(this) = self else { self.expect_failed("an arm") }; - this - } - - /// Expect a [`Node::Block`] or panic. - #[track_caller] - pub fn expect_block(self) -> &'hir Block<'hir> { - let Node::Block(this) = self else { self.expect_failed("a block") }; - this - } - - /// Expect a [`Node::Local`] or panic. - #[track_caller] - pub fn expect_local(self) -> &'hir Local<'hir> { - let Node::Local(this) = self else { self.expect_failed("a local") }; - this - } - - /// Expect a [`Node::Ctor`] or panic. - #[track_caller] - pub fn expect_ctor(self) -> &'hir VariantData<'hir> { - let Node::Ctor(this) = self else { self.expect_failed("a constructor") }; - this - } - - /// Expect a [`Node::Lifetime`] or panic. - #[track_caller] - pub fn expect_lifetime(self) -> &'hir Lifetime { - let Node::Lifetime(this) = self else { self.expect_failed("a lifetime") }; - this - } - - /// Expect a [`Node::GenericParam`] or panic. - #[track_caller] - pub fn expect_generic_param(self) -> &'hir GenericParam<'hir> { - let Node::GenericParam(this) = self else { self.expect_failed("a generic parameter") }; - this - } - - /// Expect a [`Node::Crate`] or panic. - #[track_caller] - pub fn expect_crate(self) -> &'hir Mod<'hir> { - let Node::Crate(this) = self else { self.expect_failed("a crate") }; - this - } - - /// Expect a [`Node::Infer`] or panic. - #[track_caller] - pub fn expect_infer(self) -> &'hir InferArg { - let Node::Infer(this) = self else { self.expect_failed("an infer") }; - this - } - - #[track_caller] - fn expect_failed(&self, expected: &'static str) -> ! { - panic!("expected {expected} node, found {self:?}") + expect_methods_self! { + expect_param, &'hir Param<'hir>, Node::Param(n), n; + expect_item, &'hir Item<'hir>, Node::Item(n), n; + expect_foreign_item, &'hir ForeignItem<'hir>, Node::ForeignItem(n), n; + expect_trait_item, &'hir TraitItem<'hir>, Node::TraitItem(n), n; + expect_impl_item, &'hir ImplItem<'hir>, Node::ImplItem(n), n; + expect_variant, &'hir Variant<'hir>, Node::Variant(n), n; + expect_field, &'hir FieldDef<'hir>, Node::Field(n), n; + expect_anon_const, &'hir AnonConst, Node::AnonConst(n), n; + expect_inline_const, &'hir ConstBlock, Node::ConstBlock(n), n; + expect_expr, &'hir Expr<'hir>, Node::Expr(n), n; + expect_expr_field, &'hir ExprField<'hir>, Node::ExprField(n), n; + expect_stmt, &'hir Stmt<'hir>, Node::Stmt(n), n; + expect_path_segment, &'hir PathSegment<'hir>, Node::PathSegment(n), n; + expect_ty, &'hir Ty<'hir>, Node::Ty(n), n; + expect_type_binding, &'hir TypeBinding<'hir>, Node::TypeBinding(n), n; + expect_trait_ref, &'hir TraitRef<'hir>, Node::TraitRef(n), n; + expect_pat, &'hir Pat<'hir>, Node::Pat(n), n; + expect_pat_field, &'hir PatField<'hir>, Node::PatField(n), n; + expect_arm, &'hir Arm<'hir>, Node::Arm(n), n; + expect_block, &'hir Block<'hir>, Node::Block(n), n; + expect_local, &'hir Local<'hir>, Node::Local(n), n; + expect_ctor, &'hir VariantData<'hir>, Node::Ctor(n), n; + expect_lifetime, &'hir Lifetime, Node::Lifetime(n), n; + expect_generic_param, &'hir GenericParam<'hir>, Node::GenericParam(n), n; + expect_crate, &'hir Mod<'hir>, Node::Crate(n), n; + expect_infer, &'hir InferArg, Node::Infer(n), n; } } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 34c615779366..7b741e8882de 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -3,8 +3,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, use rustc_span::{def_id::DefPathHash, HashStableContext}; use std::fmt::{self, Debug}; -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[derive(Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct OwnerId { pub def_id: LocalDefId, } @@ -73,8 +72,7 @@ impl ToStableHashKey for OwnerId { /// the `local_id` part of the `HirId` changing, which is a very useful property in /// incremental compilation where we have to persist things through changes to /// the code base. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] #[rustc_pass_by_value] pub struct HirId { pub owner: OwnerId, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 1d1a1ee88627..60f1449c177c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -233,7 +233,7 @@ language_item_table! { PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); - PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0); + PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0); PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 094d5b1e77cf..87de3c0870bc 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -4,7 +4,6 @@ #![feature(associated_type_defaults)] #![feature(closure_track_caller)] -#![feature(const_btree_len)] #![feature(let_chains)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 7ced37e5694e..8f4b83966dff 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -4,7 +4,7 @@ use crate::errors::{ ParenthesizedFnTraitExpansion, }; use crate::traits::error_reporting::report_object_safety_error; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -16,8 +16,6 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_trait_selection::traits::object_safety_violations_for_assoc_item; -use std::collections::BTreeSet; - impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// On missing type parameters, emit an E0393 error and provide a structured suggestion using /// the type parameter's name as a placeholder. @@ -504,7 +502,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// emit a generic note suggesting using a `where` clause to constraint instead. pub(crate) fn complain_about_missing_associated_types( &self, - associated_types: FxHashMap>, + associated_types: FxIndexMap>, potential_assoc_types: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], ) { @@ -514,13 +512,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); // FIXME: Marked `mut` so that we can replace the spans further below with a more // appropriate one, but this should be handled earlier in the span assignment. - let mut associated_types: FxHashMap> = associated_types + let mut associated_types: FxIndexMap> = associated_types .into_iter() .map(|(span, def_ids)| { (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) }) .collect(); - let mut names: FxHashMap> = Default::default(); + let mut names: FxIndexMap> = Default::default(); let mut names_len = 0; // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 8126b4511813..3542fc7cd332 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -945,7 +945,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Applicability::MachineApplicable, ); } else { - match (types, traits) { + let mut types = types.to_vec(); + types.sort(); + let mut traits = traits.to_vec(); + traits.sort(); + match (&types[..], &traits[..]) { ([], []) => { err.span_suggestion_verbose( span, diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index 00ff3f836adf..78c5809f8b4f 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -1,7 +1,7 @@ use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use crate::bounds::Bounds; use crate::errors::TraitObjectDeclaredWithNoTraits; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -14,7 +14,6 @@ use rustc_trait_selection::traits::error_reporting::report_object_safety_error; use rustc_trait_selection::traits::{self, astconv_object_safety_violations}; use smallvec::{smallvec, SmallVec}; -use std::collections::BTreeSet; use super::AstConv; @@ -148,8 +147,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - // Use a `BTreeSet` to keep output in a more consistent order. - let mut associated_types: FxHashMap> = FxHashMap::default(); + let mut associated_types: FxIndexMap> = FxIndexMap::default(); let regular_traits_refs_spans = trait_bounds .into_iter() diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index eb009b9368f9..7ea21b24fc82 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/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 1e75b8631655..7b5b049d254e 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -440,7 +440,7 @@ 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_param(self.tcx, ty::EarlyParamRegion { diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index d5fb4340e1c8..55c01d2084b8 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> 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/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 218f658b061c..8077acea52e4 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -80,6 +80,10 @@ pub(crate) fn insert_outlives_predicate<'tcx>( .or_insert(span); } + Component::Placeholder(_) => { + span_bug!(span, "Should not deduce placeholder outlives component"); + } + Component::Alias(alias_ty) => { // This would either arise from something like: // diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 6b6d1574b2bf..173186e6d9f1 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 77d6183f862f..386e4b4642a8 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -31,6 +31,7 @@ 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) @@ -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 eaf5e938d3cf..4b7de36e7c7b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -626,15 +626,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let coerce_to = match opt_coerce_to { - Some(c) => c, - None => { - // If the loop context is not a `loop { }`, then break with - // a value is illegal, and `opt_coerce_to` will be `None`. - // Return error in that case (#114529). - return Ty::new_misc_error(tcx); - } - }; + // If the loop context is not a `loop { }`, then break with + // a value is illegal, and `opt_coerce_to` will be `None`. + // Set expectation to error in that case and set tainted + // by error (#114529) + let coerce_to = opt_coerce_to.unwrap_or_else(|| { + let guar = tcx.sess.delay_span_bug( + expr.span, + "illegal break with value found but no error reported", + ); + self.set_tainted_by_errors(guar); + Ty::new_error(tcx, guar) + }); // Recurse without `enclosing_breakables` borrowed. e_ty = self.check_expr_with_hint(e, coerce_to); @@ -1897,7 +1900,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, private_fields, ast_fields); + self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields); } else { self.report_missing_fields( adt_ty, @@ -2056,6 +2059,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, adt_ty: Ty<'tcx>, span: Span, + expr_span: Span, private_fields: Vec<&ty::FieldDef>, used_fields: &'tcx [hir::ExprField<'tcx>], ) { @@ -2100,6 +2104,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { were = pluralize!("was", remaining_private_fields_len), )); } + + if let ty::Adt(def, _) = adt_ty.kind() { + let def_id = def.did(); + let mut items = self + .tcx + .inherent_impls(def_id) + .iter() + .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) + // Only assoc fn with no receivers. + .filter(|item| { + matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter + }) + .filter_map(|item| { + // Only assoc fns that return `Self` + let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder(); + let ret_ty = fn_sig.output(); + let ret_ty = + self.tcx.normalize_erasing_late_bound_regions(self.param_env, ret_ty); + if !self.can_eq(self.param_env, ret_ty, adt_ty) { + return None; + } + let input_len = fn_sig.inputs().skip_binder().len(); + let order = !item.name.as_str().starts_with("new"); + Some((order, item.name, input_len)) + }) + .collect::>(); + items.sort_by_key(|(order, _, _)| *order); + let suggestion = |name, args| { + format!( + "::{name}({})", + std::iter::repeat("_").take(args).collect::>().join(", ") + ) + }; + match &items[..] { + [] => {} + [(_, name, args)] => { + err.span_suggestion_verbose( + span.shrink_to_hi().with_hi(expr_span.hi()), + format!("you might have meant to use the `{name}` associated function"), + suggestion(name, *args), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_suggestions( + span.shrink_to_hi().with_hi(expr_span.hi()), + "you might have meant to use an associated function to build this type", + items + .iter() + .map(|(_, name, args)| suggestion(name, *args)) + .collect::>(), + Applicability::MaybeIncorrect, + ); + } + } + if let Some(default_trait) = self.tcx.get_diagnostic_item(sym::Default) + && self + .infcx + .type_implements_trait(default_trait, [adt_ty], self.param_env) + .may_apply() + { + err.multipart_suggestion( + "consider using the `Default` trait", + vec![ + (span.shrink_to_lo(), "<".to_string()), + ( + span.shrink_to_hi().with_hi(expr_span.hi()), + " as std::default::Default>::default()".to_string(), + ), + ], + Applicability::MaybeIncorrect, + ); + } + } + err.emit(); } @@ -2191,7 +2270,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 +2500,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 +2548,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 +2710,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 +2718,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::()) && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) @@ -2691,6 +2741,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 +2760,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 +2768,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() { - let candidate_fields: Vec<_> = fields + (ty, "unwrap().") + } else { + (expr_t, "") + }; + for (found_fields, args) in + self.get_field_candidates_considering_privacy(span, ty, mod_id, id) + { + let field_names = found_fields.iter().map(|field| field.name).collect::>(); + let mut candidate_fields: Vec<_> = found_fields + .into_iter() .filter_map(|candidate_field| { self.check_for_nested_field_satisfying( span, @@ -2729,17 +2792,18 @@ 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::>() - .join(".") + .map(|id| format!("{}.", id.name.to_ident_string())) + .collect::() }) .collect::>(); + candidate_fields.sort(); let len = candidate_fields.len(); if len > 0 { @@ -2750,9 +2814,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 +2860,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, base_ty: Ty<'tcx>, mod_id: DefId, - ) -> Option<(impl Iterator + '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`. - 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`. + 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::>(), + *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 +2905,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subst: GenericArgsRef<'tcx>, mut field_path: Vec, mod_id: DefId, + hir_id: HirId, ) -> Option> { debug!( "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}", @@ -2835,20 +2921,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); + } } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 2e0ab1560f4b..a37a595e4a8d 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") diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a51fa8354c9e..d2a72f8f2338 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(&self, value: ty::Binder<'tcx, T>) -> T + fn instantiate_bound_regions_with_erased(&self, value: ty::Binder<'tcx, T>) -> T where T: TypeFoldable>, { - 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 3624b86b5756..db434636f595 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1426,6 +1426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !suggs.is_empty() && let Some(span) = sugg_span { + suggs.sort(); err.span_suggestions( span.with_hi(item_name.span.lo()), "use fully-qualified syntax to disambiguate", @@ -1454,7 +1455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter_map(|item| { // Only assoc fns that return `Self`, `Option` or `Result`. 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; }; @@ -1983,69 +1984,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, return_type: Option>, ) { - 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::>() - .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 mut candidate_fields: Vec<_> = fields + .into_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::>() + .join(".") + }) + .collect(); + candidate_fields.sort(); - 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, + ); + } } } } @@ -2564,13 +2569,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.item_name(*trait_did), ) }); + let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect(); + sugg.sort(); - err.span_suggestions( - span, - msg, - path_strings.chain(glob_path_strings), - Applicability::MaybeIncorrect, - ); + err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); } fn suggest_valid_traits( diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 2dfe72726734..2462ac936326 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> 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> 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> 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_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 856f8a67dd61..3a4c813b5d41 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } +rustc_index_macros = { path = "../rustc_index_macros", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } smallvec = "1.8.1" @@ -14,5 +15,5 @@ smallvec = "1.8.1" [features] # tidy-alphabetical-start default = ["nightly"] -nightly = ["rustc_serialize", "rustc_macros"] +nightly = ["rustc_serialize", "rustc_macros", "rustc_index_macros/nightly"] # tidy-alphabetical-end diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 061c55c01506..c5602392c538 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -25,8 +25,7 @@ mod vec; pub use {idx::Idx, slice::IndexSlice, vec::IndexVec}; -#[cfg(feature = "rustc_macros")] -pub use rustc_macros::newtype_index; +pub use rustc_index_macros::newtype_index; /// Type size assertion. The first argument is a type and the second argument is its expected size. /// diff --git a/compiler/rustc_index/src/vec/tests.rs b/compiler/rustc_index/src/vec/tests.rs index 7e5e41bd2d19..1959f4e07b7c 100644 --- a/compiler/rustc_index/src/vec/tests.rs +++ b/compiler/rustc_index/src/vec/tests.rs @@ -1,7 +1,7 @@ // Allows the macro invocation below to work use crate as rustc_index; -rustc_macros::newtype_index! { +crate::newtype_index! { #[max = 0xFFFF_FFFA] struct MyIdx {} } diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml new file mode 100644 index 000000000000..c4ca29db3c2d --- /dev/null +++ b/compiler/rustc_index_macros/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rustc_index_macros" +version = "0.0.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +synstructure = "0.13.0" +syn = { version = "2.0.9", features = ["full"] } +proc-macro2 = "1" +quote = "1" + +[features] +default = ["nightly"] +nightly = [] \ No newline at end of file diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs new file mode 100644 index 000000000000..f6a6175374ac --- /dev/null +++ b/compiler/rustc_index_macros/src/lib.rs @@ -0,0 +1,30 @@ +#![cfg_attr(feature = "nightly", feature(allow_internal_unstable))] +#![cfg_attr(feature = "nightly", allow(internal_features))] + +use proc_macro::TokenStream; + +mod newtype; + +/// Creates a struct type `S` that can be used as an index with +/// `IndexVec` and so on. +/// +/// There are two ways of interacting with these indices: +/// +/// - The `From` impls are the preferred way. So you can do +/// `S::from(v)` with a `usize` or `u32`. And you can convert back +/// to an integer with `u32::from(s)`. +/// +/// - Alternatively, you can use the methods `S::new(v)` and `s.index()` +/// to create/return a value. +/// +/// Internally, the index uses a u32, so the index must not exceed +/// `u32::MAX`. You can also customize things like the `Debug` impl, +/// what traits are derived, and so forth via the macro. +#[proc_macro] +#[cfg_attr( + feature = "nightly", + allow_internal_unstable(step_trait, rustc_attrs, trusted_step, spec_option_partial_eq) +)] +pub fn newtype_index(input: TokenStream) -> TokenStream { + newtype::newtype(input) +} diff --git a/compiler/rustc_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs similarity index 94% rename from compiler/rustc_macros/src/newtype.rs rename to compiler/rustc_index_macros/src/newtype.rs index 72b47de1abc2..2a974fd26281 100644 --- a/compiler/rustc_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -24,9 +24,16 @@ impl Parse for Newtype { let mut consts = Vec::new(); let mut encodable = true; let mut ord = true; + let mut gate_rustc_only = quote! {}; + let mut gate_rustc_only_cfg = quote! { all() }; attrs.retain(|attr| match attr.path().get_ident() { Some(ident) => match &*ident.to_string() { + "gate_rustc_only" => { + gate_rustc_only = quote! { #[cfg(feature = "nightly")] }; + gate_rustc_only_cfg = quote! { feature = "nightly" }; + false + } "custom_encodable" => { encodable = false; false @@ -88,11 +95,13 @@ impl Parse for Newtype { let encodable_impls = if encodable { quote! { + #gate_rustc_only impl ::rustc_serialize::Decodable for #name { fn decode(d: &mut D) -> Self { Self::from_u32(d.read_u32()) } } + #gate_rustc_only impl ::rustc_serialize::Encodable for #name { fn encode(&self, e: &mut E) { e.emit_u32(self.private); @@ -110,6 +119,7 @@ impl Parse for Newtype { let step = if ord { quote! { + #gate_rustc_only impl ::std::iter::Step for #name { #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -131,6 +141,7 @@ impl Parse for Newtype { } // Safety: The implementation of `Step` upholds all invariants. + #gate_rustc_only unsafe impl ::std::iter::TrustedStep for #name {} } } else { @@ -148,6 +159,7 @@ impl Parse for Newtype { let spec_partial_eq_impl = if let Lit::Int(max) = &max { if let Ok(max_val) = max.base10_parse::() { quote! { + #gate_rustc_only impl core::option::SpecOptionPartialEq for #name { #[inline] fn eq(l: &Option, r: &Option) -> bool { @@ -173,8 +185,8 @@ impl Parse for Newtype { Ok(Self(quote! { #(#attrs)* #[derive(Clone, Copy, PartialEq, Eq, Hash, #(#derive_paths),*)] - #[rustc_layout_scalar_valid_range_end(#max)] - #[rustc_pass_by_value] + #[cfg_attr(#gate_rustc_only_cfg, rustc_layout_scalar_valid_range_end(#max))] + #[cfg_attr(#gate_rustc_only_cfg, rustc_pass_by_value)] #vis struct #name { private: u32, } diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 294bd9d1d54b..3ee4710bdef6 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -75,7 +75,7 @@ impl<'a> DescriptionCtx<'a> { // 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/at.rs b/compiler/rustc_infer/src/infer/at.rs index 2797d0797611..c32c3aa6d296 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -65,8 +65,15 @@ impl<'tcx> InferCtxt<'tcx> { /// Forks the inference context, creating a new inference context with the same inference /// variables in the same state. This can be used to "branch off" many tests from the same - /// common state. Used in coherence. + /// common state. pub fn fork(&self) -> Self { + self.fork_with_intercrate(self.intercrate) + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state, except possibly changing the intercrate mode. This can be + /// used to "branch off" many tests from the same common state. Used in negative coherence. + pub fn fork_with_intercrate(&self, intercrate: bool) -> Self { Self { tcx: self.tcx, defining_use_anchor: self.defining_use_anchor, @@ -81,7 +88,7 @@ impl<'tcx> InferCtxt<'tcx> { tainted_by_errors: self.tainted_by_errors.clone(), err_count_on_creation: self.err_count_on_creation, universe: self.universe.clone(), - intercrate: self.intercrate, + intercrate, next_trait_solver: self.next_trait_solver, } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 0546f4e1afc3..d7669b1a4b55 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -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(), ); } } @@ -2351,6 +2351,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{p}`"), + GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), GenericKind::Alias(ref p) => match p.kind(self.tcx) { ty::AliasKind::Projection | ty::AliasKind::Inherent => { format!("the associated type `{p}`") diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index f867876a2e63..806ecf35330f 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -11,6 +11,7 @@ use smallvec::{smallvec, SmallVec}; pub enum Component<'tcx> { Region(ty::Region<'tcx>), Param(ty::ParamTy), + Placeholder(ty::PlaceholderType), UnresolvedInferenceVariable(ty::InferTy), // Projections like `T::Foo` are tricky because a constraint like @@ -120,6 +121,10 @@ fn compute_components<'tcx>( out.push(Component::Param(p)); } + ty::Placeholder(p) => { + out.push(Component::Placeholder(p)); + } + // For projections, we prefer to generate an obligation like // `>::Foo: 'a`, because this gives the // regionck more ways to prove that it holds. However, @@ -176,7 +181,6 @@ fn compute_components<'tcx>( ty::Tuple(..) | // ... ty::FnPtr(_) | // OutlivesFunction (*) ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) - ty::Placeholder(..) | ty::Bound(..) | ty::Error(_) => { // (*) Function pointers and trait objects are both binders. diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index f36802e12843..1bc24682b9ac 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -243,6 +243,9 @@ where Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, *param_ty); } + Component::Placeholder(placeholder_ty) => { + self.placeholder_ty_must_outlive(origin, region, *placeholder_ty); + } Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty), Component::EscapingAlias(subcomponents) => { self.components_must_outlive(origin, &subcomponents, region, category); @@ -267,10 +270,28 @@ where region: ty::Region<'tcx>, param_ty: ty::ParamTy, ) { - let verify_bound = self.verify_bound.param_bound(param_ty); + let verify_bound = self.verify_bound.param_or_placeholder_bound(param_ty.to_ty(self.tcx)); self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound); } + #[instrument(level = "debug", skip(self))] + fn placeholder_ty_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + placeholder_ty: ty::PlaceholderType, + ) { + let verify_bound = self + .verify_bound + .param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty)); + self.delegate.push_verify( + origin, + GenericKind::Placeholder(placeholder_ty), + region, + verify_bound, + ); + } + #[instrument(level = "debug", skip(self))] fn alias_ty_must_outlive( &mut self, diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 7f0a4717d88d..35a8fe6dc470 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -37,11 +37,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + pub fn param_or_placeholder_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { // Start with anything like `T: 'a` we can scrape from the // environment. If the environment contains something like // `for<'a> T: 'a`, then we know that `T` outlives everything. - let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty); + let declared_bounds_from_env = self.declared_generic_bounds_from_env(ty); debug!(?declared_bounds_from_env); let mut param_bounds = vec![]; for declared_bound in declared_bounds_from_env { @@ -51,7 +51,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { param_bounds.push(VerifyBound::OutlivedBy(region)); } else { // This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here. - debug!("found that {param_ty:?} outlives any lifetime, returning empty vector"); + debug!("found that {ty:?} outlives any lifetime, returning empty vector"); return VerifyBound::AllBounds(vec![]); } } @@ -168,7 +168,10 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { ) -> VerifyBound<'tcx> { match *component { Component::Region(lt) => VerifyBound::OutlivedBy(lt), - Component::Param(param_ty) => self.param_bound(param_ty), + Component::Param(param_ty) => self.param_or_placeholder_bound(param_ty.to_ty(self.tcx)), + Component::Placeholder(placeholder_ty) => { + self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty)) + } Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited), Component::EscapingAlias(ref components) => { self.bound_from_components(components, visited) @@ -195,9 +198,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// bounds, but all the bounds it returns can be relied upon. fn declared_generic_bounds_from_env( &self, - param_ty: ty::ParamTy, + generic_ty: Ty<'tcx>, ) -> Vec, ty::Region<'tcx>>>> { - let generic_ty = param_ty.to_ty(self.tcx); + assert!(matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_))); self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index e888340bde3f..eb00621ed7f1 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -147,6 +147,7 @@ pub struct Verify<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] pub enum GenericKind<'tcx> { Param(ty::ParamTy), + Placeholder(ty::PlaceholderType), Alias(ty::AliasTy<'tcx>), } @@ -707,6 +708,7 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), GenericKind::Alias(ref p) => write!(f, "{p:?}"), } } @@ -716,6 +718,7 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { GenericKind::Param(ref p) => write!(f, "{p}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), GenericKind::Alias(ref p) => write!(f, "{p}"), } } @@ -725,6 +728,7 @@ impl<'tcx> GenericKind<'tcx> { pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match *self { GenericKind::Param(ref p) => p.to_ty(tcx), + GenericKind::Placeholder(ref p) => Ty::new_placeholder(tcx, *p), GenericKind::Alias(ref p) => p.to_ty(tcx), } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 8cee13ce4ec7..ed3409149d09 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -365,6 +365,11 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) } + Component::Placeholder(p) => { + let ty = Ty::new_placeholder(tcx, p); + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) + } + Component::UnresolvedInferenceVariable(_) => None, Component::Alias(alias_ty) => { diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 89125c54a6af..6225424fe4ac 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -38,21 +38,17 @@ pub type Result = result::Result; /// Can be used to run `rustc_interface` queries. /// Created by passing [`Config`] to [`run_compiler`]. pub struct Compiler { - pub(crate) sess: Lrc, - codegen_backend: Lrc, - pub(crate) register_lints: Option>, + sess: Session, + codegen_backend: Box, pub(crate) override_queries: Option, } impl Compiler { - pub fn session(&self) -> &Lrc { + pub fn session(&self) -> &Session { &self.sess } - pub fn codegen_backend(&self) -> &Lrc { - &self.codegen_backend - } - pub fn register_lints(&self) -> &Option> { - &self.register_lints + pub fn codegen_backend(&self) -> &dyn CodegenBackend { + &*self.codegen_backend } pub fn build_output_filenames( &self, @@ -485,12 +481,18 @@ pub fn run_compiler(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 7d14d088e595..0baf77c4f7e1 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, -) -> 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, stable_crate_id: StableCrateId, - lint_store: Lrc, dep_graph: DepGraph, untracked: Untracked, gcx_cell: &'tcx OnceLock>, @@ -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 3a5f788e8ddb..fbf2dbcdae50 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 { - &self.compiler.sess + fn session(&self) -> &Session { + &self.compiler.session() } - fn codegen_backend(&self) -> &Lrc { + + 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) -> Result { - 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, - codegen_backend: Lrc, - - // compilation outputs dep_graph: DepGraph, - prepare_outputs: Arc, + output_filenames: Arc, // Only present when incr. comp. is enabled. crate_hash: Option, ongoing_codegen: Box, } 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_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 068f1372c0ec..cb2e373c6f90 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -128,12 +128,6 @@ lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not en lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases .suggestion = the clause will not be checked when the type alias is used, and should be removed -lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name - .help = was set with `--cfg` but isn't in the `--check-cfg` expected names - -lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}` - .help = was set with `--cfg` but isn't in the `--check-cfg` expected values - lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e3dd8faeedb3..b434659f0d5c 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -33,7 +33,6 @@ use crate::{ BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, @@ -60,7 +59,6 @@ use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; -use rustc_session::config::ExpectedValues; use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; @@ -2889,26 +2887,3 @@ impl EarlyLintPass for SpecialModuleName { } } } - -pub use rustc_session::lint::builtin::UNEXPECTED_CFGS; - -declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]); - -impl EarlyLintPass for UnexpectedCfgs { - fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - let cfg = &cx.sess().parse_sess.config; - let check_cfg = &cx.sess().parse_sess.check_config; - for &(name, value) in cfg { - match check_cfg.expecteds.get(&name) { - Some(ExpectedValues::Some(values)) if !values.contains(&value) => { - let value = value.unwrap_or(kw::Empty); - cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value }); - } - None if check_cfg.exhaustive_names => { - cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name }); - } - _ => { /* expected */ } - } - } - } -} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a5f4c5ff0459..d500c3b66d66 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 for more information"); }, BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => { - let possibilities: Vec = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect(); + let possibilities: Vec = 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>( &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>( &self, diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index d2d99bc0da87..d3e2d5c76465 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -50,7 +50,7 @@ declare_lint! { Warn, "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, reference: "issue #89460 ", }; } @@ -59,12 +59,13 @@ declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]); impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let tcx = cx.tcx; // `Deref` is being implemented for `t` if let hir::ItemKind::Impl(impl_) = item.kind && let Some(trait_) = &impl_.of_trait - && let t = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let t = tcx.type_of(item.owner_id).instantiate_identity() && let opt_did @ Some(did) = trait_.trait_def_id() - && opt_did == cx.tcx.lang_items().deref_trait() + && opt_did == tcx.lang_items().deref_trait() // `t` is `dyn t_principal` && let ty::Dynamic(data, _, ty::Dyn) = t.kind() && let Some(t_principal) = data.principal() @@ -73,9 +74,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && let ty::Dynamic(data, _, ty::Dyn) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` - && supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self)) - .any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal) + && supertraits(tcx, t_principal.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) + .any(|sup| { + tcx.erase_regions( + sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(tcx, x)), + ) == tcx.erase_regions(target_principal) + }) { + let t = tcx.erase_regions(t); let label = impl_ .items .iter() @@ -83,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { .map(|label| SupertraitAsDerefTargetLabel { label }); cx.emit_spanned_lint( DEREF_INTO_DYN_SUPERTRAIT, - cx.tcx.def_span(item.owner_id.def_id), + tcx.def_span(item.owner_id.def_id), SupertraitAsDerefTarget { t, target_principal, label }, ); } diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 86b3b4ad0ca9..31d9c0d33fee 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 6c8b60c8d741..10c4c0dc79f9 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 0d20f6232db1..ee5fa87e45db 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 { self.provider.current_specs() } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 606e18866165..0db30cd8a3d4 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -179,7 +179,6 @@ early_lint_methods!( IncompleteInternalFeatures: IncompleteInternalFeatures, RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, - UnexpectedCfgs: UnexpectedCfgs, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 756899e50a8c..829ac6903ded 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -553,21 +553,6 @@ pub enum BuiltinSpecialModuleNameUsed { Main, } -#[derive(LintDiagnostic)] -#[diag(lint_builtin_unexpected_cli_config_name)] -#[help] -pub struct BuiltinUnexpectedCliConfigName { - pub name: Symbol, -} - -#[derive(LintDiagnostic)] -#[diag(lint_builtin_unexpected_cli_config_value)] -#[help] -pub struct BuiltinUnexpectedCliConfigValue { - pub name: Symbol, - pub value: Symbol, -} - // deref_into_dyn_supertrait.rs #[derive(LintDiagnostic)] #[diag(lint_supertrait_as_deref_target)] diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index c04053d1865d..706c8c7add58 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_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 355855b8e2b3..0ae91ac28a74 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -251,6 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { /// The root of the normal must_use lint with an optional message. Def(Span, DefId, Option), Boxed(Box), + Pinned(Box), Opaque(Box), TraitObject(Box), TupleElement(Vec<(usize, Self)>), @@ -284,6 +285,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { is_ty_must_use(cx, boxed_ty, expr, span) .map(|inner| MustUsePath::Boxed(Box::new(inner))) } + ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => { + let pinned_ty = args.type_at(0); + is_ty_must_use(cx, pinned_ty, expr, span) + .map(|inner| MustUsePath::Pinned(Box::new(inner))) + } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { elaborate( @@ -425,6 +431,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { expr_is_from_block, ); } + MustUsePath::Pinned(path) => { + let descr_pre = &format!("{descr_pre}pinned "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } MustUsePath::Opaque(path) => { let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); emit_must_use_untranslated( diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index bef9f469cc65..a2243817df95 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3439,6 +3439,7 @@ declare_lint_pass! { UNCONDITIONAL_PANIC, UNCONDITIONAL_RECURSION, UNDEFINED_NAKED_FUNCTION_ABI, + UNEXPECTED_CFGS, UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, UNKNOWN_CRATE_TYPES, diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index 6d23b9ac99d2..2893937fc4a3 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -38,51 +38,80 @@ fn parse_attributes(field: &syn::Field) -> Attributes { attrs } -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 discriminant = hash_stable_discriminant(&mut s); - let body = hash_stable_body(&mut s); - - s.bound_impl( - quote!(::rustc_data_structures::stable_hasher::HashStable<__CTX>), - quote! { - #[inline] - fn hash_stable( - &self, - __hcx: &mut __CTX, - __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { - #discriminant - match *self { #body } - } - }, - ) +pub(crate) fn hash_stable_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + hash_stable_derive_with_mode(s, HashStableMode::Normal) } -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); +pub(crate) fn hash_stable_generic_derive( + s: synstructure::Structure<'_>, +) -> proc_macro2::TokenStream { + hash_stable_derive_with_mode(s, HashStableMode::Generic) +} + +pub(crate) fn hash_stable_no_context_derive( + s: synstructure::Structure<'_>, +) -> proc_macro2::TokenStream { + hash_stable_derive_with_mode(s, HashStableMode::NoContext) +} + +enum HashStableMode { + // Use the query-system aware stable hashing context. + Normal, + // Emit a generic implementation that uses a crate-local `StableHashingContext` + // trait, when the crate is upstream of `rustc_middle`. + Generic, + // Emit a hash-stable implementation that takes no context, + // and emits per-field where clauses for (almost-)perfect derives. + NoContext, +} + +fn hash_stable_derive_with_mode( + mut s: synstructure::Structure<'_>, + mode: HashStableMode, +) -> proc_macro2::TokenStream { + let generic: syn::GenericParam = match mode { + HashStableMode::Normal => parse_quote!('__ctx), + HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX), + }; + + // no_context impl is able to derive by-field, which is closer to a perfect derive. + s.add_bounds(match mode { + HashStableMode::Normal | HashStableMode::Generic => synstructure::AddBounds::Generics, + HashStableMode::NoContext => synstructure::AddBounds::Fields, + }); + + // For generic impl, add `where __CTX: HashStableContext`. + match mode { + HashStableMode::Normal => {} + HashStableMode::Generic => { + s.add_where_predicate(parse_quote! { __CTX: crate::HashStableContext }); + } + HashStableMode::NoContext => {} + } + s.add_impl_generic(generic); let discriminant = hash_stable_discriminant(&mut s); let body = hash_stable_body(&mut s); + let context: syn::Type = match mode { + HashStableMode::Normal => { + parse_quote!(::rustc_query_system::ich::StableHashingContext<'__ctx>) + } + HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX), + }; + s.bound_impl( quote!( ::rustc_data_structures::stable_hasher::HashStable< - ::rustc_query_system::ich::StableHashingContext<'__ctx>, + #context > ), quote! { #[inline] fn hash_stable( &self, - __hcx: &mut ::rustc_query_system::ich::StableHashingContext<'__ctx>, + __hcx: &mut #context, __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { #discriminant match *self { #body } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index a66666360324..f558b74be9a9 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -19,7 +19,6 @@ mod current_version; mod diagnostics; mod hash_stable; mod lift; -mod newtype; mod query; mod serialize; mod symbols; @@ -44,32 +43,18 @@ pub fn symbols(input: TokenStream) -> TokenStream { symbols::symbols(input.into()).into() } -/// Creates a struct type `S` that can be used as an index with -/// `IndexVec` and so on. -/// -/// There are two ways of interacting with these indices: -/// -/// - The `From` impls are the preferred way. So you can do -/// `S::from(v)` with a `usize` or `u32`. And you can convert back -/// to an integer with `u32::from(s)`. -/// -/// - Alternatively, you can use the methods `S::new(v)` and `s.index()` -/// to create/return a value. -/// -/// Internally, the index uses a u32, so the index must not exceed -/// `u32::MAX`. You can also customize things like the `Debug` impl, -/// what traits are derived, and so forth via the macro. -#[proc_macro] -#[allow_internal_unstable(step_trait, rustc_attrs, trusted_step, spec_option_partial_eq)] -pub fn newtype_index(input: TokenStream) -> TokenStream { - newtype::newtype(input) -} - decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive); decl_derive!( [HashStable_Generic, attributes(stable_hasher)] => hash_stable::hash_stable_generic_derive ); +decl_derive!( + [HashStable_NoContext] => + /// `HashStable` implementation that has no `HashStableContext` bound and + /// which adds `where` bounds for `HashStable` based off of fields and not + /// generics. This is suitable for use in crates like `rustc_type_ir`. + hash_stable::hash_stable_no_context_derive +); decl_derive!([Decodable] => serialize::decodable_derive); decl_derive!([Encodable] => serialize::encodable_derive); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index ab135851b8e0..f352fa6d46ae 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> = self.tcx.erase_late_bound_regions( + let argument_types: &List> = self.tcx.instantiate_bound_regions_with_erased( self.tcx .type_of(item) .instantiate_identity() diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 354023cea9e0..333dc8ec9111 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -21,6 +21,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use rustc_middle::middle::lib_features::LibFeatures; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::ty::fast_reject::SimplifiedType; @@ -828,7 +829,7 @@ impl MetadataBlob { out, "{}{}", feature, - if let Some(since) = since { + if let FeatureStability::AcceptedSince(since) = since { format!(" since {since}") } else { String::new() @@ -1176,8 +1177,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } /// Iterates over all the stability attributes in the given crate. - fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option)] { - tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) + fn get_lib_features(self) -> LibFeatures { + LibFeatures { + stability: self + .root + .lib_features + .decode(self) + .map(|(sym, stab)| (sym, (stab, DUMMY_SP))) + .collect(), + } } /// Iterates over the stability implications in the given crate (when a `#[unstable]` attribute diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 595d816e9493..909a977ccc9a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -346,7 +346,7 @@ provide! { tcx, def_id, other, cdata, module_children => { tcx.arena.alloc_from_iter(cdata.get_module_children(def_id.index, tcx.sess)) } - defined_lib_features => { cdata.get_lib_features(tcx) } + lib_features => { cdata.get_lib_features() } stability_implications => { cdata.get_stability_implications(tcx).iter().copied().collect() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b6ffdc7378f6..19c5e8e5d59e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -25,6 +25,7 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportInfo, }; +use rustc_middle::middle::lib_features::FeatureStability; use rustc_middle::mir::interpret; use rustc_middle::query::LocalCrate; use rustc_middle::query::Providers; @@ -1902,10 +1903,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(deps.iter().map(|(_, dep)| dep)) } - fn encode_lib_features(&mut self) -> LazyArray<(Symbol, Option)> { + fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> { empty_proc_macro!(self); let tcx = self.tcx; - let lib_features = tcx.lib_features(()); + let lib_features = tcx.lib_features(LOCAL_CRATE); self.lazy_array(lib_features.to_vec()) } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 9ae5c0af0b24..145db4b2ed6a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -3,6 +3,7 @@ use decoder::Metadata; use def_path_hash_map::DefPathHashMapRef; use rustc_data_structures::fx::FxHashMap; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; +use rustc_middle::middle::lib_features::FeatureStability; use table::TableBuilder; use rustc_ast as ast; @@ -263,7 +264,7 @@ pub(crate) struct CrateRoot { crate_deps: LazyArray, dylib_dependency_formats: LazyArray>, - lib_features: LazyArray<(Symbol, Option)>, + lib_features: LazyArray<(Symbol, FeatureStability)>, stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, LangItem)>, lang_items_missing: LazyArray, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index dd761b4e3120..5735c5568f79 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -27,19 +27,12 @@ macro_rules! arena_types { rustc_middle::mir::Promoted, rustc_middle::mir::Body<'tcx> >, - [decode] closure_debuginfo: - rustc_index::IndexVec< - rustc_target::abi::FieldIdx, - rustc_span::symbol::Symbol, - >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: - rustc_middle::mir::BorrowCheckResult<'tcx>, + [decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, rustc_data_structures::sync::Lrc, )>, - [] output_filenames: std::sync::Arc, [] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>, [] resolutions: rustc_middle::ty::ResolverGlobalCtxt, [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, @@ -91,21 +84,16 @@ macro_rules! arena_types { rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> >, - [] all_traits: Vec, [] effective_visibilities: rustc_middle::middle::privacy::EffectiveVisibilities, - [] foreign_module: rustc_session::cstore::ForeignModule, - [] foreign_modules: Vec, [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap, [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, [decode] attribute: rustc_ast::Attribute, [] name_set: rustc_data_structures::unord::UnordSet, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, - [] hir_id_set: rustc_hir::HirIdSet, // Interned types [] tys: rustc_type_ir::WithCachedTypeInfo>, - [] predicates: rustc_type_ir::WithCachedTypeInfo>, [] consts: rustc_middle::ty::ConstData<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, @@ -113,7 +101,6 @@ macro_rules! arena_types { // (during lowering) and the `librustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::unord::UnordSet, - [decode] registered_tools: rustc_middle::ty::RegisteredTools, [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, @@ -124,11 +111,9 @@ macro_rules! arena_types { rustc_hir::def_id::DefId, rustc_middle::ty::EarlyBinder> >, - [] bit_set_u32: rustc_index::bit_set::BitSet, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>, [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, - [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 76ef62f9f272..dc0da165af67 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/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 85c5af9ca13c..8c1b1ff12e98 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -7,22 +7,23 @@ pub mod lib_features { use rustc_data_structures::fx::FxHashMap; use rustc_span::{symbol::Symbol, Span}; - #[derive(HashStable, Debug)] + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + #[derive(HashStable, TyEncodable, TyDecodable)] + pub enum FeatureStability { + AcceptedSince(Symbol), + Unstable, + } + + #[derive(HashStable, Debug, Default)] pub struct LibFeatures { - /// A map from feature to stabilisation version. - pub stable: FxHashMap, - pub unstable: FxHashMap, + pub stability: FxHashMap, } impl LibFeatures { - pub fn to_vec(&self) -> Vec<(Symbol, Option)> { - let mut all_features: Vec<_> = self - .stable - .iter() - .map(|(f, (s, _))| (*f, Some(*s))) - .chain(self.unstable.keys().map(|f| (*f, None))) - .collect(); - all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap()); + pub fn to_vec(&self) -> Vec<(Symbol, FeatureStability)> { + let mut all_features: Vec<_> = + self.stability.iter().map(|(&sym, &(stab, _))| (sym, stab)).collect(); + all_features.sort_unstable_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str())); all_features } } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b87c6885e04c..e360fb3eaaf3 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -525,13 +525,6 @@ 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(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/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a1324858416a..1974a35cb85c 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 + '_ { - alloc.inner().provenance().ptrs().values().map(|id| *id) + alloc.inner().provenance().ptrs().values().copied() } fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator + '_ { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index d609965f36fe..b5dd3010d3a8 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -341,7 +341,11 @@ pub enum ConstraintCategory<'tcx> { UseAsConst, UseAsStatic, TypeAnnotation, - Cast, + Cast { + /// Whether this is an unsizing cast and if yes, this contains the target type. + /// Region variables are erased to ReErased. + unsize_to: Option>, + }, /// A constraint that came from checking the body of a closure. /// diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 7b0f27f9b348..8cf9e55f0b60 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1404,18 +1404,18 @@ pub enum BinOp { BitOr, /// The `<<` operator (shift left) /// - /// The offset is truncated to the size of the first operand before shifting. + /// The offset is truncated to the size of the first operand and made unsigned before shifting. Shl, - /// Like `Shl`, but is UB if the RHS >= LHS::BITS + /// Like `Shl`, but is UB if the RHS >= LHS::BITS or RHS < 0 ShlUnchecked, /// The `>>` operator (shift right) /// - /// The offset is truncated to the size of the first operand before shifting. + /// The offset is truncated to the size of the first operand and made unsigned before shifting. /// /// This is an arithmetic shift if the LHS is signed /// and a logical shift if the LHS is unsigned. Shr, - /// Like `Shl`, but is UB if the RHS >= LHS::BITS + /// Like `Shl`, but is UB if the RHS >= LHS::BITS or RHS < 0 ShrUnchecked, /// The `==` operator (equality) Eq, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f9ec368361c5..cbba990867e0 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -109,10 +109,12 @@ pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsure, TyCtxtEnsureWithValue // Queries marked with `fatal_cycle` do not need the latter implementation, // as they will raise an fatal error on query cycles instead. rustc_queries! { + /// This exists purely for testing the interactions between delay_span_bug and incremental. query trigger_delay_span_bug(key: DefId) -> () { - desc { "triggering a delay span bug" } + desc { "triggering a delay span bug for testing incremental" } } + /// Collects the list of all tools registered using `#![register_tool]`. query registered_tools(_: ()) -> &'tcx ty::RegisteredTools { arena_cache desc { "compute registered tools for crate" } @@ -286,6 +288,7 @@ rustc_queries! { } } + /// The root query triggering all analysis passes like typeck or borrowck. query analysis(key: ()) -> Result<(), ErrorGuaranteed> { eval_always desc { "running analysis passes on this crate" } @@ -1732,13 +1735,10 @@ rustc_queries! { desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) } } - query lib_features(_: ()) -> &'tcx LibFeatures { - arena_cache - desc { "calculating the lib features map" } - } - query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option)] { + query lib_features(_: CrateNum) -> &'tcx LibFeatures { desc { "calculating the lib features defined in a crate" } separate_provide_extern + arena_cache } query stability_implications(_: CrateNum) -> &'tcx FxHashMap { arena_cache @@ -1781,10 +1781,17 @@ rustc_queries! { desc { "calculating the missing lang items in a crate" } separate_provide_extern } + + /// The visible parent map is a map from every item to a visible parent. + /// It prefers the shortest visible path to an item. + /// Used for diagnostics, for example path trimming. + /// The parents are modules, enums or traits. query visible_parent_map(_: ()) -> &'tcx DefIdMap { arena_cache desc { "calculating the visible parent map" } } + /// Collects the "trimmed", shortest accessible paths to all items for diagnostics. + /// See the [provider docs](`rustc_middle::ty::print::trimmed_def_paths`) for more info. query trimmed_def_paths(_: ()) -> &'tcx FxHashMap { arena_cache desc { "calculating trimmed def paths" } diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index a5916c4ab855..7883cd338bef 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 4b73d8e41a15..ab9e0283918d 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/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 4af841fcf9a5..16e7b16a0bf8 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -3,7 +3,6 @@ use crate::mir; use crate::ty::abstract_const::CastKind; use crate::ty::GenericArgsRef; use crate::ty::{self, visit::TypeVisitableExt as _, List, Ty, TyCtxt}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; @@ -77,28 +76,3 @@ static_assert_size!(Expr<'_>, 24); #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(super::ConstKind<'_>, 32); - -/// An inference variable for a const, for use in const generics. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] -pub enum InferConst { - /// Infer the value of the const. - Var(ty::ConstVid), - /// Infer the value of the effect. - /// - /// For why this is separate from the `Var` variant above, see the - /// documentation on `EffectVid`. - EffectVar(ty::EffectVid), - /// A fresh const variable. See `infer::freshen` for more details. - Fresh(u32), -} - -impl HashStable for InferConst { - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - match self { - InferConst::Var(_) | InferConst::EffectVar(_) => { - panic!("const variables should not be hashed: {self:?}") - } - InferConst::Fresh(i) => i.hash_stable(hcx, hasher), - } - } -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e635c3f96ec9..62b0536dabee 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -26,9 +26,9 @@ use crate::traits::solve::{ }; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind, - ImplPolarity, InferTy, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, - Predicate, PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, - TyVid, TypeAndMut, Visibility, + ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, + PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, + TypeAndMut, Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -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; @@ -96,7 +95,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ParamTy = ParamTy; type BoundTy = ty::BoundTy; type PlaceholderTy = ty::PlaceholderType; - type InferTy = InferTy; type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = &'tcx List>; @@ -104,7 +102,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AllocId = crate::mir::interpret::AllocId; type Const = ty::Const<'tcx>; - type InferConst = ty::InferConst; type AliasConst = ty::UnevaluatedConst<'tcx>; type PlaceholderConst = ty::PlaceholderConst; type ParamConst = ty::ParamConst; @@ -544,12 +541,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, - pub dep_graph: DepGraph, pub prof: SelfProfilerRef, @@ -709,7 +700,6 @@ impl<'tcx> TyCtxt<'tcx> { s: &'tcx Session, crate_types: Vec, stable_crate_id: StableCrateId, - lint_store: Lrc, arena: &'tcx WorkerLocal>, hir_arena: &'tcx WorkerLocal>, untracked: Untracked, @@ -730,7 +720,6 @@ impl<'tcx> TyCtxt<'tcx> { sess: s, crate_types, stable_crate_id, - lint_store, arena, hir_arena, interners, diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index cff0d4df6735..3e64f9a2a909 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( + pub fn instantiate_bound_regions( 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( + pub fn instantiate_bound_regions_uncached( self, value: Binder<'tcx, T>, mut replace_regions: F, @@ -325,7 +325,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable>, { - self.replace_late_bound_regions_uncached(value, |br| { + 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(self, value: Binder<'tcx, T>) -> T + pub fn instantiate_bound_regions_with_erased(self, value: Binder<'tcx, T>) -> T where T: TypeFoldable>, { - 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/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 559bf9fb825d..2436daf62cf2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -84,9 +84,7 @@ pub use self::closure::{ CapturedPlace, ClosureKind, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL, }; -pub use self::consts::{ - Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree, -}; +pub use self::consts::{Const, ConstData, ConstInt, Expr, ScalarInt, UnevaluatedConst, ValTree}; pub use self::context::{ tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, }; @@ -98,7 +96,7 @@ 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, EarlyParamRegion, EffectVid, ExistentialPredicate, + CoroutineArgs, CoroutineArgsParts, EarlyParamRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, GenSig, InlineConstArgs, InlineConstArgsParts, LateParamRegion, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index fd125af2074e..27c436c82f53 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( self, @@ -106,7 +110,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable>, { - 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>, { - 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/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 9afa50cf584c..a63a4eff5e12 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -59,6 +59,7 @@ trivially_parameterized_over_tcx! { crate::middle::codegen_fn_attrs::CodegenFnAttrs, crate::middle::debugger_visualizer::DebuggerVisualizerFile, crate::middle::exported_symbols::SymbolExportInfo, + crate::middle::lib_features::FeatureStability, crate::middle::resolve_bound_vars::ObjectLifetimeDefault, crate::mir::ConstQualifs, ty::AssocItemContainer, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ad070dcc9e38..d478677c3679 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3018,7 +3018,8 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N /// The implementation uses similar import discovery logic to that of 'use' suggestions. /// /// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths!`]. -fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap { +// this is pub to be able to intra-doc-link it +pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap { let mut map: FxHashMap = FxHashMap::default(); if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index e223ffd7c5d5..971acda33e23 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -202,34 +202,6 @@ impl<'tcx> DebugWithInfcx> for AliasTy<'tcx> { } } -impl fmt::Debug for ty::InferConst { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - InferConst::Var(var) => write!(f, "{var:?}"), - InferConst::EffectVar(var) => write!(f, "{var:?}"), - InferConst::Fresh(var) => write!(f, "Fresh({var:?})"), - } - } -} -impl<'tcx> DebugWithInfcx> for ty::InferConst { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - use ty::InferConst::*; - match this.infcx.universe_of_ct(*this.data) { - None => write!(f, "{:?}", this.data), - Some(universe) => match *this.data { - Var(vid) => write!(f, "?{}_{}c", vid.index(), universe.index()), - EffectVar(vid) => write!(f, "?{}_{}e", vid.index(), universe.index()), - Fresh(_) => { - unreachable!() - } - }, - } - } -} - impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { WithInfcx::with_no_infcx(self).fmt(f) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 2b1e57f58717..ea0e179c00de 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -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 { + 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) } @@ -1586,24 +1608,6 @@ impl fmt::Debug for EarlyParamRegion { } } -rustc_index::newtype_index! { - /// A **`const`** **v**ariable **ID**. - #[debug_format = "?{}c"] - pub struct ConstVid {} -} - -rustc_index::newtype_index! { - /// An **effect** **v**ariable **ID**. - /// - /// Handling effect infer variables happens separately from const infer variables - /// because we do not want to reuse any of the const infer machinery. If we try to - /// relate an effect variable with a normal one, we would ICE, which can catch bugs - /// where we are not correctly using the effect var for an effect param. Fallback - /// is also implemented on top of having separate effect and normal const variables. - #[debug_format = "?{}e"] - pub struct EffectVid {} -} - rustc_index::newtype_index! { /// A **region** (lifetime) **v**ariable **ID**. #[derive(HashStable)] diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index eece8684e360..2130dbdc033e 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -600,10 +600,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BinOp::Shl | BinOp::Shr if self.check_overflow && ty.is_integral() => { // For an unsigned RHS, the shift is in-range for `rhs < bits`. // For a signed RHS, `IntToInt` cast to the equivalent unsigned - // type and do that same comparison. Because the type is the - // same size, there's no negative shift amount that ends up - // overlapping with valid ones, thus it catches negatives too. + // type and do that same comparison. + // A negative value will be *at least* 128 after the cast (that's i8::MIN), + // and 128 is an overflowing shift amount for all our currently existing types, + // so this cast can never make us miss an overflow. let (lhs_size, _) = ty.int_size_and_signed(self.tcx); + assert!(lhs_size.bits() <= 128); let rhs_ty = rhs.ty(&self.local_decls, self.tcx); let (rhs_size, _) = rhs_ty.int_size_and_signed(self.tcx); @@ -625,7 +627,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // This can't overflow because the largest shiftable types are 128-bit, // which fits in `u8`, the smallest possible `unsigned_ty`. - // (And `from_uint` will `bug!` if that's ever no longer true.) let lhs_bits = Operand::const_from_scalar( self.tcx, unsigned_ty, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index bcd9140f0e18..261bd30b94ac 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_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index dfafd8598306..5890c0f2db38 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -51,6 +51,7 @@ //! Otherwise it drops all the values in scope at the last suspension point. use crate::abort_unwinding_calls; +use crate::add_call_guards; use crate::deref_separator::deref_finder; use crate::errors; use crate::pass_manager as pm; @@ -1162,7 +1163,7 @@ fn create_coroutine_drop_shim<'tcx>( pm::run_passes_no_validate( tcx, &mut body, - &[&abort_unwinding_calls::AbortUnwindingCalls], + &[&abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges], None, ); diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 98a036182861..735960f31b37 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -88,8 +88,8 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; +use rustc_index::newtype_index; use rustc_index::IndexVec; -use rustc_macros::newtype_index; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 793dcf0d994c..7b5697bc9496 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -32,7 +32,6 @@ struct CallSite<'tcx> { callee: Instance<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, block: BasicBlock, - target: Option, source_info: SourceInfo, } @@ -367,7 +366,7 @@ impl<'tcx> Inliner<'tcx> { ) -> Option> { // Only consider direct calls to functions let terminator = bb_data.terminator(); - if let TerminatorKind::Call { ref func, target, fn_span, .. } = terminator.kind { + if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { let func_ty = func.ty(caller_body, self.tcx); if let ty::FnDef(def_id, args) = *func_ty.kind() { // To resolve an instance its args have to be fully normalized. @@ -386,7 +385,7 @@ impl<'tcx> Inliner<'tcx> { let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args); let source_info = SourceInfo { span: fn_span, ..terminator.source_info }; - return Some(CallSite { callee, fn_sig, block: bb, target, source_info }); + return Some(CallSite { callee, fn_sig, block: bb, source_info }); } } @@ -541,142 +540,158 @@ impl<'tcx> Inliner<'tcx> { mut callee_body: Body<'tcx>, ) { let terminator = caller_body[callsite.block].terminator.take().unwrap(); - match terminator.kind { - TerminatorKind::Call { args, destination, unwind, .. } => { - // If the call is something like `a[*i] = f(i)`, where - // `i : &mut usize`, then just duplicating the `a[*i]` - // Place could result in two different locations if `f` - // writes to `i`. To prevent this we need to create a temporary - // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: Place<'_>) -> bool { - for elem in place.projection.iter() { - match elem { - ProjectionElem::Deref | ProjectionElem::Index(_) => return true, - _ => {} - } - } + let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else { + bug!("unexpected terminator kind {:?}", terminator.kind); + }; - false + let return_block = if let Some(block) = target { + // Prepare a new block for code that should execute when call returns. We don't use + // target block directly since it might have other predecessors. + let mut data = BasicBlockData::new(Some(Terminator { + source_info: terminator.source_info, + kind: TerminatorKind::Goto { target: block }, + })); + data.is_cleanup = caller_body[block].is_cleanup; + Some(caller_body.basic_blocks_mut().push(data)) + } else { + None + }; + + // If the call is something like `a[*i] = f(i)`, where + // `i : &mut usize`, then just duplicating the `a[*i]` + // Place could result in two different locations if `f` + // writes to `i`. To prevent this we need to create a temporary + // borrow of the place and pass the destination as `*temp` instead. + fn dest_needs_borrow(place: Place<'_>) -> bool { + for elem in place.projection.iter() { + match elem { + ProjectionElem::Deref | ProjectionElem::Index(_) => return true, + _ => {} } - - let dest = if dest_needs_borrow(destination) { - trace!("creating temp for return destination"); - let dest = Rvalue::Ref( - self.tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - destination, - ); - let dest_ty = dest.ty(caller_body, self.tcx); - let temp = Place::from(self.new_call_temp(caller_body, &callsite, dest_ty)); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((temp, dest))), - }); - self.tcx.mk_place_deref(temp) - } else { - destination - }; - - // Always create a local to hold the destination, as `RETURN_PLACE` may appear - // where a full `Place` is not allowed. - let (remap_destination, destination_local) = if let Some(d) = dest.as_local() { - (false, d) - } else { - ( - true, - self.new_call_temp( - caller_body, - &callsite, - destination.ty(caller_body, self.tcx).ty, - ), - ) - }; - - // Copy the arguments if needed. - let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body); - - let mut integrator = Integrator { - args: &args, - new_locals: Local::new(caller_body.local_decls.len()).., - new_scopes: SourceScope::new(caller_body.source_scopes.len()).., - new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., - destination: destination_local, - callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), - callsite, - cleanup_block: unwind, - in_cleanup_block: false, - tcx: self.tcx, - always_live_locals: BitSet::new_filled(callee_body.local_decls.len()), - }; - - // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones - // (or existing ones, in a few special cases) in the caller. - integrator.visit_body(&mut callee_body); - - // If there are any locals without storage markers, give them storage only for the - // duration of the call. - for local in callee_body.vars_and_temps_iter() { - if integrator.always_live_locals.contains(local) { - let new_local = integrator.map_local(local); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageLive(new_local), - }); - } - } - if let Some(block) = callsite.target { - // To avoid repeated O(n) insert, push any new statements to the end and rotate - // the slice once. - let mut n = 0; - if remap_destination { - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new(( - dest, - Rvalue::Use(Operand::Move(destination_local.into())), - ))), - }); - n += 1; - } - for local in callee_body.vars_and_temps_iter().rev() { - if integrator.always_live_locals.contains(local) { - let new_local = integrator.map_local(local); - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageDead(new_local), - }); - n += 1; - } - } - caller_body[block].statements.rotate_right(n); - } - - // Insert all of the (mapped) parts of the callee body into the caller. - caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); - caller_body.source_scopes.extend(&mut callee_body.source_scopes.drain(..)); - caller_body.var_debug_info.append(&mut callee_body.var_debug_info); - caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..)); - - caller_body[callsite.block].terminator = Some(Terminator { - source_info: callsite.source_info, - kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, - }); - - // Copy only unevaluated constants from the callee_body into the caller_body. - // Although we are only pushing `ConstKind::Unevaluated` consts to - // `required_consts`, here we may not only have `ConstKind::Unevaluated` - // because we are calling `instantiate_and_normalize_erasing_regions`. - caller_body.required_consts.extend( - callee_body.required_consts.iter().copied().filter(|&ct| match ct.const_ { - Const::Ty(_) => { - bug!("should never encounter ty::UnevaluatedConst in `required_consts`") - } - Const::Val(..) | Const::Unevaluated(..) => true, - }), - ); } - kind => bug!("unexpected terminator kind {:?}", kind), + + false } + + let dest = if dest_needs_borrow(destination) { + trace!("creating temp for return destination"); + let dest = Rvalue::Ref( + self.tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + destination, + ); + let dest_ty = dest.ty(caller_body, self.tcx); + let temp = + Place::from(self.new_call_temp(caller_body, &callsite, dest_ty, return_block)); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new((temp, dest))), + }); + self.tcx.mk_place_deref(temp) + } else { + destination + }; + + // Always create a local to hold the destination, as `RETURN_PLACE` may appear + // where a full `Place` is not allowed. + let (remap_destination, destination_local) = if let Some(d) = dest.as_local() { + (false, d) + } else { + ( + true, + self.new_call_temp( + caller_body, + &callsite, + destination.ty(caller_body, self.tcx).ty, + return_block, + ), + ) + }; + + // Copy the arguments if needed. + let args: Vec<_> = + self.make_call_args(args, &callsite, caller_body, &callee_body, return_block); + + let mut integrator = Integrator { + args: &args, + new_locals: Local::new(caller_body.local_decls.len()).., + new_scopes: SourceScope::new(caller_body.source_scopes.len()).., + new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., + destination: destination_local, + callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), + callsite, + cleanup_block: unwind, + in_cleanup_block: false, + return_block, + tcx: self.tcx, + always_live_locals: BitSet::new_filled(callee_body.local_decls.len()), + }; + + // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones + // (or existing ones, in a few special cases) in the caller. + integrator.visit_body(&mut callee_body); + + // If there are any locals without storage markers, give them storage only for the + // duration of the call. + for local in callee_body.vars_and_temps_iter() { + if integrator.always_live_locals.contains(local) { + let new_local = integrator.map_local(local); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageLive(new_local), + }); + } + } + if let Some(block) = return_block { + // To avoid repeated O(n) insert, push any new statements to the end and rotate + // the slice once. + let mut n = 0; + if remap_destination { + caller_body[block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new(( + dest, + Rvalue::Use(Operand::Move(destination_local.into())), + ))), + }); + n += 1; + } + for local in callee_body.vars_and_temps_iter().rev() { + if integrator.always_live_locals.contains(local) { + let new_local = integrator.map_local(local); + caller_body[block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageDead(new_local), + }); + n += 1; + } + } + caller_body[block].statements.rotate_right(n); + } + + // Insert all of the (mapped) parts of the callee body into the caller. + caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); + caller_body.source_scopes.extend(&mut callee_body.source_scopes.drain(..)); + caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..)); + + caller_body[callsite.block].terminator = Some(Terminator { + source_info: callsite.source_info, + kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, + }); + + // Copy only unevaluated constants from the callee_body into the caller_body. + // Although we are only pushing `ConstKind::Unevaluated` consts to + // `required_consts`, here we may not only have `ConstKind::Unevaluated` + // because we are calling `instantiate_and_normalize_erasing_regions`. + caller_body.required_consts.extend(callee_body.required_consts.iter().copied().filter( + |&ct| match ct.const_ { + Const::Ty(_) => { + bug!("should never encounter ty::UnevaluatedConst in `required_consts`") + } + Const::Val(..) | Const::Unevaluated(..) => true, + }, + )); } fn make_call_args( @@ -685,6 +700,7 @@ impl<'tcx> Inliner<'tcx> { callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, callee_body: &Body<'tcx>, + return_block: Option, ) -> Vec { let tcx = self.tcx; @@ -713,8 +729,18 @@ impl<'tcx> Inliner<'tcx> { // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. if callsite.fn_sig.abi() == Abi::RustCall && callee_body.spread_arg.is_none() { let mut args = args.into_iter(); - let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); - let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); + let self_ = self.create_temp_if_necessary( + args.next().unwrap(), + callsite, + caller_body, + return_block, + ); + let tuple = self.create_temp_if_necessary( + args.next().unwrap(), + callsite, + caller_body, + return_block, + ); assert!(args.next().is_none()); let tuple = Place::from(tuple); @@ -731,13 +757,13 @@ impl<'tcx> Inliner<'tcx> { let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty)); // Spill to a local to make e.g., `tmp0`. - self.create_temp_if_necessary(tuple_field, callsite, caller_body) + self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block) }); closure_ref_arg.chain(tuple_tmp_args).collect() } else { args.into_iter() - .map(|a| self.create_temp_if_necessary(a, callsite, caller_body)) + .map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block)) .collect() } } @@ -749,6 +775,7 @@ impl<'tcx> Inliner<'tcx> { arg: Operand<'tcx>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, + return_block: Option, ) -> Local { // Reuse the operand if it is a moved temporary. if let Operand::Move(place) = &arg @@ -761,7 +788,7 @@ impl<'tcx> Inliner<'tcx> { // Otherwise, create a temporary for the argument. trace!("creating temp for argument {:?}", arg); let arg_ty = arg.ty(caller_body, self.tcx); - let local = self.new_call_temp(caller_body, callsite, arg_ty); + let local = self.new_call_temp(caller_body, callsite, arg_ty, return_block); caller_body[callsite.block].statements.push(Statement { source_info: callsite.source_info, kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), @@ -775,6 +802,7 @@ impl<'tcx> Inliner<'tcx> { caller_body: &mut Body<'tcx>, callsite: &CallSite<'tcx>, ty: Ty<'tcx>, + return_block: Option, ) -> Local { let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); @@ -783,7 +811,7 @@ impl<'tcx> Inliner<'tcx> { kind: StatementKind::StorageLive(local), }); - if let Some(block) = callsite.target { + if let Some(block) = return_block { caller_body[block].statements.insert( 0, Statement { @@ -814,6 +842,7 @@ struct Integrator<'a, 'tcx> { callsite: &'a CallSite<'tcx>, cleanup_block: UnwindAction, in_cleanup_block: bool, + return_block: Option, tcx: TyCtxt<'tcx>, always_live_locals: BitSet, } @@ -957,7 +986,7 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { *unwind = self.map_unwind(*unwind); } TerminatorKind::Return => { - terminator.kind = if let Some(tgt) = self.callsite.target { + terminator.kind = if let Some(tgt) = self.return_block { TerminatorKind::Goto { target: tgt } } else { TerminatorKind::Unreachable diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 5f3d8dfc6c42..249e0fc633e8 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/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 4ae5ea4c8d68..f24a2d07e494 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -111,8 +111,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::MakeShim, - &add_call_guards::CriticalCallEdges, &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, ], Some(MirPhase::Runtime(RuntimePhase::Optimized)), ); @@ -179,7 +179,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) 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_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 266190da035e..49a2c4144674 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -739,6 +739,9 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe` +parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before + .suggestion = move `{$kw}` before the `for<...>` + parse_type_ascription_removed = if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8ab1ec298a1c..7ce348619c6b 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, }, } @@ -2828,3 +2827,23 @@ pub(crate) struct GenericArgsInPatRequireTurbofishSyntax { )] pub suggest_turbofish: Span, } + +#[derive(Diagnostic)] +#[diag(parse_transpose_dyn_or_impl)] +pub(crate) struct TransposeDynOrImpl<'a> { + #[primary_span] + pub span: Span, + pub kw: &'a str, + #[subdiagnostic] + pub sugg: TransposeDynOrImplSugg<'a>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct TransposeDynOrImplSugg<'a> { + #[suggestion_part(code = "")] + pub removal_span: Span, + #[suggestion_part(code = "{kw} ")] + pub insertion_span: Span, + pub kw: &'a str, +} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 104de47b97dd..bad7c19cc278 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 f2acb70ac45b..57bc865a0eed 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -21,14 +21,16 @@ use crate::errors::{ use crate::fluent_generated as fluent; use crate::parser; +use crate::parser::attr::InnerAttrPolicy; 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; @@ -722,6 +724,101 @@ impl<'a> Parser<'a> { Err(err) } + pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) { + // Missing semicolon typo error. + let span = self.prev_token.span.shrink_to_hi(); + let mut err = self.sess.create_err(ExpectedSemi { + span, + token: self.token.clone(), + unexpected_token_label: Some(self.token.span), + sugg: ExpectedSemiSugg::AddSemi(span), + }); + let attr_span = match &expr.attrs[..] { + [] => unreachable!(), + [only] => only.span, + [first, rest @ ..] => { + for attr in rest { + err.span_label(attr.span, ""); + } + first.span + } + }; + err.span_label( + attr_span, + format!( + "only `;` terminated statements or tail expressions are allowed after {}", + if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" }, + ), + ); + if self.token == token::Pound + && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Bracket)) + { + // We have + // #[attr] + // expr + // #[not_attr] + // other_expr + err.span_label(span, "expected `;` here"); + err.multipart_suggestion( + "alternatively, consider surrounding the expression with a block", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ); + let mut snapshot = self.create_snapshot_for_diagnostic(); + if let [attr] = &expr.attrs[..] + && let ast::AttrKind::Normal(attr_kind) = &attr.kind + && let [segment] = &attr_kind.item.path.segments[..] + && segment.ident.name == sym::cfg + && let Ok(next_attr) = snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None)) + && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind + && let [next_segment] = &next_attr_kind.item.path.segments[..] + && segment.ident.name == sym::cfg + && let Ok(next_expr) = snapshot.parse_expr() + { + // We have for sure + // #[cfg(..)] + // expr + // #[cfg(..)] + // other_expr + // So we suggest using `if cfg!(..) { expr } else if cfg!(..) { other_expr }`. + let margin = self.sess.source_map().span_to_margin(next_expr.span).unwrap_or(0); + let sugg = vec![ + (attr.span.with_hi(segment.span().hi()), "if cfg!".to_string()), + ( + attr_kind.item.args.span().unwrap().shrink_to_hi().with_hi(attr.span.hi()), + " {".to_string(), + ), + (expr.span.shrink_to_lo(), " ".to_string()), + ( + next_attr.span.with_hi(next_segment.span().hi()), + "} else if cfg!".to_string(), + ), + ( + next_attr_kind + .item + .args + .span() + .unwrap() + .shrink_to_hi() + .with_hi(next_attr.span.hi()), + " {".to_string(), + ), + (next_expr.span.shrink_to_lo(), " ".to_string()), + (next_expr.span.shrink_to_hi(), format!("\n{}}}", " ".repeat(margin))), + ]; + err.multipart_suggestion( + "it seems like you are trying to provide different expressions depending on \ + `cfg`, consider using `if cfg!(..)`", + sugg, + Applicability::MachineApplicable, + ); + } + } + err.emit(); + } fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool { let sm = self.sess.source_map(); match (&self.prev_token.kind, &self.token.kind) { @@ -2252,6 +2349,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 235b28b6e26e..d8e99d34016e 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 801860c21236..d124ea571ab0 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 2fe706943687..9bd436f01ac7 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); @@ -110,7 +110,7 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath { && $self.may_recover() && $self.look_ahead(1, |t| t == &token::ModSep) && let token::Interpolated(nt) = &$self.token.kind - && let token::NtTy(ty) = &**nt + && let token::NtTy(ty) = &nt.0 { let ty = ty.clone(); $self.bump(); @@ -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 025b0615a7e4..06cc39fbb5ad 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 0a4c7c17d06a..5cec3f5762d7 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 8626dbe40afd..0f4ba9617c68 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 aa939a71d637..5316dde60960 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(); @@ -617,6 +617,20 @@ impl<'a> Parser<'a> { let mut add_semi_to_stmt = false; match &mut stmt.kind { + // Expression without semicolon. + StmtKind::Expr(expr) + if classify::expr_requires_semi_to_be_stmt(expr) + && !expr.attrs.is_empty() + && ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)] + .contains(&self.token.kind) => + { + // The user has written `#[attr] expr` which is unsupported. (#106020) + self.attr_on_non_tail_expr(&expr); + // We already emitted an error, so don't emit another type error + let sp = expr.span.to(self.prev_token.span); + *expr = self.mk_expr_err(sp); + } + // Expression without semicolon. StmtKind::Expr(expr) if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index dc0f13965230..75617b1b3ea5 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -287,6 +287,7 @@ impl<'a> Parser<'a> { // Function pointer type self.parse_ty_bare_fn(lo, ThinVec::new(), None, recover_return_sign)? } else if self.check_keyword(kw::For) { + let for_span = self.token.span; // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` @@ -299,9 +300,42 @@ impl<'a> Parser<'a> { recover_return_sign, )? } else { - let path = self.parse_path(PathStyle::Type)?; - let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); - self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)? + // Try to recover `for<'a> dyn Trait` or `for<'a> impl Trait`. + if self.may_recover() + && (self.eat_keyword_noexpect(kw::Impl) || self.eat_keyword_noexpect(kw::Dyn)) + { + let kw = self.prev_token.ident().unwrap().0.name; + let mut err = self.sess.create_err(errors::TransposeDynOrImpl { + span: self.prev_token.span, + kw: kw.as_str(), + sugg: errors::TransposeDynOrImplSugg { + removal_span: self.prev_token.span.with_hi(self.token.span.lo()), + insertion_span: for_span.shrink_to_lo(), + kw: kw.as_str(), + }, + }); + let path = self.parse_path(PathStyle::Type)?; + let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); + let kind = + self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?; + // Take the parsed bare trait object and turn it either + // into a `dyn` object or an `impl Trait`. + let kind = match (kind, kw) { + (TyKind::TraitObject(bounds, _), kw::Dyn) => { + TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) + } + (TyKind::TraitObject(bounds, _), kw::Impl) => { + TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) + } + _ => return Err(err), + }; + err.emit(); + kind + } else { + let path = self.parse_path(PathStyle::Type)?; + let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); + self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)? + } } } else if self.eat_keyword(kw::Impl) { self.parse_impl_ty(&mut impl_dyn_multi)? diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f73965982204..e6c46fc25283 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -186,6 +186,7 @@ fn emit_malformed_attribute( msg.push_str(&format!("`{code}`")); suggestions.push(code); } + suggestions.sort(); if should_warn(name) { sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg); } else { diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 0daa273db676..6c0412beda29 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -8,18 +8,14 @@ use rustc_ast::Attribute; use rustc_attr::VERSION_PLACEHOLDER; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::lib_features::LibFeatures; -use rustc_middle::query::Providers; +use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; +use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice}; -fn new_lib_features() -> LibFeatures { - LibFeatures { stable: Default::default(), unstable: Default::default() } -} - pub struct LibFeatureCollector<'tcx> { tcx: TyCtxt<'tcx>, lib_features: LibFeatures, @@ -27,10 +23,10 @@ pub struct LibFeatureCollector<'tcx> { impl<'tcx> LibFeatureCollector<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> LibFeatureCollector<'tcx> { - LibFeatureCollector { tcx, lib_features: new_lib_features() } + LibFeatureCollector { tcx, lib_features: LibFeatures::default() } } - fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option, Span)> { + fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> { let stab_attrs = [ sym::stable, sym::unstable, @@ -72,8 +68,11 @@ impl<'tcx> LibFeatureCollector<'tcx> { | sym::rustc_const_unstable | sym::rustc_default_body_unstable ); - if since.is_some() || is_unstable { - return Some((feature, since, attr.span)); + if is_unstable { + return Some((feature, FeatureStability::Unstable, attr.span)); + } + if let Some(since) = since { + return Some((feature, FeatureStability::AcceptedSince(since), attr.span)); } } // We need to iterate over the other attributes, because @@ -86,39 +85,39 @@ impl<'tcx> LibFeatureCollector<'tcx> { None } - fn collect_feature(&mut self, feature: Symbol, since: Option, span: Span) { - let already_in_stable = self.lib_features.stable.contains_key(&feature); - let already_in_unstable = self.lib_features.unstable.contains_key(&feature); + fn collect_feature(&mut self, feature: Symbol, stability: FeatureStability, span: Span) { + let existing_stability = self.lib_features.stability.get(&feature).cloned(); - match (since, already_in_stable, already_in_unstable) { - (Some(since), _, false) => { - if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { - if *prev_since != since { - self.tcx.sess.emit_err(FeatureStableTwice { - span, - feature, - since, - prev_since: *prev_since, - }); - return; - } + match (stability, existing_stability) { + (_, None) => { + self.lib_features.stability.insert(feature, (stability, span)); + } + ( + FeatureStability::AcceptedSince(since), + Some((FeatureStability::AcceptedSince(prev_since), _)), + ) => { + if prev_since != since { + self.tcx.sess.emit_err(FeatureStableTwice { span, feature, since, prev_since }); } - - self.lib_features.stable.insert(feature, (since, span)); } - (None, false, _) => { - self.lib_features.unstable.insert(feature, span); - } - (Some(_), _, true) | (None, true, _) => { - let declared = if since.is_some() { "stable" } else { "unstable" }; - let prev_declared = if since.is_none() { "stable" } else { "unstable" }; + (FeatureStability::AcceptedSince(_), Some((FeatureStability::Unstable, _))) => { self.tcx.sess.emit_err(FeaturePreviouslyDeclared { span, feature, - declared, - prev_declared, + declared: "stable", + prev_declared: "unstable", }); } + (FeatureStability::Unstable, Some((FeatureStability::AcceptedSince(_), _))) => { + self.tcx.sess.emit_err(FeaturePreviouslyDeclared { + span, + feature, + declared: "unstable", + prev_declared: "stable", + }); + } + // duplicate `unstable` feature is ok. + (FeatureStability::Unstable, Some((FeatureStability::Unstable, _))) => {} } } } @@ -137,11 +136,11 @@ impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> { } } -fn lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures { +fn lib_features(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> LibFeatures { // If `staged_api` is not enabled then we aren't allowed to define lib // features; there is no point collecting them. if !tcx.features().staged_api { - return new_lib_features(); + return LibFeatures::default(); } let mut collector = LibFeatureCollector::new(tcx); diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 018d42793b2b..03070dc6f28d 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,11 +9,12 @@ use rustc_attr::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{LocalDefId, LocalModDefId, CRATE_DEF_ID}; +use rustc_hir::def_id::{LocalDefId, LocalModDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; +use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index}; use rustc_middle::query::Providers; @@ -978,29 +979,27 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { tcx: TyCtxt<'tcx>, remaining_lib_features: &mut FxIndexMap<&Symbol, Span>, remaining_implications: &mut FxHashMap, - defined_features: &[(Symbol, Option)], + defined_features: &LibFeatures, all_implications: &FxHashMap, ) { - for (feature, since) in defined_features { - if let Some(since) = since + for (feature, since) in defined_features.to_vec() { + if let FeatureStability::AcceptedSince(since) = since && let Some(span) = remaining_lib_features.get(&feature) { // Warn if the user has enabled an already-stable lib feature. if let Some(implies) = all_implications.get(&feature) { - unnecessary_partially_stable_feature_lint( - tcx, *span, *feature, *implies, *since, - ); + unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since); } else { - unnecessary_stable_feature_lint(tcx, *span, *feature, *since); + unnecessary_stable_feature_lint(tcx, *span, feature, since); } } - remaining_lib_features.remove(feature); + remaining_lib_features.remove(&feature); // `feature` is the feature doing the implying, but `implied_by` is the feature with // the attribute that establishes this relationship. `implied_by` is guaranteed to be a // feature defined in the local crate because `remaining_implications` is only the // implications from this crate. - remaining_implications.remove(feature); + remaining_implications.remove(&feature); if remaining_lib_features.is_empty() && remaining_implications.is_empty() { break; @@ -1009,12 +1008,11 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } // All local crate implications need to have the feature that implies it confirmed to exist. - let mut remaining_implications = - tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); + let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone(); // We always collect the lib features declared in the current crate, even if there are // no unknown features, because the collection also does feature attribute validation. - let local_defined_features = tcx.lib_features(()).to_vec(); + let local_defined_features = tcx.lib_features(LOCAL_CRATE); if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() { // Loading the implications of all crates is unavoidable to be able to emit the partial // stabilization diagnostic, but it can be avoided when there are no @@ -1028,7 +1026,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { tcx, &mut remaining_lib_features, &mut remaining_implications, - local_defined_features.as_slice(), + local_defined_features, &all_implications, ); @@ -1040,7 +1038,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { tcx, &mut remaining_lib_features, &mut remaining_implications, - tcx.defined_lib_features(cnum).to_vec().as_slice(), + tcx.lib_features(cnum), &all_implications, ); } @@ -1051,13 +1049,12 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } for (implied_by, feature) in remaining_implications { - let local_defined_features = tcx.lib_features(()); - let span = *local_defined_features - .stable + let local_defined_features = tcx.lib_features(LOCAL_CRATE); + let span = local_defined_features + .stability .get(&feature) - .map(|(_, span)| span) - .or_else(|| local_defined_features.unstable.get(&feature)) - .expect("feature that implied another does not exist"); + .expect("feature that implied another does not exist") + .1; tcx.sess.emit_err(errors::ImpliedFeatureNotExist { span, feature, implied_by }); } diff --git a/compiler/rustc_query_system/src/dep_graph/edges.rs b/compiler/rustc_query_system/src/dep_graph/edges.rs index 6ba3924f65eb..400f128d5833 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 6cace01955e7..831062b1678f 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 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 { +pub(crate) struct DepGraphData { /// 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 DepGraph { } #[inline] - pub fn data(&self) -> Option<&DepGraphData> { + pub(crate) fn data(&self) -> Option<&DepGraphData> { self.data.as_deref() } @@ -333,7 +332,7 @@ impl DepGraphData { /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html #[inline(always)] - pub fn with_task, A: Debug, R>( + pub(crate) fn with_task, A: Debug, R>( &self, key: DepNode, cx: Ctxt, @@ -398,7 +397,7 @@ impl DepGraphData { /// 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, OP, R>( + pub(crate) fn with_anon_task, OP, R>( &self, cx: Tcx, dep_kind: DepKind, @@ -618,7 +617,7 @@ impl DepGraph { impl DepGraphData { #[inline] - pub fn dep_node_index_of_opt(&self, dep_node: &DepNode) -> Option { + fn dep_node_index_of_opt(&self, dep_node: &DepNode) -> Option { 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 DepGraphData { } #[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 DepGraphData { /// 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 DepGraph { 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(&self, dep_node: DepNode, debug_str_gen: F) + pub(crate) fn register_dep_node_debug_str(&self, dep_node: DepNode, debug_str_gen: F) where F: FnOnce() -> String, { @@ -725,7 +725,7 @@ impl DepGraphData { /// 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>( + pub(crate) fn try_mark_green>( &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 624ae680a8ff..feb69ecd0786 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 fcf46be6e6f5..e97ef8072045 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 5593a15412fb..eec0433ae68d 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>, - 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/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index b2177be0e368..81a7eb604f5a 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -123,5 +123,3 @@ impl<'tcx> HashStable> for rustc_feature::Features { }); } } - -impl<'ctx> rustc_type_ir::HashStableContext for StableHashingContext<'ctx> {} diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 1944ac443eae..2ed420f35643 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 93db6cfc4635..84225bbe39b2 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) + } } } @@ -2596,6 +2608,7 @@ fn show_candidates( path_strings.extend(core_path_strings); path_strings.dedup_by(|a, b| a.0 == b.0); } + accessible_path_strings.sort(); if !accessible_path_strings.is_empty() { let (determiner, kind, name, through) = diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3be962dab90b..9b7897d69e78 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 : ;`. current_let_binding: Option<(Span, Option, Option)>, + 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 fd5d6fabf021..d62d7fdcae0c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,11 +1,13 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet}; +use crate::ty::fast_reject::SimplifiedType; use crate::{errors, path_names_to_string}; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; use rustc_hir::def::Namespace::{self, *}; +use rustc_ast::ptr::P; use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, @@ -15,7 +17,7 @@ use rustc_ast_pretty::pprust::where_bound_predicate_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, + MultiSpan, SuggestionStyle, }; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; @@ -29,6 +31,8 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; +use rustc_middle::ty; + use std::borrow::Cow; use std::iter; use std::ops::Deref; @@ -431,6 +435,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 +1068,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, @@ -1304,7 +1335,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); - let path_sep = |err: &mut Diagnostic, expr: &Expr, kind: DefKind| { + let path_sep = |this: &mut Self, err: &mut Diagnostic, expr: &Expr, kind: DefKind| { const MESSAGE: &str = "use the path separator to refer to an item"; let (lhs_span, rhs_span) = match &expr.kind { @@ -1325,7 +1356,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { true } else if kind == DefKind::Struct && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span) - && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(lhs_source_span) + && let Ok(snippet) = this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span) { // The LHS is a type that originates from a macro call. // We have to add angle brackets around it. @@ -1360,13 +1391,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } }; - let mut bad_struct_syntax_suggestion = |def_id: DefId| { - let (followed_by_brace, closing_brace) = self.followed_by_brace(span); + let mut bad_struct_syntax_suggestion = |this: &mut Self, def_id: DefId| { + let (followed_by_brace, closing_brace) = this.followed_by_brace(span); match source { PathSource::Expr(Some( parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. }, - )) if path_sep(err, &parent, DefKind::Struct) => {} + )) if path_sep(this, err, &parent, DefKind::Struct) => {} PathSource::Expr( None | Some(Expr { @@ -1403,7 +1434,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => { let span = find_span(&source, err); - err.span_label(self.r.def_span(def_id), format!("`{path_str}` defined here")); + err.span_label(this.r.def_span(def_id), format!("`{path_str}` defined here")); let (tail, descr, applicability, old_fields) = match source { PathSource::Pat => ("", "pattern", Applicability::MachineApplicable, None), @@ -1413,50 +1444,69 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { Applicability::MachineApplicable, Some( args.iter() - .map(|a| self.r.tcx.sess.source_map().span_to_snippet(*a).ok()) + .map(|a| this.r.tcx.sess.source_map().span_to_snippet(*a).ok()) .collect::>>(), ), ), _ => (": val", "literal", Applicability::HasPlaceholders, None), }; - let field_ids = self.r.field_def_ids(def_id); - let (fields, applicability) = match field_ids { - Some(field_ids) => { - let fields = field_ids.iter().map(|&id| self.r.tcx.item_name(id)); - let fields = if let Some(old_fields) = old_fields { - fields - .enumerate() - .map(|(idx, new)| (new, old_fields.get(idx))) - .map(|(new, old)| { - let new = new.to_ident_string(); - if let Some(Some(old)) = old - && new != *old - { - format!("{new}: {old}") - } else { - new - } - }) - .collect::>() - } else { - fields.map(|f| format!("{f}{tail}")).collect::>() - }; + if !this.has_private_fields(def_id) { + // If the fields of the type are private, we shouldn't be suggesting using + // the struct literal syntax at all, as that will cause a subsequent error. + let field_ids = this.r.field_def_ids(def_id); + let (fields, applicability) = match field_ids { + Some(field_ids) => { + let fields = field_ids.iter().map(|&id| this.r.tcx.item_name(id)); - (fields.join(", "), applicability) - } - None => ("/* fields */".to_string(), Applicability::HasPlaceholders), - }; - let pad = match field_ids { - Some(field_ids) if field_ids.is_empty() => "", - _ => " ", - }; - err.span_suggestion( - span, - format!("use struct {descr} syntax instead"), - format!("{path_str} {{{pad}{fields}{pad}}}"), - applicability, - ); + let fields = if let Some(old_fields) = old_fields { + fields + .enumerate() + .map(|(idx, new)| (new, old_fields.get(idx))) + .map(|(new, old)| { + let new = new.to_ident_string(); + if let Some(Some(old)) = old + && new != *old + { + format!("{new}: {old}") + } else { + new + } + }) + .collect::>() + } else { + fields.map(|f| format!("{f}{tail}")).collect::>() + }; + + (fields.join(", "), applicability) + } + None => ("/* fields */".to_string(), Applicability::HasPlaceholders), + }; + let pad = match field_ids { + Some(field_ids) if field_ids.is_empty() => "", + _ => " ", + }; + err.span_suggestion( + span, + format!("use struct {descr} syntax instead"), + format!("{path_str} {{{pad}{fields}{pad}}}"), + applicability, + ); + } + if let PathSource::Expr(Some(Expr { + kind: ExprKind::Call(path, ref args), + span: call_span, + .. + })) = source + { + this.suggest_alternative_construction_methods( + def_id, + err, + path.span, + *call_span, + &args[..], + ); + } } _ => { err.span_label(span, fallback_label.to_string()); @@ -1506,7 +1556,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { Res::Def(kind @ (DefKind::Mod | DefKind::Trait), _), PathSource::Expr(Some(parent)), ) => { - if !path_sep(err, &parent, kind) { + if !path_sep(self, err, &parent, kind) { return false; } } @@ -1540,13 +1590,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor { if let PathSource::Expr(Some(parent)) = source { if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind { - bad_struct_syntax_suggestion(def_id); + bad_struct_syntax_suggestion(self, def_id); return true; } } struct_ctor } else { - bad_struct_syntax_suggestion(def_id); + bad_struct_syntax_suggestion(self, def_id); return true; }; @@ -1566,30 +1616,21 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { Some(Vec::from(pattern_spans)) } // e.g. `let _ = Enum::TupleVariant(field1, field2);` - _ if source.is_call() => { + PathSource::Expr(Some(Expr { + kind: ExprKind::Call(path, ref args), + span: call_span, + .. + })) => { err.set_primary_message( "cannot initialize a tuple struct which contains private fields", ); - if !def_id.is_local() - && self - .r - .tcx - .inherent_impls(def_id) - .iter() - .flat_map(|impl_def_id| { - self.r.tcx.provided_trait_methods(*impl_def_id) - }) - .any(|assoc| !assoc.fn_has_self_parameter && assoc.name == sym::new) - { - // FIXME: look for associated functions with Self return type, - // instead of relying only on the name and lack of self receiver. - err.span_suggestion_verbose( - span.shrink_to_hi(), - "you might have meant to use the `new` associated function", - "::new".to_string(), - Applicability::MaybeIncorrect, - ); - } + self.suggest_alternative_construction_methods( + def_id, + err, + path.span, + *call_span, + &args[..], + ); // Use spans of the tuple struct definition. self.r.field_def_ids(def_id).map(|field_ids| { field_ids @@ -1636,7 +1677,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { err.span_label(span, "constructor is not visible here due to private fields"); } (Res::Def(DefKind::Union | DefKind::Variant, def_id), _) if ns == ValueNS => { - bad_struct_syntax_suggestion(def_id); + bad_struct_syntax_suggestion(self, def_id); } (Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => { match source { @@ -1682,6 +1723,161 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { true } + fn suggest_alternative_construction_methods( + &mut self, + def_id: DefId, + err: &mut Diagnostic, + path_span: Span, + call_span: Span, + args: &[P], + ) { + if def_id.is_local() { + // Doing analysis on local `DefId`s would cause infinite recursion. + return; + } + // Look at all the associated functions without receivers in the type's + // inherent impls to look for builders that return `Self` + let mut items = self + .r + .tcx + .inherent_impls(def_id) + .iter() + .flat_map(|i| self.r.tcx.associated_items(i).in_definition_order()) + // Only assoc fn with no receivers. + .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter) + .filter_map(|item| { + // Only assoc fns that return `Self` + let fn_sig = self.r.tcx.fn_sig(item.def_id).skip_binder(); + let ret_ty = fn_sig.output(); + let ret_ty = self + .r + .tcx + .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), ret_ty); + let ty::Adt(def, _args) = ret_ty.kind() else { + return None; + }; + let input_len = fn_sig.inputs().skip_binder().len(); + if def.did() != def_id { + return None; + } + let order = !item.name.as_str().starts_with("new"); + Some((order, item.name, input_len)) + }) + .collect::>(); + items.sort_by_key(|(order, _, _)| *order); + let suggestion = |name, args| { + format!( + "::{name}({})", + std::iter::repeat("_").take(args).collect::>().join(", ") + ) + }; + match &items[..] { + [] => {} + [(_, name, len)] if *len == args.len() => { + err.span_suggestion_verbose( + path_span.shrink_to_hi(), + format!("you might have meant to use the `{name}` associated function",), + format!("::{name}"), + Applicability::MaybeIncorrect, + ); + } + [(_, name, len)] => { + err.span_suggestion_verbose( + path_span.shrink_to_hi().with_hi(call_span.hi()), + format!("you might have meant to use the `{name}` associated function",), + suggestion(name, *len), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_suggestions_with_style( + path_span.shrink_to_hi().with_hi(call_span.hi()), + "you might have meant to use an associated function to build this type", + items + .iter() + .map(|(_, name, len)| suggestion(name, *len)) + .collect::>(), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + } + } + // We'd ideally use `type_implements_trait` but don't have access to + // the trait solver here. We can't use `get_diagnostic_item` or + // `all_traits` in resolve either. So instead we abuse the import + // suggestion machinery to get `std::default::Default` and perform some + // checks to confirm that we got *only* that trait. We then see if the + // Adt we have has a direct implementation of `Default`. If so, we + // provide a structured suggestion. + let default_trait = self + .r + .lookup_import_candidates( + Ident::with_dummy_span(sym::Default), + Namespace::TypeNS, + &self.parent_scope, + &|res: Res| matches!(res, Res::Def(DefKind::Trait, _)), + ) + .iter() + .filter_map(|candidate| candidate.did) + .filter(|did| { + self.r + .tcx + .get_attrs(*did, sym::rustc_diagnostic_item) + .any(|attr| attr.value_str() == Some(sym::Default)) + }) + .next(); + let Some(default_trait) = default_trait else { + return; + }; + if self + .r + .extern_crate_map + .iter() + // FIXME: This doesn't include impls like `impl Default for String`. + .flat_map(|(_, crate_)| self.r.tcx.implementations_of_trait((*crate_, default_trait))) + .filter_map(|(_, simplified_self_ty)| *simplified_self_ty) + .filter_map(|simplified_self_ty| match simplified_self_ty { + SimplifiedType::Adt(did) => Some(did), + _ => None, + }) + .any(|did| did == def_id) + { + err.multipart_suggestion( + "consider using the `Default` trait", + vec![ + (path_span.shrink_to_lo(), "<".to_string()), + ( + path_span.shrink_to_hi().with_hi(call_span.hi()), + " as std::default::Default>::default()".to_string(), + ), + ], + Applicability::MaybeIncorrect, + ); + } + } + + fn has_private_fields(&self, def_id: DefId) -> bool { + let fields = match def_id.as_local() { + Some(def_id) => self.r.struct_constructors.get(&def_id).cloned().map(|(_, _, f)| f), + None => Some( + self.r + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|field_id| self.r.tcx.visibility(field_id)) + .collect(), + ), + }; + + fields.map_or(false, |fields| { + fields + .iter() + .filter(|vis| !self.r.is_accessible_from(**vis, self.parent_scope.module)) + .next() + .is_some() + }) + } + /// Given the target `ident` and `kind`, search for the similarly named associated item /// in `self.current_trait_ref`. pub(crate) fn find_similarly_named_assoc_item( @@ -2062,11 +2258,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if suggest_only_tuple_variants { // Suggest only tuple variants regardless of whether they have fields and do not // suggest path with added parentheses. - let suggestable_variants = variants + let mut suggestable_variants = variants .iter() .filter(|(.., kind)| *kind == CtorKind::Fn) .map(|(variant, ..)| path_names_to_string(variant)) .collect::>(); + suggestable_variants.sort(); let non_suggestable_variant_count = variants.len() - suggestable_variants.len(); @@ -2117,7 +2314,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } }; - let suggestable_variants = variants + let mut suggestable_variants = variants .iter() .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind)) .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) @@ -2126,6 +2323,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { CtorKind::Fn => format!("({variant}())"), }) .collect::>(); + suggestable_variants.sort(); let no_suggestable_variant = suggestable_variants.is_empty(); if !no_suggestable_variant { @@ -2143,7 +2341,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ); } - let suggestable_variants_with_placeholders = variants + let mut suggestable_variants_with_placeholders = variants .iter() .filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind)) .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) @@ -2152,6 +2350,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { _ => None, }) .collect::>(); + suggestable_variants_with_placeholders.sort(); if !suggestable_variants_with_placeholders.is_empty() { let msg = diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index add40b83d21d..d4f9122e7e38 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/session.rs b/compiler/rustc_session/src/session.rs index e9655a5587db..38e09f47eacd 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -16,7 +16,9 @@ use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::jobserver::{self, Client}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_data_structures::sync::{AtomicU64, Lock, Lrc, OneThread, Ordering::SeqCst}; +use rustc_data_structures::sync::{ + AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread, Ordering::SeqCst, +}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{DynEmitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; @@ -37,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; @@ -167,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>, + + /// 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, @@ -1483,6 +1495,8 @@ pub fn build_session( 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_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 7cfdbbbf7037..147d683b51f1 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::{ + AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, FloatTy, + GenericArgKind, GenericArgs, IntTy, Region, RigidTy, TraitRef, Ty, UintTy, +}; +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 } } @@ -50,6 +63,82 @@ impl<'tcx> RustcInternal<'tcx> for Ty { } } +impl<'tcx> RustcInternal<'tcx> for RigidTy { + type T = rustc_ty::TyKind<'tcx>; + + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + RigidTy::Bool => rustc_ty::TyKind::Bool, + RigidTy::Char => rustc_ty::TyKind::Char, + RigidTy::Int(int_ty) => rustc_ty::TyKind::Int(int_ty.internal(tables)), + RigidTy::Uint(uint_ty) => rustc_ty::TyKind::Uint(uint_ty.internal(tables)), + RigidTy::Float(float_ty) => rustc_ty::TyKind::Float(float_ty.internal(tables)), + RigidTy::Never => rustc_ty::TyKind::Never, + RigidTy::Array(ty, cnst) => { + rustc_ty::TyKind::Array(ty.internal(tables), ty_const(cnst, tables)) + } + RigidTy::Adt(def, args) => { + rustc_ty::TyKind::Adt(def.internal(tables), args.internal(tables)) + } + RigidTy::Str => rustc_ty::TyKind::Str, + RigidTy::Slice(ty) => rustc_ty::TyKind::Slice(ty.internal(tables)), + RigidTy::RawPtr(..) + | RigidTy::Ref(..) + | RigidTy::Foreign(_) + | RigidTy::FnDef(_, _) + | RigidTy::FnPtr(_) + | RigidTy::Closure(..) + | RigidTy::Coroutine(..) + | RigidTy::CoroutineWitness(..) + | RigidTy::Dynamic(..) + | RigidTy::Tuple(..) => { + todo!() + } + } + } +} + +impl<'tcx> RustcInternal<'tcx> for IntTy { + type T = rustc_ty::IntTy; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + IntTy::Isize => rustc_ty::IntTy::Isize, + IntTy::I8 => rustc_ty::IntTy::I8, + IntTy::I16 => rustc_ty::IntTy::I16, + IntTy::I32 => rustc_ty::IntTy::I32, + IntTy::I64 => rustc_ty::IntTy::I64, + IntTy::I128 => rustc_ty::IntTy::I128, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for UintTy { + type T = rustc_ty::UintTy; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + UintTy::Usize => rustc_ty::UintTy::Usize, + UintTy::U8 => rustc_ty::UintTy::U8, + UintTy::U16 => rustc_ty::UintTy::U16, + UintTy::U32 => rustc_ty::UintTy::U32, + UintTy::U64 => rustc_ty::UintTy::U64, + UintTy::U128 => rustc_ty::UintTy::U128, + } + } +} + +impl<'tcx> RustcInternal<'tcx> for FloatTy { + type T = rustc_ty::FloatTy; + + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + match self { + FloatTy::F32 => rustc_ty::FloatTy::F32, + FloatTy::F64 => rustc_ty::FloatTy::F64, + } + } +} + fn ty_const<'tcx>(constant: &Const, tables: &mut Tables<'tcx>) -> rustc_ty::Const<'tcx> { match constant.internal(tables) { rustc_middle::mir::Const::Ty(c) => c, @@ -65,3 +154,125 @@ 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 +where + T: RustcInternal<'tcx>, + T::T: rustc_ty::TypeVisitable>, +{ + 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> RustcInternal<'tcx> for AdtDef { + type T = rustc_ty::AdtDef<'tcx>; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.tcx.adt_def(self.0.internal(&mut *tables)) + } +} + +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 f0b368bec39c..fa75fd3076ce 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)) } @@ -144,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. @@ -165,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(tcx: TyCtxt<'_>, f: F) -> Result +where + F: FnOnce() -> T, +{ let tables = TablesWrapper(RefCell::new(Tables { tcx, def_ids: IndexMap::default(), @@ -175,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] @@ -241,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 000000000000..3ef2d28ea473 --- /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::>(); + }); + Ok(()) +} diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 8ff3958da7bd..7e74a1d92c76 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 8845320ca8b7..167a01ab7990 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,23 +7,24 @@ //! //! 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, EarlyParamRegion, 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; -use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt, Variance}; +use rustc_middle::ty::{self, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, Variance}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; 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, Error, Filename, ItemKind}; use std::cell::RefCell; use tracing::debug; @@ -85,9 +86,28 @@ 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 is_foreign_item(&self, item: CrateItem) -> bool { + let tables = self.0.borrow(); + tables.tcx.is_foreign_item(tables[item.0]) + } + + fn adt_kind(&self, def: AdtDef) -> AdtKind { let mut tables = self.0.borrow_mut(); - tables.tcx.def_kind(tables[def_id]).stable(&mut *tables) + def.internal(&mut *tables).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.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 +218,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn instance_body(&self, def: InstanceDef) -> Body { + fn instance_body(&self, def: InstanceDef) -> Option { 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 +271,69 @@ 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 { + 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 { + 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) + } + + fn adt_is_box(&self, def: AdtDef) -> bool { + let mut tables = self.0.borrow_mut(); + def.internal(&mut *tables).is_box() + } + + fn eval_target_usize(&self, cnst: &Const) -> Result { + let mut tables = self.0.borrow_mut(); + let mir_const = cnst.internal(&mut *tables); + mir_const + .try_eval_target_usize(tables.tcx, ParamEnv::empty()) + .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) + } + + fn usize_to_const(&self, val: u64) -> Result { + let mut tables = self.0.borrow_mut(); + let ty = tables.tcx.types.usize; + let size = tables.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap().size; + + let scalar = ScalarInt::try_from_uint(val, size).ok_or_else(|| { + Error::new(format!("Value overflow: cannot convert `{val}` to usize.")) + })?; + Ok(ty::Const::new_value(tables.tcx, ty::ValTree::from_scalar_int(scalar), ty) + .stable(&mut *tables)) + } + + fn new_rigid_ty(&self, kind: RigidTy) -> stable_mir::ty::Ty { + let mut tables = self.0.borrow_mut(); + let internal_kind = kind.internal(&mut *tables); + tables.tcx.mk_ty_from_kind(internal_kind).stable(&mut *tables) + } } pub(crate) struct TablesWrapper<'tcx>(pub(crate) RefCell>); @@ -271,6 +356,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 +377,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 +440,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, @@ -926,6 +1057,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 { @@ -1062,8 +1205,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()) } } @@ -1486,7 +1627,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() } } @@ -1762,15 +1903,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; @@ -1805,3 +1937,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 +where + T: Stable<'tcx>, +{ + type T = Option; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + self.as_ref().map(|value| value.stable(tables)) + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 302be85a429f..2e3b54464050 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1164,7 +1164,6 @@ symbols! { optin_builtin_traits, option, option_env, - option_payload_ptr, options, or, or_patterns, diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs index b4c7b0f120f9..f99f6a3b7216 100644 --- a/compiler/rustc_target/src/abi/call/aarch64.rs +++ b/compiler/rustc_target/src/abi/call/aarch64.rs @@ -40,6 +40,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !ret.layout.is_sized() { + // Not touching this... + return; + } if !ret.layout.is_aggregate() { if kind == AbiKind::DarwinPCS { // On Darwin, when returning an i8/i16, it must be sign-extended to 32 bits, @@ -67,6 +71,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if !arg.layout.is_aggregate() { if kind == AbiKind::DarwinPCS { // On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits, diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs index 1923ea58838b..95f6691d42ae 100644 --- a/compiler/rustc_target/src/abi/call/arm.rs +++ b/compiler/rustc_target/src/abi/call/arm.rs @@ -30,6 +30,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !ret.layout.is_sized() { + // Not touching this... + return; + } if !ret.layout.is_aggregate() { ret.extend_integer_width_to(32); return; @@ -56,6 +60,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if !arg.layout.is_aggregate() { arg.extend_integer_width_to(32); return; diff --git a/compiler/rustc_target/src/abi/call/csky.rs b/compiler/rustc_target/src/abi/call/csky.rs index 706493b0a6a6..8b4328db52eb 100644 --- a/compiler/rustc_target/src/abi/call/csky.rs +++ b/compiler/rustc_target/src/abi/call/csky.rs @@ -7,6 +7,10 @@ use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; fn classify_ret(arg: &mut ArgAbi<'_, Ty>) { + if !arg.layout.is_sized() { + // Not touching this... + return; + } // For return type, aggregate which <= 2*XLen will be returned in registers. // Otherwise, aggregate will be returned indirectly. if arg.layout.is_aggregate() { @@ -24,6 +28,10 @@ fn classify_ret(arg: &mut ArgAbi<'_, Ty>) { } fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { + if !arg.layout.is_sized() { + // Not touching this... + return; + } // For argument type, the first 4*XLen parts of aggregate will be passed // in registers, and the rest will be passed in stack. // So we can coerce to integers directly and let backend handle it correctly. diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs index e649d58bbcab..647b6500c52d 100644 --- a/compiler/rustc_target/src/abi/call/loongarch.rs +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -152,6 +152,10 @@ fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u6 where Ty: TyAbiInterface<'a, C> + Copy, { + if !arg.layout.is_sized() { + // Not touching this... + return false; // I guess? return value of this function is not documented + } if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { match conv { FloatConv::Float(f) => { @@ -214,6 +218,10 @@ fn classify_arg<'a, Ty, C>( ) where Ty: TyAbiInterface<'a, C> + Copy, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if !is_vararg { match should_use_fp_conv(cx, &arg.layout, xlen, flen) { Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { diff --git a/compiler/rustc_target/src/abi/call/m68k.rs b/compiler/rustc_target/src/abi/call/m68k.rs index 1d4649ed8678..06697bdd83ee 100644 --- a/compiler/rustc_target/src/abi/call/m68k.rs +++ b/compiler/rustc_target/src/abi/call/m68k.rs @@ -9,6 +9,10 @@ fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { } fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if arg.layout.is_aggregate() { arg.make_indirect_byval(None); } else { diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/abi/call/mips.rs index edcd1bab8b4b..57ccfe2152bd 100644 --- a/compiler/rustc_target/src/abi/call/mips.rs +++ b/compiler/rustc_target/src/abi/call/mips.rs @@ -17,6 +17,10 @@ fn classify_arg(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size) where C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } let dl = cx.data_layout(); let size = arg.layout.size; let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 5efd171b9dd7..f7c860cf56b9 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -422,7 +422,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { })) } - Abi::ScalarPair(..) | Abi::Aggregate { .. } => { + Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => { // Helper for computing `homogeneous_aggregate`, allowing a custom // starting offset (used below for handling variants). let from_fields_at = @@ -520,6 +520,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ok(result) } } + Abi::Aggregate { sized: false } => Err(Heterogeneous), } } } @@ -555,8 +556,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)), ), Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()), - // The `Aggregate` ABI should always be adjusted later. - Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()), + Abi::Aggregate { .. } => Self::indirect_pass_mode(&layout), }; ArgAbi { layout, mode } } @@ -580,14 +580,30 @@ impl<'a, Ty> ArgAbi<'a, Ty> { PassMode::Indirect { attrs, meta_attrs, on_stack: false } } + /// Pass this argument directly instead. Should NOT be used! + /// Only exists because of past ABI mistakes that will take time to fix + /// (see ). + pub fn make_direct_deprecated(&mut self) { + match self.mode { + PassMode::Indirect { .. } => { + self.mode = PassMode::Direct(ArgAttributes::new()); + } + PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => return, // already direct + _ => panic!("Tried to make {:?} direct", self.mode), + } + } + pub fn make_indirect(&mut self) { match self.mode { - PassMode::Direct(_) | PassMode::Pair(_, _) => {} - PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: false } => return, + PassMode::Direct(_) | PassMode::Pair(_, _) => { + self.mode = Self::indirect_pass_mode(&self.layout); + } + PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false } => { + // already indirect + return; + } _ => panic!("Tried to make {:?} indirect", self.mode), } - - self.mode = Self::indirect_pass_mode(&self.layout); } pub fn make_indirect_byval(&mut self, byval_align: Option) { @@ -836,7 +852,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/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs index 4abe51cd697e..5c040ce9c3b1 100644 --- a/compiler/rustc_target/src/abi/call/nvptx64.rs +++ b/compiler/rustc_target/src/abi/call/nvptx64.rs @@ -4,12 +4,18 @@ use crate::abi::{HasDataLayout, TyAbiInterface}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { ret.make_indirect(); + } else { + // FIXME: this is wrong! Need to decide which ABI we really want here. + ret.make_direct_deprecated(); } } fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 { arg.make_indirect(); + } else { + // FIXME: this is wrong! Need to decide which ABI we really want here. + arg.make_direct_deprecated(); } } @@ -30,6 +36,9 @@ where _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), }; arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) }); + } else { + // FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271. + arg.make_direct_deprecated(); } } diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs index 359bb8fc09a8..2d41f77e50e1 100644 --- a/compiler/rustc_target/src/abi/call/powerpc64.rs +++ b/compiler/rustc_target/src/abi/call/powerpc64.rs @@ -46,6 +46,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !ret.layout.is_sized() { + // Not touching this... + return; + } if !ret.layout.is_aggregate() { ret.extend_integer_width_to(64); return; @@ -89,6 +93,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if !arg.layout.is_aggregate() { arg.extend_integer_width_to(64); return; diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs index 93a2045632a8..cbde234d34cc 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -158,6 +158,10 @@ fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u6 where Ty: TyAbiInterface<'a, C> + Copy, { + if !arg.layout.is_sized() { + // Not touching this... + return false; // I guess? return value of this function is not documented + } if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { match conv { FloatConv::Float(f) => { @@ -220,6 +224,10 @@ fn classify_arg<'a, Ty, C>( ) where Ty: TyAbiInterface<'a, C> + Copy, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if !is_vararg { match should_use_fp_conv(cx, &arg.layout, xlen, flen) { Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/abi/call/s390x.rs index ea23692817f5..1a2191082d5d 100644 --- a/compiler/rustc_target/src/abi/call/s390x.rs +++ b/compiler/rustc_target/src/abi/call/s390x.rs @@ -17,6 +17,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 { arg.extend_integer_width_to(64); return; diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/abi/call/sparc.rs index edcd1bab8b4b..57ccfe2152bd 100644 --- a/compiler/rustc_target/src/abi/call/sparc.rs +++ b/compiler/rustc_target/src/abi/call/sparc.rs @@ -17,6 +17,10 @@ fn classify_arg(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size) where C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } let dl = cx.data_layout(); let size = arg.layout.size; let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs index 796b752ff9d4..a7a2b314a94f 100644 --- a/compiler/rustc_target/src/abi/call/wasm.rs +++ b/compiler/rustc_target/src/abi/call/wasm.rs @@ -34,6 +34,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { + if !arg.layout.is_sized() { + // Not touching this... + return; + } arg.extend_integer_width_to(32); if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) { arg.make_indirect_byval(None); @@ -67,21 +71,33 @@ where /// Also see . pub fn compute_wasm_abi_info(fn_abi: &mut FnAbi<'_, Ty>) { if !fn_abi.ret.is_ignore() { - classify_ret(&mut fn_abi.ret); + classify_ret_wasm_abi(&mut fn_abi.ret); } for arg in fn_abi.args.iter_mut() { if arg.is_ignore() { continue; } - classify_arg(arg); + classify_arg_wasm_abi(arg); } - fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { + fn classify_ret_wasm_abi(ret: &mut ArgAbi<'_, Ty>) { + if !ret.layout.is_sized() { + // Not touching this... + return; + } + // FIXME: this is bad! https://github.com/rust-lang/rust/issues/115666 + ret.make_direct_deprecated(); ret.extend_integer_width_to(32); } - fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { + fn classify_arg_wasm_abi(arg: &mut ArgAbi<'_, Ty>) { + if !arg.layout.is_sized() { + // Not touching this... + return; + } + // FIXME: this is bad! https://github.com/rust-lang/rust/issues/115666 + arg.make_direct_deprecated(); arg.extend_integer_width_to(32); } } diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index c27f1e6dddab..e9aedc3d28a1 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -14,7 +14,7 @@ where C: HasDataLayout + HasTargetSpec, { if !fn_abi.ret.is_ignore() { - if fn_abi.ret.layout.is_aggregate() { + if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() { // Returning a structure. Most often, this will use // a hidden first argument. On some platforms, though, // small structs are returned as integers. @@ -50,7 +50,7 @@ where } for arg in fn_abi.args.iter_mut() { - if arg.is_ignore() { + if arg.is_ignore() || !arg.layout.is_sized() { continue; } diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index d1efe9776992..6c34585a11b8 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -153,9 +153,9 @@ fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option], size: Size) -> Option { +fn cast_target(cls: &[Option], size: Size) -> CastTarget { let mut i = 0; - let lo = reg_component(cls, &mut i, size)?; + let lo = reg_component(cls, &mut i, size).unwrap(); let offset = Size::from_bytes(8) * (i as u64); let mut target = CastTarget::from(lo); if size > offset { @@ -164,7 +164,7 @@ fn cast_target(cls: &[Option], size: Size) -> Option { } } assert_eq!(reg_component(cls, &mut i, Size::ZERO), None); - Some(target) + target } const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9 @@ -179,6 +179,10 @@ where let mut sse_regs = MAX_SSE_REGS; let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| { + if !arg.layout.is_sized() { + // Not touching this... + return; + } let mut cls_or_mem = classify_arg(cx, arg); if is_arg { @@ -227,9 +231,7 @@ where // split into sized chunks passed individually if arg.layout.is_aggregate() { let size = arg.layout.size; - if let Some(cast_target) = cast_target(cls, size) { - arg.cast_to(cast_target); - } + arg.cast_to(cast_target(cls, size)); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/x86_win64.rs b/compiler/rustc_target/src/abi/call/x86_win64.rs index 1aaf0e511ca4..90de1a42bc06 100644 --- a/compiler/rustc_target/src/abi/call/x86_win64.rs +++ b/compiler/rustc_target/src/abi/call/x86_win64.rs @@ -6,8 +6,8 @@ use crate::abi::Abi; pub fn compute_abi_info(fn_abi: &mut FnAbi<'_, Ty>) { let fixup = |a: &mut ArgAbi<'_, Ty>| { match a.layout.abi { - Abi::Uninhabited => {} - Abi::ScalarPair(..) | Abi::Aggregate { .. } => match a.layout.size.bits() { + Abi::Uninhabited | Abi::Aggregate { sized: false } => {} + Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => match a.layout.size.bits() { 8 => a.cast_to(Reg::i8()), 16 => a.cast_to(Reg::i16()), 32 => a.cast_to(Reg::i32()), diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 99e64503e257..f32cb06d68f6 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -9,11 +9,12 @@ mod tests; use Arch::*; #[allow(non_camel_case_types)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] pub enum Arch { Armv7k, Armv7s, Arm64, + Arm64e, Arm64_32, I386, I686, @@ -31,6 +32,7 @@ impl Arch { Armv7k => "armv7k", Armv7s => "armv7s", Arm64 | Arm64_macabi | Arm64_sim => "arm64", + Arm64e => "arm64e", Arm64_32 => "arm64_32", I386 => "i386", I686 => "i686", @@ -42,7 +44,7 @@ impl Arch { pub fn target_arch(self) -> Cow<'static, str> { Cow::Borrowed(match self { Armv7k | Armv7s => "arm", - Arm64 | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64", + Arm64 | Arm64e | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64", I386 | I686 => "x86", X86_64 | X86_64_sim | X86_64_macabi | X86_64h => "x86_64", }) @@ -50,7 +52,7 @@ impl Arch { fn target_abi(self) -> &'static str { match self { - Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "", + Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => "", X86_64_macabi | Arm64_macabi => "macabi", // x86_64-apple-ios is a simulator target, even though it isn't // declared that way in the target like the other ones... @@ -63,6 +65,7 @@ impl Arch { Armv7k => "cortex-a8", Armv7s => "swift", // iOS 10 is only supported on iPhone 5 or higher. Arm64 => "apple-a7", + Arm64e => "apple-a12", Arm64_32 => "apple-s4", // Only macOS 10.12+ is supported, which means // all x86_64/x86 CPUs must be running at least penryn @@ -88,7 +91,7 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs { }; let platform_version: StaticCow = match os { - "ios" => ios_lld_platform_version(), + "ios" => ios_lld_platform_version(arch), "tvos" => tvos_lld_platform_version(), "watchos" => watchos_lld_platform_version(), "macos" => macos_lld_platform_version(arch), @@ -202,12 +205,22 @@ pub fn deployment_target(target: &Target) -> Option<(u32, u32)> { let (major, minor) = match &*target.os { "macos" => { // This does not need to be specific. It just needs to handle x86 vs M1. - let arch = if target.arch == "x86" || target.arch == "x86_64" { X86_64 } else { Arm64 }; + let arch = match target.arch.as_ref() { + "x86" | "x86_64" => X86_64, + "arm64e" => Arm64e, + _ => Arm64, + }; macos_deployment_target(arch) } "ios" => match &*target.abi { "macabi" => mac_catalyst_deployment_target(), - _ => ios_deployment_target(), + _ => { + let arch = match target.arch.as_ref() { + "arm64e" => Arm64e, + _ => Arm64, + }; + ios_deployment_target(arch) + } }, "watchos" => watchos_deployment_target(), "tvos" => tvos_deployment_target(), @@ -228,7 +241,7 @@ fn from_set_deployment_target(var_name: &str) -> Option<(u32, u32)> { fn macos_default_deployment_target(arch: Arch) -> (u32, u32) { match arch { // Note: Arm64_sim is not included since macOS has no simulator. - Arm64 | Arm64_macabi => (11, 0), + Arm64 | Arm64e | Arm64_macabi => (11, 0), _ => (10, 12), } } @@ -280,8 +293,8 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow]> // Otherwise if cross-compiling for a different OS/SDK, remove any part // of the linking environment that's wrong and reversed. match arch { - Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim | X86_64h - | Arm64_sim => { + Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim + | X86_64h | Arm64_sim => { cvs!["MACOSX_DEPLOYMENT_TARGET"] } X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"], @@ -289,9 +302,10 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow]> } } -fn ios_deployment_target() -> (u32, u32) { +fn ios_deployment_target(arch: Arch) -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. - from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((10, 0)) + let (major, minor) = if arch == Arm64e { (14, 0) } else { (10, 0) }; + from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((major, minor)) } fn mac_catalyst_deployment_target() -> (u32, u32) { @@ -306,17 +320,17 @@ pub fn ios_llvm_target(arch: Arch) -> String { // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode // to pick it up (since std and core are still built with the fallback // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION). - let (major, minor) = ios_deployment_target(); + let (major, minor) = ios_deployment_target(arch); format!("{}-apple-ios{}.{}.0", arch.target_name(), major, minor) } -fn ios_lld_platform_version() -> String { - let (major, minor) = ios_deployment_target(); +fn ios_lld_platform_version(arch: Arch) -> String { + let (major, minor) = ios_deployment_target(arch); format!("{major}.{minor}") } pub fn ios_sim_llvm_target(arch: Arch) -> String { - let (major, minor) = ios_deployment_target(); + let (major, minor) = ios_deployment_target(arch); format!("{}-apple-ios{}.{}.0-simulator", arch.target_name(), major, minor) } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index d8dd4ae2286d..9b0fedba0923 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1544,6 +1544,7 @@ supported_targets! { ("i686-unknown-hurd-gnu", i686_unknown_hurd_gnu), ("aarch64-apple-darwin", aarch64_apple_darwin), + ("arm64e-apple-darwin", arm64e_apple_darwin), ("x86_64-apple-darwin", x86_64_apple_darwin), ("x86_64h-apple-darwin", x86_64h_apple_darwin), ("i686-apple-darwin", i686_apple_darwin), @@ -1566,6 +1567,7 @@ supported_targets! { ("i386-apple-ios", i386_apple_ios), ("x86_64-apple-ios", x86_64_apple_ios), ("aarch64-apple-ios", aarch64_apple_ios), + ("arm64e-apple-ios", arm64e_apple_ios), ("armv7s-apple-ios", armv7s_apple_ios), ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi), ("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi), @@ -1585,7 +1587,6 @@ supported_targets! { ("armv7r-none-eabihf", armv7r_none_eabihf), ("x86_64-pc-solaris", x86_64_pc_solaris), - ("x86_64-sun-solaris", x86_64_sun_solaris), ("sparcv9-sun-solaris", sparcv9_sun_solaris), ("x86_64-unknown-illumos", x86_64_unknown_illumos), @@ -1609,7 +1610,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 +2244,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_target/src/spec/targets/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs index 9fc5b5de466b..f291ac5458d3 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs @@ -18,19 +18,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, frame_pointer: FramePointer::NonLeaf, - // Taken from a clang build on Xcode 11.4.1. - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - arm64-apple-ios11.0.0\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs index 0172a3a9c2e8..78067a138a93 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs @@ -17,17 +17,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a12".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, frame_pointer: FramePointer::NonLeaf, - // Taken from a clang build on Xcode 11.4.1. - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - arm64-apple-ios-macabi\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -Os\0" - .into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs index 602a687779e4..41760e9093fa 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs @@ -18,19 +18,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, frame_pointer: FramePointer::NonLeaf, - // Taken from a clang build on Xcode 11.4.1. - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - arm64-apple-ios14.0-simulator\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs index a1a31935509c..e817308b6859 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, frame_pointer: FramePointer::NonLeaf, ..opts("tvos", arch) }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs index 7b0bbb28e476..c4aa8479279d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs @@ -11,20 +11,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, frame_pointer: FramePointer::NonLeaf, - // Taken from (and slightly modified) the aarch64-apple-ios-sim spec which says: - // Taken from a clang build on Xcode 11.4.1. - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - arm64-apple-tvos15.0-simulator\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..opts("tvos", arch) }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs index 014560d2278b..96d43e6d27f1 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs @@ -15,19 +15,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, frame_pointer: FramePointer::NonLeaf, - // Taken from a clang build on Xcode 11.4.1. - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - arm64-apple-watchos5.0-simulator\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..opts("watchos", arch) }, } diff --git a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs index 9931b7b866cb..59e6022d985d 100644 --- a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs @@ -11,19 +11,8 @@ pub fn target() -> Target { options: TargetOptions { features: "+v8a,+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - forces_embed_bitcode: true, dynamic_linking: false, position_independent_executables: true, - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - arm64_32-apple-watchos5.0.0\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs new file mode 100644 index 000000000000..5d1f81158b69 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs @@ -0,0 +1,27 @@ +use crate::spec::base::apple::{macos_llvm_target, opts, Arch}; +use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; + +pub fn target() -> Target { + let arch = Arch::Arm64e; + let mut base = opts("macos", arch); + base.cpu = "apple-m1".into(); + base.max_atomic_width = Some(128); + + // FIXME: The leak sanitizer currently fails the tests, see #88132. + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD; + + Target { + // Clang automatically chooses a more specific target based on + // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work + // correctly, we do too. + llvm_target: macos_llvm_target(arch).into(), + pointer_width: 64, + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(), + arch: arch.target_arch(), + options: TargetOptions { + mcount: "\u{1}mcount".into(), + frame_pointer: FramePointer::NonLeaf, + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs new file mode 100644 index 000000000000..8daa78a02ed2 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs @@ -0,0 +1,30 @@ +use crate::spec::base::apple::{ios_llvm_target, opts, Arch}; +use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; + +pub fn target() -> Target { + let arch = Arch::Arm64e; + let mut base = opts("ios", arch); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD; + + Target { + llvm_target: ios_llvm_target(arch).into(), + pointer_width: 64, + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(), + arch: arch.target_arch(), + options: TargetOptions { + features: "+neon,+fp-armv8,+apple-a12,+v8.3a,+paca,+pacg".into(), + max_atomic_width: Some(128), + forces_embed_bitcode: true, + frame_pointer: FramePointer::NonLeaf, + bitcode_llvm_cmdline: "-triple\0\ + arm64e-apple-ios14.1.0\0\ + -emit-obj\0\ + -disable-llvm-passes\0\ + -target-abi\0\ + darwinpcs\0\ + -Os\0" + .into(), + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs index 751fdcb20f19..7ed71c1ba6e8 100644 --- a/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs @@ -11,19 +11,8 @@ pub fn target() -> Target { options: TargetOptions { features: "+v7,+vfp4,+neon".into(), max_atomic_width: Some(64), - forces_embed_bitcode: true, dynamic_linking: false, position_independent_executables: true, - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - armv7k-apple-watchos3.0.0\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..opts("watchos", arch) }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs index 258148677fb8..9faf9b1ca3f7 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs @@ -12,18 +12,6 @@ pub fn target() -> Target { options: TargetOptions { max_atomic_width: Some(128), stack_probes: StackProbeType::X86, - forces_embed_bitcode: true, - // Taken from a clang build on Xcode 11.4.1. - // These arguments are not actually invoked - they just have - // to look right to pass App Store validation. - bitcode_llvm_cmdline: "-triple\0\ - x86_64-apple-watchos5.0-simulator\0\ - -emit-obj\0\ - -disable-llvm-passes\0\ - -target-abi\0\ - darwinpcs\0\ - -Os\0" - .into(), ..opts("watchos", arch) }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/targets/x86_64_sun_solaris.rs deleted file mode 100644 index cca099d3bbf6..000000000000 --- a/compiler/rustc_target/src/spec/targets/x86_64_sun_solaris.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::spec::{base, Cc, LinkerFlavor, StackProbeType, Target}; - -pub fn target() -> Target { - let mut base = base::solaris::opts(); - base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), &["-m64"]); - base.cpu = "x86-64".into(); - base.plt_by_default = false; - base.vendor = "sun".into(); - base.max_atomic_width = Some(64); - base.stack_probes = StackProbeType::X86; - - Target { - llvm_target: "x86_64-pc-solaris".into(), - pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), - arch: "x86_64".into(), - options: base, - } -} diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index a9792ca2795c..d753aa8618e8 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/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index f7031c5f4933..739bbe929b33 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" - ); - } + + let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; + + let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; + + let variance = match direction { + ty::AliasRelationDirection::Equate => ty::Variance::Invariant, + ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, + }; match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { - (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), + (None, None) => { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - // 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, - ), - - // 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, - ), + (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 { + 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)) => { - debug!("both sides are aliases"); + self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs) + } + } + } - 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); - - if let Some(merged) = self.try_merge_responses(&candidates) { - Ok(merged) + /// 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>, + term: ty::Term<'tcx>, + ) -> Result>, 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 { - // 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 . - // - // 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), - } + Ok(Some(term)) } } } } - #[instrument(level = "debug", skip(self), ret)] - fn assemble_normalizes_to_candidate( + fn define_opaque( &mut self, param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - other: ty::Term<'tcx>, - direction: ty::AliasRelationDirection, - invert: Invert, + opaque: ty::AliasTy<'tcx>, + term: ty::Term<'tcx>, ) -> 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) - }) - } - - // 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( - &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 - } - }; 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 27d2bdead838..b6d2228d4b8f 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -110,7 +110,7 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait /// errors but still want to emit errors for other trait goals. We have some special @@ -263,7 +263,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { - return ambig; + return vec![ambig]; } let mut candidates = self.assemble_candidates_via_self_ty(goal, 0); @@ -288,15 +288,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn assemble_self_ty_infer_ambiguity_response>( &mut self, goal: Goal<'tcx, G>, - ) -> Option>> { - goal.predicate.self_ty().is_ty_var().then(|| { - vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result: self - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - .unwrap(), - }] - }) + ) -> Option> { + if goal.predicate.self_ty().is_ty_var() { + debug!("adding self_ty_infer_ambiguity_response"); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let result = self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(); + let mut dummy_probe = self.inspect.new_probe(); + dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); + self.inspect.finish_probe(dummy_probe); + Some(Candidate { source, result }) + } else { + None + } } /// Assemble candidates which apply to the self type. This only looks at candidate which @@ -310,7 +315,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { - return ambig; + return vec![ambig]; } let mut candidates = Vec::new(); @@ -352,7 +357,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, @@ -395,8 +400,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { for &impl_def_id in impls_for_type { match G::consider_impl_candidate(self, goal, impl_def_id) { - Ok(result) => candidates - .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } @@ -514,8 +518,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); for &impl_def_id in trait_impls.blanket_impls() { match G::consider_impl_candidate(self, goal, impl_def_id) { - Ok(result) => candidates - .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } @@ -864,23 +867,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/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs new file mode 100644 index 000000000000..c47152c601c3 --- /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( + &mut self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result, + ) -> Result { + 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 583eb9f96a9d..23ce0a301ce7 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 `::Assoc == u32` we recursively - // call `exists ::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>( + &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/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 6087b9167909..91fd48807a4d 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,5 +1,10 @@ +use crate::solve::assembly::Candidate; + use super::EvalCtxt; -use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; +use rustc_middle::traits::{ + query::NoSolution, + solve::{inspect, CandidateSource, QueryResult}, +}; use std::marker::PhantomData; pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { @@ -36,6 +41,23 @@ where } } +pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> { + cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>, + source: CandidateSource, +} + +impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F> +where + F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, +{ + pub(in crate::solve) fn enter( + self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> Result, NoSolution> { + self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) + } +} + impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// `probe_kind` is only called when proof tree building is enabled so it can be /// as expensive as necessary to output the desired information. @@ -69,20 +91,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { pub(in crate::solve) fn probe_trait_candidate( &mut self, source: CandidateSource, - ) -> ProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, - QueryResult<'tcx>, - > { - ProbeCtxt { - ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { - source, - result: *result, + ) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>> + { + TraitProbeCtxt { + cx: ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { + source, + result: *result, + }, + _result: PhantomData, }, - _result: PhantomData, + source, } } } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 951080ac61a5..5752d49ed2cf 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -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 088455b38cbb..0d46df44c910 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 dba5369fa0fe..65d061ab3f44 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>, NoSolution> { - for _ in 0..self.local_overflow_limit() { - let ty::Alias(_, projection_ty) = *ty.kind() else { - return Ok(Some(ty)); - }; + ty: Ty<'tcx>, + ) -> Option> { + self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty) + } - let normalized_ty = self.next_ty_infer(); + fn try_normalize_ty_recur( + &mut self, + param_env: ty::ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + depth: usize, + ty: Ty<'tcx>, + ) -> Option> { + 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 7fb550aa3e06..6c29ce492df0 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs @@ -1,6 +1,6 @@ use crate::traits::{check_args_compatible, specialization_graph}; -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, Candidate}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -154,7 +154,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); 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 ebd129f32b91..1fde129c3a0b 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 7ffa1d7d3193..68f81a055361 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 84baec4ff4c1..a0c065dfa03b 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,12 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::inspect::ProbeKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -38,7 +40,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -63,7 +65,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe_misc_candidate("impl").enter(|ecx| { + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); @@ -469,7 +471,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); }; @@ -536,9 +538,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 7ca33ae41c0c..9119792e3243 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -25,7 +25,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -397,8 +397,11 @@ fn impl_intersection_has_negative_obligation( ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); + // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates + // do not need intercrate mode enabled. let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build(); - let universe = infcx.universe(); + let root_universe = infcx.universe(); + assert_eq!(root_universe, ty::UniverseIndex::ROOT); let impl1_header = fresh_impl_header(infcx, impl1_def_id); let param_env = @@ -408,13 +411,25 @@ fn impl_intersection_has_negative_obligation( // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). - let Some(_equate_obligations) = + let Some(equate_obligations) = equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header) else { return false; }; - plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args)); + // FIXME(with_negative_coherence): the infcx has constraints from equating + // the impl headers. We should use these constraints as assumptions, not as + // requirements, when proving the negated where clauses below. + drop(equate_obligations); + drop(infcx.take_registered_region_obligations()); + drop(infcx.take_and_reset_region_constraints()); + + plug_infer_with_placeholders( + infcx, + root_universe, + (impl1_header.impl_args, impl2_header.impl_args), + ); + let param_env = infcx.resolve_vars_if_possible(param_env); util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) @@ -541,15 +556,11 @@ fn try_prove_negated_where_clause<'tcx>( return false; }; - // FIXME(with_negative_coherence): the infcx has region contraints from equating - // the impl headers as requirements. Given that the only region constraints we - // get are involving inference regions in the root, it shouldn't matter, but - // still sus. - // - // We probably should just throw away the region obligations registered up until - // now, or ideally use them as assumptions when proving the region obligations - // that we get from proving the negative predicate below. - let ref infcx = root_infcx.fork(); + // N.B. We don't need to use intercrate mode here because we're trying to prove + // the *existence* of a negative goal, not the non-existence of a positive goal. + // Without this, we over-eagerly register coherence ambiguity candidates when + // impl candidates do exist. + let ref infcx = root_infcx.fork_with_intercrate(false); let ocx = ObligationCtxt::new(infcx); ocx.register_obligation(Obligation::new( @@ -999,7 +1010,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { let Goal { param_env, predicate } = goal.goal(); - // For bound predicates we simply call `infcx.replace_bound_vars_with_placeholders` + // For bound predicates we simply call `infcx.instantiate_binder_with_placeholders` // 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, @@ -1014,6 +1025,28 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { _ => return ControlFlow::Continue(()), }; + // Add ambiguity causes for reservation impls. + for cand in goal.candidates() { + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(def_id), + result: Ok(_), + } = cand.kind() + { + if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { + let value = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(value) = value { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }); + } + } + } + } + + // Add ambiguity causes for unknowable goals. let mut ambiguity_cause = None; for cand in goal.candidates() { // FIXME: boiiii, using string comparisions here sure is scuffed. 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 60da6a58920d..da2d5dfbfb5c 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 @@ -321,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 { @@ -350,7 +350,7 @@ pub struct OnUnimplementedNote { pub enum AppendConstMessage { #[default] Default, - Custom(Symbol), + Custom(Symbol, Span), } #[derive(LintDiagnostic)] @@ -372,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, + old: Option, + 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>, @@ -384,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 { @@ -398,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); } @@ -417,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; } @@ -437,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) @@ -470,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); @@ -519,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, @@ -556,6 +634,7 @@ impl<'tcx> OnUnimplementedDirective { item_def_id, value, attr.span, + attr.span, )?), notes: Vec::new(), parent_label: None, @@ -633,7 +712,11 @@ impl<'tcx> OnUnimplementedDirective { // `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) + OnUnimplementedFormatString(v, cfg.span).format( + tcx, + trait_ref, + &options_map + ) ) }); @@ -678,8 +761,9 @@ impl<'tcx> OnUnimplementedFormatString { item_def_id: DefId, from: Symbol, err_sp: Span, + value_span: Span, ) -> Result { - 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 9bd1030ac74f..145b0cd0092f 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); @@ -3374,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() { 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 78c9ac157c0a..5239725f5096 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 @@ -2729,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 @@ -2750,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/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 32bbd626d4e4..1c2966bb3e53 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/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index e345fc39eade..57df277f4b3a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -208,6 +208,9 @@ fn implied_bounds_from_components<'tcx>( Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + Component::Placeholder(_) => { + unimplemented!("Shouldn't expect a placeholder type in implied bounds (yet)") + } Component::EscapingAlias(_) => // If the projection has escaping regions, don't // try to infer any implied bounds even for its 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 c1d44601891b..1529f736109a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -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 abb4e71a9af5..2ab3ecbd5a3e 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_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 38f2d616f9a5..737acfbc6005 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -16,7 +16,7 @@ use rustc_target::spec::abi::Abi as SpecAbi; use std::iter; -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { fn_abi_of_fn_ptr, fn_abi_of_instance, ..*providers }; } @@ -326,6 +326,76 @@ fn adjust_for_rust_scalar<'tcx>( } } +/// Ensure that the ABI makes basic sense. +fn fn_abi_sanity_check<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) { + fn fn_arg_sanity_check<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + arg: &ArgAbi<'tcx, Ty<'tcx>>, + ) { + match &arg.mode { + PassMode::Ignore => {} + PassMode::Direct(_) => { + // Here the Rust type is used to determine the actual ABI, so we have to be very + // careful. Scalar/ScalarPair is fine, since backends will generally use + // `layout.abi` and ignore everything else. We should just reject `Aggregate` + // entirely here, but some targets need to be fixed first. + if matches!(arg.layout.abi, Abi::Aggregate { .. }) { + // For an unsized type we'd only pass the sized prefix, so there is no universe + // in which we ever want to allow this. + assert!( + arg.layout.is_sized(), + "`PassMode::Direct` for unsized type in ABI: {:#?}", + fn_abi + ); + // This really shouldn't happen even for sized aggregates, since + // `immediate_llvm_type` will use `layout.fields` to turn this Rust type into an + // LLVM type. This means all sorts of Rust type details leak into the ABI. + // However wasm sadly *does* currently use this mode so we have to allow it -- + // but we absolutely shouldn't let any more targets do that. + // (Also see .) + // + // The unstable abi `PtxKernel` also uses Direct for now. + // It needs to switch to something else before stabilization can happen. + // (See issue: https://github.com/rust-lang/rust/issues/117271) + assert!( + matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64") + || fn_abi.conv == Conv::PtxKernel, + "`PassMode::Direct` for aggregates only allowed on wasm and `extern \"ptx-kernel\"` fns\nProblematic type: {:#?}", + arg.layout, + ); + } + } + PassMode::Pair(_, _) => { + // Similar to `Direct`, we need to make sure that backends use `layout.abi` and + // ignore the rest of the layout. + assert!( + matches!(arg.layout.abi, Abi::ScalarPair(..)), + "PassMode::Pair for type {}", + arg.layout.ty + ); + } + PassMode::Cast { .. } => { + // `Cast` means "transmute to `CastType`"; that only makes sense for sized types. + assert!(arg.layout.is_sized()); + } + PassMode::Indirect { meta_attrs: None, .. } => { + // No metadata, must be sized. + assert!(arg.layout.is_sized()); + } + PassMode::Indirect { meta_attrs: Some(_), on_stack, .. } => { + // With metadata. Must be unsized and not on the stack. + assert!(arg.layout.is_unsized() && !on_stack); + } + } + } + + for arg in fn_abi.args.iter() { + fn_arg_sanity_check(cx, fn_abi, arg); + } + fn_arg_sanity_check(cx, fn_abi, &fn_abi.ret); +} + // FIXME(eddyb) perhaps group the signature/type-containing (or all of them?) // arguments of this method, into a separate `struct`. #[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))] @@ -452,6 +522,7 @@ fn fn_abi_new_uncached<'tcx>( }; fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?; debug!("fn_abi_new_uncached = {:?}", fn_abi); + fn_abi_sanity_check(cx, &fn_abi); Ok(cx.tcx.arena.alloc(fn_abi)) } @@ -519,13 +590,14 @@ fn fn_abi_adjust_for_abi<'tcx>( _ => return, } - // `Aggregate` ABI must be adjusted to ensure that ABI-compatible Rust types are passed - // the same way. + // Compute `Aggregate` ABI. + + let is_indirect_not_on_stack = + matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }); + assert!(is_indirect_not_on_stack, "{:?}", arg); let size = arg.layout.size; - if arg.layout.is_unsized() || size > Pointer(AddressSpace::DATA).size(cx) { - arg.make_indirect(); - } else { + if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) { // We want to pass small aggregates as immediates, but using // an LLVM aggregate type for this leads to bad optimizations, // so we pick an appropriately sized integer type instead. diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 3e140793b5a5..0a34aef16ae0 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -8,7 +8,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, GenericArgs, ImplTraitInTraitData, Ty, TyCtxt}; use rustc_span::symbol::kw; -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { associated_item, associated_item_def_ids, @@ -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/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 35487d3b6982..9ced50c8e138 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -17,7 +17,7 @@ use crate::errors::{GenericConstantTooComplex, GenericConstantTooComplexSub}; /// Destructures array, ADT or tuple constants into the constants /// of their fields. -pub(crate) fn destructure_const<'tcx>( +fn destructure_const<'tcx>( tcx: TyCtxt<'tcx>, const_: ty::Const<'tcx>, ) -> ty::DestructuredConst<'tcx> { @@ -396,7 +396,7 @@ impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { } /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. -pub fn thir_abstract_const( +fn thir_abstract_const( tcx: TyCtxt<'_>, def: LocalDefId, ) -> Result>>, ErrorGuaranteed> { @@ -428,6 +428,6 @@ pub fn thir_abstract_const( Ok(Some(ty::EarlyBinder::bind(recurse_build(tcx, body, body_id, root_span)?))) } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { destructure_const, thir_abstract_const, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 24afd7dc3578..6cf5aa6f2fb3 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use std::iter; -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { assumed_wf_types, assumed_wf_types_for_rpitit: |tcx, def_id| { diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 1487f40fd994..31f6a56eaeb4 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -61,7 +61,7 @@ fn resolve_instance<'tcx>( Ok(Some(Instance { def, args })) }; - debug!("inner_resolve_instance: result={:?}", result); + debug!("resolve_instance: result={:?}", result); result } @@ -328,6 +328,6 @@ fn resolve_associated_item<'tcx>( }) } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { resolve_instance, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 52f723eba80e..b09566e16682 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -24,7 +24,7 @@ use crate::errors::{ }; use crate::layout_sanity_check::sanity_check_layout; -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { layout_of, ..*providers }; } @@ -65,7 +65,11 @@ fn layout_of<'tcx>( let layout = layout_of_uncached(&cx, ty)?; let layout = TyAndLayout { ty, layout }; - record_layout_for_printing(&cx, layout); + // If we are running with `-Zprint-type-sizes`, maybe record layouts + // for dumping later. + if cx.tcx.sess.opts.unstable_opts.print_type_sizes { + record_layout_for_printing(&cx, layout); + } sanity_check_layout(&cx, &layout); @@ -911,21 +915,7 @@ fn coroutine_layout<'tcx>( Ok(layout) } -/// This is invoked by the `layout_of` query to record the final -/// layout of each type. -#[inline(always)] fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: TyAndLayout<'tcx>) { - // If we are running with `-Zprint-type-sizes`, maybe record layouts - // for dumping later. - if cx.tcx.sess.opts.unstable_opts.print_type_sizes { - record_layout_for_printing_outlined(cx, layout) - } -} - -fn record_layout_for_printing_outlined<'tcx>( - cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, - layout: TyAndLayout<'tcx>, -) { // Ignore layouts that are done with non-empty environments or // non-monomorphic layouts, as the user only wants to see the stuff // resulting from the final codegen session. diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 8de058f02c9e..8321732b7663 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -10,11 +10,11 @@ #![allow(internal_features)] #![feature(assert_matches)] #![feature(associated_type_defaults)] +#![feature(box_patterns)] +#![feature(if_let_guard)] #![feature(iterator_try_collect)] #![feature(let_chains)] -#![feature(if_let_guard)] #![feature(never_type)] -#![feature(box_patterns)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -34,13 +34,13 @@ mod common_traits; mod consts; mod errors; mod implied_bounds; -pub mod instance; +mod instance; mod layout; mod layout_sanity_check; mod needs_drop; mod opaque_types; -pub mod representability; -pub mod sig_types; +mod representability; +mod sig_types; mod structural_match; mod ty; diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index f34e0df2c75e..3aaa2e73bb80 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -6,7 +6,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { representability, representability_adt_ty, params_in_repr, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ccdc6120196a..268639a7f444 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Span; use rustc_type_ir::visit::TypeVisitable; -pub trait SpannedTypeVisitor<'tcx> { +pub(crate) trait SpannedTypeVisitor<'tcx> { type BreakTy = !; fn visit( &mut self, @@ -17,7 +17,7 @@ pub trait SpannedTypeVisitor<'tcx> { ) -> ControlFlow; } -pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( +pub(crate) fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( tcx: TyCtxt<'tcx>, item: LocalDefId, visitor: &mut V, diff --git a/compiler/rustc_ty_utils/src/structural_match.rs b/compiler/rustc_ty_utils/src/structural_match.rs index 215acbe2c8f8..6e7a9887774f 100644 --- a/compiler/rustc_ty_utils/src/structural_match.rs +++ b/compiler/rustc_ty_utils/src/structural_match.rs @@ -39,6 +39,6 @@ fn has_structural_eq_impls<'tcx>(tcx: TyCtxt<'tcx>, adt_ty: Ty<'tcx>) -> bool { ocx.select_all_or_error().is_empty() } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { providers.has_structural_eq_impls = has_structural_eq_impls; } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 485bde735d8f..b7c75da7301f 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -351,7 +351,7 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet { pub value: V, pub max_universe: UniverseIndex, @@ -60,17 +58,6 @@ impl Canonical { } } -impl> HashStable for Canonical -where - I::CanonicalVars: HashStable, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.value.hash_stable(hcx, hasher); - self.max_universe.hash_stable(hcx, hasher); - self.variables.hash_stable(hcx, hasher); - } -} - impl Eq for Canonical {} impl PartialEq for Canonical { diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 33782b13ca8f..a5f90421de5f 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -1,8 +1,8 @@ -use rustc_data_structures::stable_hasher::HashStable; -use rustc_data_structures::stable_hasher::StableHasher; +#[cfg(feature = "nightly")] +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use std::fmt; -use crate::{DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, WithInfcx}; +use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; use self::ConstKind::*; @@ -16,13 +16,13 @@ use self::ConstKind::*; Ord = "feature_allow_slow_enum", Hash(bound = "") )] -#[derive(TyEncodable, TyDecodable)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum ConstKind { /// A const generic parameter. Param(I::ParamConst), /// Infer the value of the const. - Infer(I::InferConst), + Infer(InferConst), /// Bound const variable, used only when preparing a trait query. Bound(DebruijnIndex, I::BoundConst), @@ -47,48 +47,6 @@ pub enum ConstKind { Expr(I::ExprConst), } -const fn const_kind_discriminant(value: &ConstKind) -> usize { - match value { - Param(_) => 0, - Infer(_) => 1, - Bound(_, _) => 2, - Placeholder(_) => 3, - Unevaluated(_) => 4, - Value(_) => 5, - Error(_) => 6, - Expr(_) => 7, - } -} - -impl HashStable for ConstKind -where - I::ParamConst: HashStable, - I::InferConst: HashStable, - I::BoundConst: HashStable, - I::PlaceholderConst: HashStable, - I::AliasConst: HashStable, - I::ValueConst: HashStable, - I::ErrorGuaranteed: HashStable, - I::ExprConst: HashStable, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - const_kind_discriminant(self).hash_stable(hcx, hasher); - match self { - Param(p) => p.hash_stable(hcx, hasher), - Infer(i) => i.hash_stable(hcx, hasher), - Bound(d, b) => { - d.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - Placeholder(p) => p.hash_stable(hcx, hasher), - Unevaluated(u) => u.hash_stable(hcx, hasher), - Value(v) => v.hash_stable(hcx, hasher), - Error(e) => e.hash_stable(hcx, hasher), - Expr(e) => e.hash_stable(hcx, hasher), - } - } -} - impl PartialEq for ConstKind { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -134,3 +92,77 @@ impl DebugWithInfcx for ConstKind { } } } + +rustc_index::newtype_index! { + /// A **`const`** **v**ariable **ID**. + #[debug_format = "?{}c"] + #[gate_rustc_only] + pub struct ConstVid {} +} + +rustc_index::newtype_index! { + /// An **effect** **v**ariable **ID**. + /// + /// Handling effect infer variables happens separately from const infer variables + /// because we do not want to reuse any of the const infer machinery. If we try to + /// relate an effect variable with a normal one, we would ICE, which can catch bugs + /// where we are not correctly using the effect var for an effect param. Fallback + /// is also implemented on top of having separate effect and normal const variables. + #[debug_format = "?{}e"] + #[gate_rustc_only] + pub struct EffectVid {} +} + +/// An inference variable for a const, for use in const generics. +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] +pub enum InferConst { + /// Infer the value of the const. + Var(ConstVid), + /// Infer the value of the effect. + /// + /// For why this is separate from the `Var` variant above, see the + /// documentation on `EffectVid`. + EffectVar(EffectVid), + /// A fresh const variable. See `infer::freshen` for more details. + Fresh(u32), +} + +impl fmt::Debug for InferConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + InferConst::Var(var) => write!(f, "{var:?}"), + InferConst::EffectVar(var) => write!(f, "{var:?}"), + InferConst::Fresh(var) => write!(f, "Fresh({var:?})"), + } + } +} +impl DebugWithInfcx for InferConst { + fn fmt>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match this.infcx.universe_of_ct(*this.data) { + None => write!(f, "{:?}", this.data), + Some(universe) => match *this.data { + InferConst::Var(vid) => write!(f, "?{}_{}c", vid.index(), universe.index()), + InferConst::EffectVar(vid) => write!(f, "?{}_{}e", vid.index(), universe.index()), + InferConst::Fresh(_) => { + unreachable!() + } + }, + } + } +} + +#[cfg(feature = "nightly")] +impl HashStable for InferConst { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + match self { + InferConst::Var(_) | InferConst::EffectVar(_) => { + panic!("const variables should not be hashed: {self:?}") + } + InferConst::Fresh(i) => i.hash_stable(hcx, hasher), + } + } +} diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs index 4ea3eb3e84f4..db29ec9da8c9 100644 --- a/compiler/rustc_type_ir/src/debug.rs +++ b/compiler/rustc_type_ir/src/debug.rs @@ -1,4 +1,4 @@ -use crate::{Interner, UniverseIndex}; +use crate::{InferConst, InferTy, Interner, UniverseIndex}; use core::fmt; use std::marker::PhantomData; @@ -6,15 +6,14 @@ use std::marker::PhantomData; pub trait InferCtxtLike { type Interner: Interner; - fn universe_of_ty(&self, ty: ::InferTy) -> Option; + fn universe_of_ty(&self, ty: InferTy) -> Option; fn universe_of_lt( &self, lt: ::InferRegion, ) -> Option; - fn universe_of_ct(&self, ct: ::InferConst) - -> Option; + fn universe_of_ct(&self, ct: InferConst) -> Option; } pub struct NoInfcx(PhantomData); @@ -22,11 +21,11 @@ pub struct NoInfcx(PhantomData); impl InferCtxtLike for NoInfcx { type Interner = I; - fn universe_of_ty(&self, _ty: ::InferTy) -> Option { + fn universe_of_ty(&self, _ty: InferTy) -> Option { None } - fn universe_of_ct(&self, _ct: ::InferConst) -> Option { + fn universe_of_ct(&self, _ct: InferConst) -> Option { None } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index fc56400df161..8d402588398e 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -45,12 +45,18 @@ //! - u.fold_with(folder) //! ``` -use rustc_data_structures::sync::Lrc; use rustc_index::{Idx, IndexVec}; use std::mem; +use crate::Lrc; use crate::{visit::TypeVisitable, Interner}; +#[cfg(feature = "nightly")] +type Never = !; + +#[cfg(not(feature = "nightly"))] +type Never = std::convert::Infallible; + /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. /// @@ -79,7 +85,10 @@ pub trait TypeFoldable: TypeVisitable { /// folders. Do not override this method, to ensure coherence with /// `try_fold_with`. fn fold_with>(self, folder: &mut F) -> Self { - self.try_fold_with(folder).into_ok() + match self.try_fold_with(folder) { + Ok(t) => t, + Err(e) => match e {}, + } } } @@ -100,7 +109,10 @@ pub trait TypeSuperFoldable: TypeFoldable { /// infallible folders. Do not override this method, to ensure coherence /// with `try_super_fold_with`. fn super_fold_with>(self, folder: &mut F) -> Self { - self.try_super_fold_with(folder).into_ok() + match self.try_super_fold_with(folder) { + Ok(t) => t, + Err(e) => match e {}, + } } } @@ -113,7 +125,7 @@ pub trait TypeSuperFoldable: TypeFoldable { /// A blanket implementation of [`FallibleTypeFolder`] will defer to /// the infallible methods of this trait to ensure that the two APIs /// are coherent. -pub trait TypeFolder: FallibleTypeFolder { +pub trait TypeFolder: FallibleTypeFolder { fn interner(&self) -> I; fn fold_binder(&mut self, t: I::Binder) -> I::Binder @@ -208,13 +220,13 @@ impl FallibleTypeFolder for F where F: TypeFolder, { - type Error = !; + type Error = Never; fn interner(&self) -> I { TypeFolder::interner(self) } - fn try_fold_binder(&mut self, t: I::Binder) -> Result, !> + fn try_fold_binder(&mut self, t: I::Binder) -> Result, Never> where T: TypeFoldable, I::Binder: TypeSuperFoldable, @@ -222,25 +234,25 @@ where Ok(self.fold_binder(t)) } - fn try_fold_ty(&mut self, t: I::Ty) -> Result + fn try_fold_ty(&mut self, t: I::Ty) -> Result where I::Ty: TypeSuperFoldable, { Ok(self.fold_ty(t)) } - fn try_fold_region(&mut self, r: I::Region) -> Result { + fn try_fold_region(&mut self, r: I::Region) -> Result { Ok(self.fold_region(r)) } - fn try_fold_const(&mut self, c: I::Const) -> Result + fn try_fold_const(&mut self, c: I::Const) -> Result where I::Const: TypeSuperFoldable, { Ok(self.fold_const(c)) } - fn try_fold_predicate(&mut self, p: I::Predicate) -> Result + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result where I::Predicate: TypeSuperFoldable, { @@ -311,7 +323,7 @@ impl> TypeFoldable for Lrc { // Call to `Lrc::make_mut` above guarantees that `unique` is the // sole reference to the contained value, so we can avoid doing // a checked `get_mut` here. - let slot = Lrc::get_mut_unchecked(&mut unique); + let slot = Lrc::get_mut(&mut unique).unwrap_unchecked(); // Semantically move the contained type out from `unique`, fold // it, then move the folded value back into `unique`. Should diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index da504c54fddd..170a791fb54d 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -27,7 +27,6 @@ pub trait Interner: Sized { type ParamTy: Clone + Debug + Hash + Ord; type BoundTy: Clone + Debug + Hash + Ord; type PlaceholderTy: Clone + Debug + Hash + Ord; - type InferTy: Clone + DebugWithInfcx + Hash + Ord; // Things stored inside of tys type ErrorGuaranteed: Clone + Debug + Hash + Ord; @@ -37,7 +36,6 @@ pub trait Interner: Sized { // Kinds of consts type Const: Clone + DebugWithInfcx + Hash + Ord; - type InferConst: Clone + DebugWithInfcx + Hash + Ord; type AliasConst: Clone + DebugWithInfcx + Hash + Ord; type PlaceholderConst: Clone + Debug + Hash + Ord; type ParamConst: Clone + Debug + Hash + Ord; diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index e8785fff2efc..acea7aa46f6f 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,25 +1,28 @@ -#![feature(associated_type_defaults)] -#![feature(fmt_helpers_for_derive)] -#![feature(get_mut_unchecked)] -#![feature(min_specialization)] -#![feature(never_type)] -#![feature(new_uninit)] -#![feature(rustc_attrs)] -#![feature(unwrap_infallible)] +#![cfg_attr( + feature = "nightly", + feature(associated_type_defaults, min_specialization, never_type, rustc_attrs) +)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] -#![allow(internal_features)] +#![cfg_attr(feature = "nightly", allow(internal_features))] +#[cfg(feature = "nightly")] extern crate self as rustc_type_ir; #[macro_use] extern crate bitflags; +#[cfg(feature = "nightly")] #[macro_use] extern crate rustc_macros; +#[cfg(feature = "nightly")] +use rustc_data_structures::sync::Lrc; use std::fmt; use std::hash::Hash; +#[cfg(not(feature = "nightly"))] +use std::sync::Arc as Lrc; +#[cfg(feature = "nightly")] pub mod codec; pub mod fold; pub mod ty_info; @@ -37,6 +40,7 @@ mod predicate_kind; mod region_kind; pub use canonical::*; +#[cfg(feature = "nightly")] pub use codec::*; pub use const_kind::*; pub use debug::{DebugWithInfcx, InferCtxtLike, WithInfcx}; @@ -47,9 +51,6 @@ pub use region_kind::*; pub use ty_info::*; pub use ty_kind::*; -/// Needed so we can use #[derive(HashStable_Generic)] -pub trait HashStableContext {} - rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing /// regions (and perhaps later types) in a higher-ranked setting. In @@ -90,8 +91,9 @@ rustc_index::newtype_index! { /// is the outer fn. /// /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index - #[derive(HashStable_Generic)] + #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] #[debug_format = "DebruijnIndex({})"] + #[gate_rustc_only] pub struct DebruijnIndex { const INNERMOST = 0; } @@ -173,8 +175,9 @@ pub fn debug_bound_var( } } -#[derive(Copy, Clone, PartialEq, Eq, Decodable, Encodable, Hash, HashStable_Generic)] -#[rustc_pass_by_value] +#[derive(Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "nightly", derive(Decodable, Encodable, Hash, HashStable_NoContext))] +#[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum Variance { Covariant, // T <: T iff A <: B -- e.g., function return type Invariant, // T <: T iff B == A -- e.g., type of mutable cell @@ -289,8 +292,9 @@ rustc_index::newtype_index! { /// declared, but a type name in a non-zero universe is a placeholder /// type -- an idealized representative of "types in general" that we /// use for checking generic functions. - #[derive(HashStable_Generic)] + #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] #[debug_format = "U{}"] + #[gate_rustc_only] pub struct UniverseIndex {} } diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 48662d426423..da041ab1f355 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -1,16 +1,15 @@ -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use std::fmt; use std::ops::ControlFlow; use crate::fold::{FallibleTypeFolder, TypeFoldable}; use crate::visit::{TypeVisitable, TypeVisitor}; -use crate::{HashStableContext, Interner}; +use crate::Interner; /// A clause is something that can appear in where bounds or be inferred /// by implied bounds. #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""), Hash(bound = ""))] -#[derive(TyEncodable, TyDecodable)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum ClauseKind { /// Corresponds to `where Foo: Bar`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` @@ -67,45 +66,6 @@ impl PartialEq for ClauseKind { impl Eq for ClauseKind {} -fn clause_kind_discriminant(value: &ClauseKind) -> usize { - match value { - ClauseKind::Trait(_) => 0, - ClauseKind::RegionOutlives(_) => 1, - ClauseKind::TypeOutlives(_) => 2, - ClauseKind::Projection(_) => 3, - ClauseKind::ConstArgHasType(_, _) => 4, - ClauseKind::WellFormed(_) => 5, - ClauseKind::ConstEvaluatable(_) => 6, - } -} - -impl HashStable for ClauseKind -where - I::Ty: HashStable, - I::Const: HashStable, - I::GenericArg: HashStable, - I::TraitPredicate: HashStable, - I::ProjectionPredicate: HashStable, - I::TypeOutlivesPredicate: HashStable, - I::RegionOutlivesPredicate: HashStable, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - clause_kind_discriminant(self).hash_stable(hcx, hasher); - match self { - ClauseKind::Trait(p) => p.hash_stable(hcx, hasher), - ClauseKind::RegionOutlives(p) => p.hash_stable(hcx, hasher), - ClauseKind::TypeOutlives(p) => p.hash_stable(hcx, hasher), - ClauseKind::Projection(p) => p.hash_stable(hcx, hasher), - ClauseKind::ConstArgHasType(c, t) => { - c.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); - } - ClauseKind::WellFormed(t) => t.hash_stable(hcx, hasher), - ClauseKind::ConstEvaluatable(c) => c.hash_stable(hcx, hasher), - } - } -} - impl TypeFoldable for ClauseKind where I::Ty: TypeFoldable, @@ -161,7 +121,7 @@ where #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""), Hash(bound = ""))] -#[derive(TyEncodable, TyDecodable)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum PredicateKind { /// Prove a clause Clause(ClauseKind), @@ -239,56 +199,6 @@ impl PartialEq for PredicateKind { impl Eq for PredicateKind {} -fn predicate_kind_discriminant(value: &PredicateKind) -> usize { - match value { - PredicateKind::Clause(_) => 0, - PredicateKind::ObjectSafe(_) => 1, - PredicateKind::ClosureKind(_, _, _) => 2, - PredicateKind::Subtype(_) => 3, - PredicateKind::Coerce(_) => 4, - PredicateKind::ConstEquate(_, _) => 5, - PredicateKind::Ambiguous => 6, - PredicateKind::AliasRelate(_, _, _) => 7, - } -} - -impl HashStable for PredicateKind -where - I::DefId: HashStable, - I::Const: HashStable, - I::GenericArgs: HashStable, - I::Term: HashStable, - I::CoercePredicate: HashStable, - I::SubtypePredicate: HashStable, - I::ClosureKind: HashStable, - ClauseKind: HashStable, -{ - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - predicate_kind_discriminant(self).hash_stable(hcx, hasher); - match self { - PredicateKind::Clause(p) => p.hash_stable(hcx, hasher), - PredicateKind::ObjectSafe(d) => d.hash_stable(hcx, hasher), - PredicateKind::ClosureKind(d, g, k) => { - d.hash_stable(hcx, hasher); - g.hash_stable(hcx, hasher); - k.hash_stable(hcx, hasher); - } - PredicateKind::Subtype(p) => p.hash_stable(hcx, hasher), - PredicateKind::Coerce(p) => p.hash_stable(hcx, hasher), - PredicateKind::ConstEquate(c1, c2) => { - c1.hash_stable(hcx, hasher); - c2.hash_stable(hcx, hasher); - } - PredicateKind::Ambiguous => {} - PredicateKind::AliasRelate(t1, t2, r) => { - t1.hash_stable(hcx, hasher); - t2.hash_stable(hcx, hasher); - r.hash_stable(hcx, hasher); - } - } - } -} - impl TypeFoldable for PredicateKind where I::DefId: TypeFoldable, @@ -361,7 +271,7 @@ where } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] -#[derive(HashStable_Generic, Encodable, Decodable)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, Encodable, Decodable))] pub enum AliasRelationDirection { Equate, Subtype, diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 5d3ce49bc8f7..4157d49287c2 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,8 +1,8 @@ -use rustc_data_structures::stable_hasher::HashStable; -use rustc_data_structures::stable_hasher::StableHasher; +#[cfg(feature = "nightly")] +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use std::fmt; -use crate::{DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, WithInfcx}; +use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; use self::RegionKind::*; @@ -121,7 +121,7 @@ use self::RegionKind::*; Ord = "feature_allow_slow_enum", Hash(bound = "") )] -#[derive(TyEncodable, TyDecodable)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] pub enum RegionKind { /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`. /// @@ -261,8 +261,9 @@ impl fmt::Debug for RegionKind { } } +#[cfg(feature = "nightly")] // This is not a derived impl because a derive would require `I: HashStable` -impl HashStable for RegionKind +impl HashStable for RegionKind where I::EarlyParamRegion: HashStable, I::BoundRegion: HashStable, diff --git a/compiler/rustc_type_ir/src/ty_info.rs b/compiler/rustc_type_ir/src/ty_info.rs index d986e310c329..0e4930552c21 100644 --- a/compiler/rustc_type_ir/src/ty_info.rs +++ b/compiler/rustc_type_ir/src/ty_info.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "nightly")] use rustc_data_structures::fingerprint::Fingerprint; +#[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; @@ -16,6 +18,8 @@ use crate::{DebruijnIndex, TypeFlags}; #[derive(Copy, Clone)] pub struct WithCachedTypeInfo { pub internee: T, + + #[cfg(feature = "nightly")] pub stable_hash: Fingerprint, /// This field provides fast access to information that is also contained @@ -81,14 +85,16 @@ impl Deref for WithCachedTypeInfo { impl Hash for WithCachedTypeInfo { #[inline] fn hash(&self, s: &mut H) { + #[cfg(feature = "nightly")] if self.stable_hash != Fingerprint::ZERO { - self.stable_hash.hash(s) - } else { - self.internee.hash(s) + return self.stable_hash.hash(s); } + + self.internee.hash(s) } } +#[cfg(feature = "nightly")] impl, CTX> HashStable for WithCachedTypeInfo { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { if self.stable_hash == Fingerprint::ZERO || cfg!(debug_assertions) { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 09a9a332269e..494eeaf3dc88 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1,11 +1,11 @@ #![allow(rustc::usage_of_ty_tykind)] +#[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +#[cfg(feature = "nightly")] use rustc_data_structures::unify::{EqUnifyValue, UnifyKey}; use std::fmt; -use std::mem::discriminant; -use crate::HashStableContext; use crate::Interner; use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, WithInfcx}; @@ -13,8 +13,8 @@ use self::TyKind::*; /// The movability of a coroutine / closure literal: /// whether a coroutine contains self-references, causing it to be `!Unpin`. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)] -#[derive(HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum Movability { /// May contain self-references, `!Unpin`. Static, @@ -23,7 +23,7 @@ pub enum Movability { } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] -#[derive(HashStable_Generic, Encodable, Decodable)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum Mutability { // N.B. Order is deliberate, so that Not < Mut Not, @@ -75,7 +75,7 @@ impl Mutability { /// Specifies how a trait object is represented. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum DynKind { /// An unsized `dyn Trait` object Dyn, @@ -89,7 +89,7 @@ pub enum DynKind { } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum AliasKind { /// A projection `::AssocType`. /// Can get normalized away if monomorphic enough. @@ -109,7 +109,7 @@ pub enum AliasKind { /// /// Types written by the user start out as `hir::TyKind` and get /// converted to this representation using `AstConv::ast_ty_to_ty`. -#[rustc_diagnostic_item = "IrTyKind"] +#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")] #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), @@ -119,7 +119,7 @@ pub enum AliasKind { Ord = "feature_allow_slow_enum", Hash(bound = "") )] -#[derive(TyEncodable, TyDecodable)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum TyKind { /// The primitive boolean type. Written as `bool`. Bool, @@ -281,7 +281,7 @@ pub enum TyKind { /// correctly deal with higher ranked types. Though unlike placeholders, /// that universe is stored in the `InferCtxt` instead of directly /// inside of the type. - Infer(I::InferTy), + Infer(InferTy), /// A placeholder for a type which could not be computed; this is /// propagated to avoid useless error messages. @@ -407,7 +407,7 @@ impl DebugWithInfcx for TyKind { write!(f, ">") } - Foreign(d) => f.debug_tuple_field1_finish("Foreign", d), + Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)), Slice(t) => write!(f, "[{:?}]", &this.wrap(t)), @@ -423,7 +423,7 @@ impl DebugWithInfcx for TyKind { Mutability::Mut => write!(f, "&{:?} mut {:?}", &this.wrap(r), &this.wrap(t)), Mutability::Not => write!(f, "&{:?} {:?}", &this.wrap(r), &this.wrap(t)), }, - FnDef(d, s) => f.debug_tuple_field2_finish("FnDef", d, &this.wrap(s)), + FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&this.wrap(s)).finish(), FnPtr(s) => write!(f, "{:?}", &this.wrap(s)), Dynamic(p, r, repr) => match repr { DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &this.wrap(p), &this.wrap(r)), @@ -431,10 +431,12 @@ impl DebugWithInfcx for TyKind { write!(f, "dyn* {:?} + {:?}", &this.wrap(p), &this.wrap(r)) } }, - Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, &this.wrap(s)), - Coroutine(d, s, m) => f.debug_tuple_field3_finish("Coroutine", d, &this.wrap(s), m), + Closure(d, s) => f.debug_tuple("Closure").field(d).field(&this.wrap(s)).finish(), + Coroutine(d, s, m) => { + f.debug_tuple("Coroutine").field(d).field(&this.wrap(s)).field(m).finish() + } CoroutineWitness(d, s) => { - f.debug_tuple_field2_finish("CoroutineWitness", d, &this.wrap(s)) + f.debug_tuple("CoroutineWitness").field(d).field(&this.wrap(s)).finish() } Never => write!(f, "!"), Tuple(t) => { @@ -453,7 +455,7 @@ impl DebugWithInfcx for TyKind { } write!(f, ")") } - Alias(i, a) => f.debug_tuple_field2_finish("Alias", i, &this.wrap(a)), + Alias(i, a) => f.debug_tuple("Alias").field(i).field(&this.wrap(a)).finish(), Param(p) => write!(f, "{p:?}"), Bound(d, b) => crate::debug_bound_var(f, *d, b), Placeholder(p) => write!(f, "{p:?}"), @@ -470,120 +472,8 @@ impl fmt::Debug for TyKind { } } -// This is not a derived impl because a derive would require `I: HashStable` -#[allow(rustc::usage_of_ty_tykind)] -impl HashStable for TyKind -where - I::AdtDef: HashStable, - I::DefId: HashStable, - I::GenericArgs: HashStable, - I::Ty: HashStable, - I::Const: HashStable, - I::TypeAndMut: HashStable, - I::PolyFnSig: HashStable, - I::BoundExistentialPredicates: HashStable, - I::Region: HashStable, - I::Tys: HashStable, - I::AliasTy: HashStable, - I::BoundTy: HashStable, - I::ParamTy: HashStable, - I::PlaceholderTy: HashStable, - I::InferTy: HashStable, - I::ErrorGuaranteed: HashStable, -{ - #[inline] - fn hash_stable(&self, __hcx: &mut CTX, __hasher: &mut StableHasher) { - std::mem::discriminant(self).hash_stable(__hcx, __hasher); - match self { - Bool => {} - Char => {} - Int(i) => { - i.hash_stable(__hcx, __hasher); - } - Uint(u) => { - u.hash_stable(__hcx, __hasher); - } - Float(f) => { - f.hash_stable(__hcx, __hasher); - } - Adt(adt, args) => { - adt.hash_stable(__hcx, __hasher); - args.hash_stable(__hcx, __hasher); - } - Foreign(def_id) => { - def_id.hash_stable(__hcx, __hasher); - } - Str => {} - Array(t, c) => { - t.hash_stable(__hcx, __hasher); - c.hash_stable(__hcx, __hasher); - } - Slice(t) => { - t.hash_stable(__hcx, __hasher); - } - RawPtr(tam) => { - tam.hash_stable(__hcx, __hasher); - } - Ref(r, t, m) => { - r.hash_stable(__hcx, __hasher); - t.hash_stable(__hcx, __hasher); - m.hash_stable(__hcx, __hasher); - } - FnDef(def_id, args) => { - def_id.hash_stable(__hcx, __hasher); - args.hash_stable(__hcx, __hasher); - } - FnPtr(polyfnsig) => { - polyfnsig.hash_stable(__hcx, __hasher); - } - Dynamic(l, r, repr) => { - l.hash_stable(__hcx, __hasher); - r.hash_stable(__hcx, __hasher); - repr.hash_stable(__hcx, __hasher); - } - Closure(def_id, args) => { - def_id.hash_stable(__hcx, __hasher); - args.hash_stable(__hcx, __hasher); - } - Coroutine(def_id, args, m) => { - def_id.hash_stable(__hcx, __hasher); - args.hash_stable(__hcx, __hasher); - m.hash_stable(__hcx, __hasher); - } - CoroutineWitness(def_id, args) => { - def_id.hash_stable(__hcx, __hasher); - args.hash_stable(__hcx, __hasher); - } - Never => {} - Tuple(args) => { - args.hash_stable(__hcx, __hasher); - } - Alias(k, p) => { - k.hash_stable(__hcx, __hasher); - p.hash_stable(__hcx, __hasher); - } - Param(p) => { - p.hash_stable(__hcx, __hasher); - } - Bound(d, b) => { - d.hash_stable(__hcx, __hasher); - b.hash_stable(__hcx, __hasher); - } - Placeholder(p) => { - p.hash_stable(__hcx, __hasher); - } - Infer(i) => { - i.hash_stable(__hcx, __hasher); - } - Error(d) => { - d.hash_stable(__hcx, __hasher); - } - } - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum IntTy { Isize, I8, @@ -641,7 +531,7 @@ impl IntTy { } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum UintTy { Usize, U8, @@ -699,7 +589,7 @@ impl UintTy { } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(Encodable, Decodable, HashStable_Generic)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum FloatTy { F32, F64, @@ -733,18 +623,21 @@ pub struct FloatVarValue(pub FloatTy); rustc_index::newtype_index! { /// A **ty**pe **v**ariable **ID**. #[debug_format = "?{}t"] + #[gate_rustc_only] pub struct TyVid {} } rustc_index::newtype_index! { /// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**. #[debug_format = "?{}i"] + #[gate_rustc_only] pub struct IntVid {} } rustc_index::newtype_index! { /// A **float**ing-point (`f32` or `f64`) type **v**ariable **ID**. #[debug_format = "?{}f"] + #[gate_rustc_only] pub struct FloatVid {} } @@ -753,7 +646,8 @@ rustc_index::newtype_index! { /// E.g., if we have an empty array (`[]`), then we create a fresh /// type variable for the element type since we won't know until it's /// used what the element type is supposed to be. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))] pub enum InferTy { /// A type variable. TyVar(TyVid), @@ -786,6 +680,7 @@ pub enum InferTy { /// Raw `TyVid` are used as the unification key for `sub_relations`; /// they carry no values. +#[cfg(feature = "nightly")] impl UnifyKey for TyVid { type Value = (); #[inline] @@ -801,8 +696,10 @@ impl UnifyKey for TyVid { } } +#[cfg(feature = "nightly")] impl EqUnifyValue for IntVarValue {} +#[cfg(feature = "nightly")] impl UnifyKey for IntVid { type Value = Option; #[inline] // make this function eligible for inlining - it is quite hot. @@ -818,8 +715,10 @@ impl UnifyKey for IntVid { } } +#[cfg(feature = "nightly")] impl EqUnifyValue for FloatVarValue {} +#[cfg(feature = "nightly")] impl UnifyKey for FloatVid { type Value = Option; #[inline] @@ -835,10 +734,11 @@ impl UnifyKey for FloatVid { } } +#[cfg(feature = "nightly")] impl HashStable for InferTy { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { use InferTy::*; - discriminant(self).hash_stable(ctx, hasher); + std::mem::discriminant(self).hash_stable(ctx, hasher); match self { TyVar(_) | IntVar(_) | FloatVar(_) => { panic!("type variables should not be hashed: {self:?}") @@ -909,7 +809,7 @@ impl fmt::Debug for InferTy { } } -impl> DebugWithInfcx for InferTy { +impl DebugWithInfcx for InferTy { fn fmt>( this: WithInfcx<'_, Infcx, &Self>, f: &mut fmt::Formatter<'_>, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 9c7b8156b043..7aa990046675 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -41,12 +41,12 @@ //! - u.visit_with(visitor) //! ``` -use rustc_data_structures::sync::Lrc; use rustc_index::{Idx, IndexVec}; use std::fmt; use std::ops::ControlFlow; use crate::Interner; +use crate::Lrc; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. @@ -82,8 +82,12 @@ pub trait TypeSuperVisitable: TypeVisitable { /// method defined for every type of interest. Each such method has a default /// that recurses into the type's fields in a non-custom fashion. pub trait TypeVisitor: Sized { + #[cfg(feature = "nightly")] type BreakTy = !; + #[cfg(not(feature = "nightly"))] + type BreakTy; + fn visit_binder>(&mut self, t: &I::Binder) -> ControlFlow where I::Binder: TypeSuperVisitable, diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs index 199106914562..1ff65717e87d 100644 --- a/compiler/stable_mir/src/error.rs +++ b/compiler/stable_mir/src/error.rs @@ -8,6 +8,11 @@ use std::convert::From; use std::fmt::{Debug, Display, Formatter}; use std::{error, fmt}; +macro_rules! error { + ($fmt: literal $(,)?) => { Error(format!($fmt)) }; + ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg:tt)*)) }; + } + /// An error type used to represent an error that has already been reported by the compiler. #[derive(Clone, Copy, PartialEq, Eq)] pub enum CompilerError { @@ -24,10 +29,10 @@ pub enum CompilerError { /// A generic error to represent an API request that cannot be fulfilled. #[derive(Debug)] -pub struct Error(String); +pub struct Error(pub(crate) String); impl Error { - pub(crate) fn new(msg: String) -> Self { + pub fn new(msg: String) -> Self { Self(msg) } } diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index f316671b278e..91f1066d7205 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, @@ -31,11 +31,15 @@ use self::ty::{ #[macro_use] extern crate scoped_tls; +#[macro_use] pub mod error; 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, Const, RigidTy}; pub use error::*; use mir::mono::Instance; use ty::{FnDef, GenericArgs}; @@ -47,7 +51,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 +103,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 +129,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(&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 +223,31 @@ 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 whether this is a foreign item. + fn is_foreign_item(&self, item: CrateItem) -> bool; + + /// Returns the kind of a given algebraic data type + fn adt_kind(&self, def: AdtDef) -> AdtKind; + + /// Returns if the ADT is a box. + fn adt_is_box(&self, def: AdtDef) -> bool; + + /// Evaluate constant as a target usize. + fn eval_target_usize(&self, cnst: &Const) -> Result; + + /// Create a target usize constant for the given value. + fn usize_to_const(&self, val: u64) -> Result; + + /// Create a new type from the given kind. + fn new_rigid_ty(&self, kind: RigidTy) -> Ty; + + /// 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 +257,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; /// Get the instance type with generic substitutions applied and lifetimes erased. fn instance_ty(&self, instance: InstanceDef) -> Ty; @@ -234,18 +277,36 @@ pub trait Context { /// Resolve an instance from the given function definition and generic arguments. fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; + + /// 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; + + /// Resolve instance for a closure with the requested type. + fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option; } // 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(context: &dyn Context, f: F) -> Result +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 +321,7 @@ pub fn with(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 2e1714b49c18..2cbe6eb4ad11 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 351e7bb69c3f..ac07246dfd3e 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,6 +1,9 @@ -use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, Ty}; -use crate::Opaque; -use crate::Span; +use crate::mir::pretty::{function_body, pretty_statement}; +use crate::ty::{ + AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, +}; +use crate::{Error, Opaque, Span}; +use std::io; /// The SMIR representation of a single function. #[derive(Clone, Debug)] @@ -56,6 +59,28 @@ impl Body { pub fn locals(&self) -> &[LocalDecl] { &self.locals } + + pub fn dump(&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::>(); + writeln!(w, " }}").unwrap(); + Ok(()) + }) + .collect::, _>>()?; + Ok(()) + } } type LocalDecls = Vec; @@ -64,6 +89,7 @@ type LocalDecls = Vec; pub struct LocalDecl { pub ty: Ty, pub span: Span, + pub mutability: Mutability, } #[derive(Clone, Debug)] @@ -537,7 +563,7 @@ pub struct SwitchTarget { pub target: usize, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Shared, @@ -555,14 +581,14 @@ pub enum BorrowKind { }, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum MutBorrowKind { Default, TwoPhaseBorrow, ClosureCapture, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Mutability { Not, Mut, @@ -627,10 +653,16 @@ pub enum NullOp { } impl Operand { - pub fn ty(&self, locals: &[LocalDecl]) -> Ty { + /// Get the type of an operand relative to the local declaration. + /// + /// In order to retrieve the correct type, the `locals` argument must match the list of all + /// locals from the function body where this operand originates from. + /// + /// Errors indicate a malformed operand or incompatible locals list. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { match self { Operand::Copy(place) | Operand::Move(place) => place.ty(locals), - Operand::Constant(c) => c.ty(), + Operand::Constant(c) => Ok(c.ty()), } } } @@ -642,12 +674,57 @@ 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") + /// Resolve down the chain of projections to get the type referenced at the end of it. + /// E.g.: + /// Calling `ty()` on `var.field` should return the type of `field`. + /// + /// In order to retrieve the correct type, the `locals` argument must match the list of all + /// locals from the function body where this place originates from. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + let start_ty = locals[self.local].ty; + self.projection.iter().fold(Ok(start_ty), |place_ty, elem| { + let ty = place_ty?; + match elem { + ProjectionElem::Deref => Self::deref_ty(ty), + ProjectionElem::Field(_idx, fty) => Ok(*fty), + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { + Self::index_ty(ty) + } + ProjectionElem::Subslice { from, to, from_end } => { + Self::subslice_ty(ty, from, to, from_end) + } + ProjectionElem::Downcast(_) => Ok(ty), + ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), + } + }) + } + + fn index_ty(ty: Ty) -> Result { + ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}")) + } + + fn subslice_ty(ty: Ty, from: &u64, to: &u64, from_end: &bool) -> Result { + let ty_kind = ty.kind(); + match ty_kind { + TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty), + TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array( + inner, + to.checked_sub(*from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?, + ), + TyKind::RigidTy(RigidTy::Array(inner, size)) => { + let size = size.eval_target_usize()?; + let len = size - from - to; + Ty::try_new_array(inner, len) + } + _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))), + } + } + + fn deref_ty(ty: Ty) -> Result { + let deref_ty = ty + .kind() + .builtin_deref(true) + .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?; + Ok(deref_ty.ty) } } diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 8f533349848e..8c43f2c4b8f2 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 std::fmt::Debug; +use crate::ty::{ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; +use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque}; +use std::fmt::{Debug, Formatter}; -#[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, 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 { with(|context| context.instance_body(self.def)) } @@ -54,6 +54,42 @@ 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 { + 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 { + with(|context| { + context.resolve_closure(def, args, kind).ok_or_else(|| { + crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) + }) + }) + } +} + +impl Debug for Instance { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Instance") + .field("kind", &self.kind) + .field("def", &self.mangled_name()) + .finish() + } } /// Try to convert a crate item into an instance. @@ -86,12 +122,36 @@ impl TryFrom for CrateItem { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +impl From 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 for StaticDef { + type Error = crate::Error; + + fn try_from(value: CrateItem) -> Result { + 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 000000000000..e52c3360ce42 --- /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 475d6e9763d9..40bedd67352f 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -142,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) } } @@ -157,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)); } @@ -417,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 e95c09abe788..f0b11dce9aac 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -1,10 +1,10 @@ use super::{ mir::Safety, mir::{Body, Mutability}, - with, AllocId, DefId, Symbol, + with, AllocId, DefId, Error, 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); @@ -15,6 +15,21 @@ impl Debug for Ty { } } +/// Constructors for `Ty`. +impl Ty { + /// Create a new type from a given kind. + /// + /// Note that not all types may be supported at this point. + fn from_rigid_kind(kind: RigidTy) -> Ty { + with(|cx| cx.new_rigid_ty(kind)) + } + + /// Create a new array type. + pub fn try_new_array(elem_ty: Ty, size: u64) -> Result { + Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, Const::try_from_target_usize(size)?))) + } +} + impl Ty { pub fn kind(&self) -> TyKind { with(|context| context.ty_kind(*self)) @@ -47,6 +62,16 @@ impl Const { pub fn ty(&self) -> Ty { self.ty } + + /// Creates an interned usize constant. + fn try_from_target_usize(val: u64) -> Result { + with(|cx| cx.usize_to_const(val)) + } + + /// Try to evaluate to a target `usize`. + pub fn eval_target_usize(&self) -> Result { + with(|cx| cx.eval_target_usize(self)) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -135,6 +160,78 @@ 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> { + 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 + } + } + + /// Returns the type of `ty[i]` for builtin types. + pub fn builtin_index(&self) -> Option { + match self.rigid()? { + RigidTy::Array(ty, _) | RigidTy::Slice(ty) => Some(*ty), + _ => None, + } + } + + /// Returns the type and mutability of `*ty` for builtin types. + /// + /// The parameter `explicit` indicates if this is an *explicit* dereference. + /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. + pub fn builtin_deref(&self, explicit: bool) -> Option { + match self.rigid()? { + RigidTy::Adt(def, args) if def.is_box() => { + Some(TypeAndMut { ty: *args.0.first()?.ty()?, mutability: Mutability::Not }) + } + RigidTy::Ref(_, ty, mutability) => { + Some(TypeAndMut { ty: *ty, mutability: *mutability }) + } + RigidTy::RawPtr(ty, mutability) if explicit => { + Some(TypeAndMut { ty: *ty, mutability: *mutability }) + } + _ => None, + } + } +} + +pub struct TypeAndMut { + pub ty: Ty, + pub mutability: Mutability, +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum RigidTy { Bool, @@ -159,6 +256,12 @@ pub enum RigidTy { CoroutineWitness(CoroutineWitnessDef, GenericArgs), } +impl From for TyKind { + fn from(value: RigidTy) -> Self { + TyKind::RigidTy(value) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum IntTy { Isize, @@ -218,6 +321,47 @@ 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)) + } + + pub fn is_box(&self) -> bool { + with(|cx| cx.adt_is_box(*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); @@ -286,6 +430,14 @@ impl GenericArgKind { _ => panic!("{self:?}"), } } + + /// Return the generic argument type if applicable, otherwise return `None`. + pub fn ty(&self) -> Option<&Ty> { + match self { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + } + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -355,6 +507,30 @@ pub struct Binder { pub bound_vars: Vec, } +impl Binder { + pub fn skip_binder(self) -> T { + self.value + } + + pub fn map_bound_ref(&self, f: F) -> Binder + 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(self, f: F) -> Binder + 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 { pub value: T, @@ -393,12 +569,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 { + pub fn with_self_ty(&self, self_ty: Ty) -> Binder { + 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, @@ -504,10 +695,39 @@ impl TraitDecl { pub type ImplTrait = EarlyBinder; +/// 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 { + 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/config.example.toml b/config.example.toml index e5df28a49af6..170856bd97db 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/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index da43d17bf362..01a6db14474b 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -36,12 +36,12 @@ impl SpecFromElem for i8 { if elem == 0 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } + let mut v = Vec::with_capacity_in(n, alloc); unsafe { - let mut v = Vec::with_capacity_in(n, alloc); ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); v.set_len(n); - v } + v } } @@ -51,11 +51,26 @@ impl SpecFromElem for u8 { if elem == 0 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } + let mut v = Vec::with_capacity_in(n, alloc); unsafe { - let mut v = Vec::with_capacity_in(n, alloc); ptr::write_bytes(v.as_mut_ptr(), elem, n); v.set_len(n); - v } + v + } +} + +// A better way would be to implement this for all ZSTs which are `Copy` and have trivial `Clone` +// but the latter cannot be detected currently +impl SpecFromElem for () { + #[inline] + fn from_elem(_elem: (), n: usize, alloc: A) -> Vec<(), A> { + let mut v = Vec::with_capacity_in(n, alloc); + // SAFETY: the capacity has just been set to `n` + // and `()` is a ZST with trivial `Clone` implementation + unsafe { + v.set_len(n); + } + v } } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 9c8d7bbd9997..34213637a32f 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/ffi/mod.rs b/library/core/src/ffi/mod.rs index 6908c824f44b..7340ad90da50 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/intrinsics.rs b/library/core/src/intrinsics.rs index c5aef67b5df8..f25ca9e2b186 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2487,12 +2487,6 @@ extern "rust-intrinsic" { where G: FnOnce, F: FnOnce; - - /// 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(arg: *const Option) -> *const T; } // Some functions are defined here because they accidentally got made diff --git a/library/core/src/iter/adapters/copied.rs b/library/core/src/iter/adapters/copied.rs index 8f6b2904eae4..7a2c9d839b7e 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> { - 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 693479977db5..32308c84d71c 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 = (&val as *const Option) + .byte_add(core::mem::offset_of!(Option, 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/lib.rs b/library/core/src/lib.rs index 4c6d5df389c0..921a0fb6a9f8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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/mod.rs b/library/core/src/mem/mod.rs index b810318fe7d3..bf10ada0176c 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1132,10 +1132,12 @@ impl fmt::Debug for Discriminant { /// /// [Reference]: ../../reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations /// -/// The value of a [`Discriminant`] is independent of any *lifetimes* in `T`. As such, reading -/// or writing a `Discriminant>` as a `Discriminant>` (whether via [`transmute`] or -/// otherwise) is always sound. Note that this is **not** true for other kinds of generic -/// parameters; `Discriminant>` and `Discriminant>` might be incompatible. +/// The value of a [`Discriminant`] is independent of any *free lifetimes* in `T`. As such, +/// reading or writing a `Discriminant>` as a `Discriminant>` (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>` and +/// `Discriminant>` as well as `Discriminant Trait<'a>>>` and +/// `Discriminant>>` may be incompatible. /// /// # Examples /// diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index f60626b00dc8..709eba2ffc9a 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 0a87021d8c18..73fa61574cc4 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/option.rs b/library/core/src/option.rs index 4ddcc49c989c..bfd6aee4a235 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`] has the same size and alignment as `T`. In some +//! [`Option`] has the same size, alignment, and [function call ABI] as `T`. In some //! of these cases, Rust further guarantees that //! `transmute::<_, Option>([0u8; size_of::()])` is sound and //! produces `Option::::None`. These cases are identified by the @@ -127,7 +127,7 @@ //! //! | `T` | `transmute::<_, Option>([0u8; size_of::()])` sound? | //! |---------------------------------------------------------------------|----------------------------------------------------------------------| -//! | [`Box`] | when `U: Sized` | +//! | [`Box`] (specifically, only `Box`) | 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`] | 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`]: ../../std/boxed/struct.Box.html //! [`num::NonZero*`]: crate::num //! [`ptr::NonNull`]: crate::ptr::NonNull +//! [function call ABI]: ../primitive.fn.html#abi-compatibility //! //! This is called the "null pointer optimization" or NPO. //! @@ -779,7 +780,7 @@ impl Option { // `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()), ) } @@ -835,8 +836,7 @@ impl Option { // 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()), ) } diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 87e492108749..a7e20407cece 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` (specifically, only `Box`), and +/// `NonNull` 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 (`::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` 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`, and the value used for the argument is `None`, then this call is Undefined +/// Behavior since transmuting `None::` 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 bbf7199fffa0..e578d2257f6e 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() -> 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 { 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 = >::dangling().as_ptr(); + /// let two = >::dangling().as_ptr(); + /// let four = >::dangling().as_ptr(); + /// + /// assert_eq!(four.mask(Alignment::of::().mask()), four); + /// assert_eq!(four.mask(Alignment::of::().mask()), four); + /// assert_eq!(four.mask(Alignment::of::().mask()), four); + /// assert_ne!(one.mask(Alignment::of::().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/str/pattern.rs b/library/core/src/str/pattern.rs index d5d6d60acf61..701e61e66157 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/time.rs b/library/core/src/time.rs index 6ef35d8414be..b677776443fe 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -461,6 +461,27 @@ impl Duration { self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 } + /// Computes the absolute difference between `self` and `other`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(duration_abs_diff)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(100, 0).abs_diff(Duration::new(80, 0)), Duration::new(20, 0)); + /// assert_eq!(Duration::new(100, 400_000_000).abs_diff(Duration::new(110, 0)), Duration::new(9, 600_000_000)); + /// ``` + #[unstable(feature = "duration_abs_diff", issue = "117618")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn abs_diff(self, other: Duration) -> Duration { + if let Some(res) = self.checked_sub(other) { res } else { other.checked_sub(self).unwrap() } + } + /// Checked `Duration` addition. Computes `self + other`, returning [`None`] /// if overflow occurred. /// diff --git a/library/core/tests/fmt/num.rs b/library/core/tests/fmt/num.rs index 1ddcd5ab7955..bc387a46ea7d 100644 --- a/library/core/tests/fmt/num.rs +++ b/library/core/tests/fmt/num.rs @@ -152,8 +152,11 @@ fn test_format_int_exp_precision() { assert_eq!(format!("{:+10.3e}", 1), " +1.000e0"); // test precision remains correct when rounding to next power - - for i in i16::MIN..=i16::MAX { + #[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}"), diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index c167240ba243..3a459052d265 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -28,6 +28,7 @@ #![feature(core_private_diy_float)] #![feature(dec2flt)] #![feature(div_duration)] +#![feature(duration_abs_diff)] #![feature(duration_consts_float)] #![feature(duration_constants)] #![feature(exact_size_is_empty)] diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index 5defeb50d40f..5dc012bab4ad 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::.as_slice(), &[]); + assert_eq!(None::.as_mut_slice(), &[]); +} diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs index bd6e63edbb9e..24ab4be9d8c9 100644 --- a/library/core/tests/time.rs +++ b/library/core/tests/time.rs @@ -73,6 +73,19 @@ fn nanos() { assert_eq!(Duration::from_nanos(1_000_000_001).subsec_nanos(), 1); } +#[test] +fn abs_diff() { + assert_eq!(Duration::new(2, 0).abs_diff(Duration::new(1, 0)), Duration::new(1, 0)); + assert_eq!(Duration::new(1, 0).abs_diff(Duration::new(2, 0)), Duration::new(1, 0)); + assert_eq!(Duration::new(1, 0).abs_diff(Duration::new(1, 0)), Duration::new(0, 0)); + assert_eq!(Duration::new(1, 1).abs_diff(Duration::new(0, 2)), Duration::new(0, 999_999_999)); + assert_eq!(Duration::new(1, 1).abs_diff(Duration::new(2, 1)), Duration::new(1, 0)); + assert_eq!(Duration::MAX.abs_diff(Duration::MAX), Duration::ZERO); + assert_eq!(Duration::ZERO.abs_diff(Duration::ZERO), Duration::ZERO); + assert_eq!(Duration::MAX.abs_diff(Duration::ZERO), Duration::MAX); + assert_eq!(Duration::ZERO.abs_diff(Duration::MAX), Duration::MAX); +} + #[test] fn add() { assert_eq!(Duration::new(0, 0) + Duration::new(0, 1), Duration::new(0, 1)); diff --git a/library/std/build.rs b/library/std/build.rs index ad0a82eab8ca..11ba29766c17 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/backtrace.rs b/library/std/src/backtrace.rs index e7110aebdea1..7fcf2ee358c4 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -95,7 +95,7 @@ use crate::fmt; use crate::panic::UnwindSafe; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use crate::sync::LazyLock; -use crate::sys_common::backtrace::{lock, output_filename}; +use crate::sys_common::backtrace::{lock, output_filename, set_image_base}; use crate::vec::Vec; /// A captured OS thread stack backtrace. @@ -327,6 +327,7 @@ impl Backtrace { let _lock = lock(); let mut frames = Vec::new(); let mut actual_start = None; + set_image_base(); unsafe { backtrace_rs::trace_unsynchronized(|frame| { frames.push(BacktraceFrame { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 55aafc3db330..6c7494a6a6ff 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -507,6 +507,16 @@ impl Seek for BufReader { ) }) } + + /// 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 SizeHint for BufReader { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7d70a0bac24f..ff5b4e76d440 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 { 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/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs index 12c0293285a5..8fb6e99ecfa1 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/process.rs b/library/std/src/os/linux/process.rs index 2b3ff76d7a4a..51af432d0568 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -152,6 +152,12 @@ pub trait CommandExt: Sealed { /// in a guaranteed race-free manner (e.g. if the `clone3` system call /// is supported). Otherwise, [`pidfd`] will return an error. /// + /// If a pidfd has been successfully created and not been taken from the `Child` + /// then calls to `kill()`, `wait()` and `try_wait()` will use the pidfd + /// instead of the pid. This can prevent pid recycling races, e.g. + /// those caused by rogue libraries in the same process prematurely reaping + /// zombie children via `waitpid(-1, ...)` calls. + /// /// [`Command`]: process::Command /// [`Child`]: process::Child /// [`pidfd`]: fn@ChildExt::pidfd diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index a568f9b26baa..c29dd62bc06f 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/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index ac7c800ff6f8..5d8967bfbe68 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 fmt::Debug for RwLockReadGuard<'_, T> { +impl fmt::Debug for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } @@ -546,7 +546,7 @@ impl fmt::Display for RwLockReadGuard<'_, T> { } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RwLockWriteGuard<'_, T> { +impl 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 d58aa6c27b8c..b7357460f393 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 3bb492fa98bc..3d4ba509829d 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/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 72aca4e66593..ee86a5f88dd7 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -9,6 +9,8 @@ use core::ffi::NonZero_c_int; #[cfg(target_os = "linux")] use crate::os::linux::process::PidFd; +#[cfg(target_os = "linux")] +use crate::os::unix::io::AsRawFd; #[cfg(any( target_os = "macos", @@ -696,11 +698,12 @@ impl Command { msg.msg_iov = &mut iov as *mut _ as *mut _; msg.msg_iovlen = 1; - msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; - msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; // only attach cmsg if we successfully acquired the pidfd if pidfd >= 0 { + msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; + msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); (*hdr).cmsg_level = SOL_SOCKET; (*hdr).cmsg_type = SCM_RIGHTS; @@ -717,7 +720,7 @@ impl Command { // so we get a consistent SEQPACKET order match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { Ok(0) => {} - _ => rtabort!("failed to communicate with parent process"), + other => rtabort!("failed to communicate with parent process. {:?}", other), } } } @@ -748,7 +751,7 @@ impl Command { msg.msg_controllen = mem::size_of::() as _; msg.msg_control = &mut cmsg as *mut _ as *mut _; - match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, 0)) { + match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { Err(_) => return -1, Ok(_) => {} } @@ -787,7 +790,7 @@ pub struct Process { // On Linux, stores the pidfd created for this child. // This is None if the user did not request pidfd creation, // or if the pidfd could not be created for some reason - // (e.g. the `clone3` syscall was not available). + // (e.g. the `pidfd_open` syscall was not available). #[cfg(target_os = "linux")] pidfd: Option, } @@ -816,10 +819,23 @@ impl Process { // and used for another process, and we probably shouldn't be killing // random processes, so return Ok because the process has exited already. if self.status.is_some() { - Ok(()) - } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + return Ok(()); } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too + return cvt(unsafe { + libc::syscall( + libc::SYS_pidfd_send_signal, + pid_fd.as_raw_fd(), + libc::SIGKILL, + crate::ptr::null::<()>(), + 0, + ) + }) + .map(drop); + } + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } pub fn wait(&mut self) -> io::Result { @@ -827,6 +843,17 @@ impl Process { if let Some(status) = self.status { return Ok(status); } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt_r(|| unsafe { + libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) + })?; + let status = ExitStatus::from_waitid_siginfo(siginfo); + self.status = Some(status); + return Ok(status); + } let mut status = 0 as c_int; cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; self.status = Some(ExitStatus::new(status)); @@ -837,6 +864,25 @@ impl Process { if let Some(status) = self.status { return Ok(Some(status)); } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt(unsafe { + libc::waitid( + libc::P_PIDFD, + pid_fd.as_raw_fd() as u32, + &mut siginfo, + libc::WEXITED | libc::WNOHANG, + ) + })?; + if unsafe { siginfo.si_pid() } == 0 { + return Ok(None); + } + let status = ExitStatus::from_waitid_siginfo(siginfo); + self.status = Some(status); + return Ok(Some(status)); + } let mut status = 0 as c_int; let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; if pid == 0 { @@ -866,6 +912,20 @@ impl ExitStatus { ExitStatus(status) } + #[cfg(target_os = "linux")] + pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus { + let status = unsafe { siginfo.si_status() }; + + match siginfo.si_code { + libc::CLD_EXITED => ExitStatus((status & 0xff) << 8), + libc::CLD_KILLED => ExitStatus(status), + libc::CLD_DUMPED => ExitStatus(status | 0x80), + libc::CLD_CONTINUED => ExitStatus(0xffff), + libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f), + _ => unreachable!("waitid() should only return the above codes"), + } + } + fn exited(&self) -> bool { libc::WIFEXITED(self.0) } diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs index 6aa79e7f9e71..6e952ed7c42a 100644 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -64,7 +64,8 @@ fn test_command_fork_no_unwind() { #[test] #[cfg(target_os = "linux")] fn test_command_pidfd() { - use crate::os::fd::RawFd; + use crate::assert_matches::assert_matches; + use crate::os::fd::{AsRawFd, RawFd}; use crate::os::linux::process::{ChildExt, CommandExt}; use crate::process::Command; @@ -78,10 +79,22 @@ fn test_command_pidfd() { }; // always exercise creation attempts - let child = Command::new("echo").create_pidfd(true).spawn().unwrap(); + let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); // but only check if we know that the kernel supports pidfds if pidfd_open_available { - assert!(child.pidfd().is_ok()) + assert!(child.pidfd().is_ok()); } + if let Ok(pidfd) = child.pidfd() { + let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); + assert!(flags & libc::FD_CLOEXEC != 0); + } + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.code(), Some(1)); + + let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); + assert_matches!(child.try_wait(), Ok(None)); + child.kill().expect("failed to kill child"); + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.signal(), Some(libc::SIGKILL)); } diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index 06399e8a2742..667fd5169624 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -23,6 +23,8 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { /// This is necessary because the __cxa_thread_atexit_impl implementation /// std links to by default may be a C or C++ implementation that was not /// compiled using the Clang integer normalization option. + #[cfg(sanitizer_cfi_normalize_integers)] + use core::ffi::c_int; #[cfg(not(sanitizer_cfi_normalize_integers))] #[cfi_encoding = "i"] #[repr(transparent)] diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index 84e2c5d8d7f7..adfe721cfa9a 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -64,6 +64,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: let mut first_omit = true; // Start immediately if we're not using a short backtrace. let mut start = print_fmt != PrintFmt::Short; + set_image_base(); backtrace_rs::trace_unsynchronized(|frame| { if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { return false; @@ -213,3 +214,14 @@ pub fn output_filename( } fmt::Display::fmt(&file.display(), fmt) } + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +pub fn set_image_base() { + let image_base = crate::os::fortanix_sgx::mem::image_base(); + backtrace_rs::set_image_base(crate::ptr::invalid_mut(image_base as _)); +} + +#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))] +pub fn set_image_base() { + // nothing to do for platforms other than SGX +} diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index b98b13119e8a..b27b524b873b 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 f362c4111f10..087544397f5e 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 79424f28d27b..6e6c36600279 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/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index d36c41906b0c..ecaaf91aec12 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -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 af69860df1c5..c253dd2c6aad 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -887,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 @@ -1003,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/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index e3fd942fc380..834f88dc891e 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -7,8 +7,9 @@ //! Everything here is basically just a shim around calling either `rustbook` or //! `rustdoc`. -use std::fs; +use std::io::{self, Write}; use std::path::{Path, PathBuf}; +use std::{fs, mem}; use crate::core::build_steps::compile; use crate::core::build_steps::tool::{self, prepare_tool_cargo, SourceType, Tool}; @@ -388,6 +389,104 @@ impl Step for Standalone { } } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Releases { + compiler: Compiler, + target: TargetSelection, +} + +impl Step for Releases { + type Output = (); + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let builder = run.builder; + run.path("RELEASES.md").alias("releases").default_condition(builder.config.docs) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Releases { + compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + target: run.target, + }); + } + + /// Generates HTML release notes to include in the final docs bundle. + /// + /// This uses the same stylesheet and other tools as Standalone, but the + /// RELEASES.md file is included at the root of the repository and gets + /// the headline added. In the end, the conversion is done by Rustdoc. + fn run(self, builder: &Builder<'_>) { + let target = self.target; + let compiler = self.compiler; + let _guard = builder.msg_doc(compiler, "releases", target); + let out = builder.doc_out(target); + t!(fs::create_dir_all(&out)); + + builder.ensure(Standalone { + compiler: builder.compiler(builder.top_stage, builder.config.build), + target, + }); + + let version_info = builder.ensure(SharedAssets { target: self.target }).version_info; + + let favicon = builder.src.join("src/doc/favicon.inc"); + let footer = builder.src.join("src/doc/footer.inc"); + let full_toc = builder.src.join("src/doc/full-toc.inc"); + + let html = out.join("releases.html"); + let tmppath = out.join("releases.md"); + let inpath = builder.src.join("RELEASES.md"); + let rustdoc = builder.rustdoc(compiler); + if !up_to_date(&inpath, &html) + || !up_to_date(&footer, &html) + || !up_to_date(&favicon, &html) + || !up_to_date(&full_toc, &html) + || !(builder.config.dry_run() + || up_to_date(&version_info, &html) + || up_to_date(&rustdoc, &html)) + { + let mut tmpfile = t!(fs::File::create(&tmppath)); + t!(tmpfile.write_all(b"% Rust Release Notes\n\n")); + t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile)); + mem::drop(tmpfile); + let mut cmd = builder.rustdoc_cmd(compiler); + + // Needed for --index-page flag + cmd.arg("-Z").arg("unstable-options"); + + cmd.arg("--html-after-content") + .arg(&footer) + .arg("--html-before-content") + .arg(&version_info) + .arg("--html-in-header") + .arg(&favicon) + .arg("--markdown-no-toc") + .arg("--markdown-css") + .arg("rust.css") + .arg("--index-page") + .arg(&builder.src.join("src/doc/index.md")) + .arg("--markdown-playground-url") + .arg("https://play.rust-lang.org/") + .arg("-o") + .arg(&out) + .arg(&tmppath); + + if !builder.config.docs_minification { + cmd.arg("--disable-minification"); + } + + builder.run(&mut cmd); + } + + // We open doc/RELEASES.html as the default if invoked as `x.py doc --open RELEASES.md` + // with no particular explicit doc requested (e.g. library/core). + if builder.was_invoked_explicitly::(Kind::Doc) { + builder.open_in_browser(&html); + } + } +} + #[derive(Debug, Clone)] pub struct SharedAssetsPaths { pub version_info: PathBuf, diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 254fbc72a8c7..7908a3850c5c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -49,9 +49,9 @@ const MIR_OPT_BLESS_TARGET_MAPPING: &[(&str, &str)] = &[ ("i686-unknown-linux-musl", "x86_64-unknown-linux-musl"), ("i686-pc-windows-msvc", "x86_64-pc-windows-msvc"), ("i686-pc-windows-gnu", "x86_64-pc-windows-gnu"), - ("i686-apple-darwin", "x86_64-apple-darwin"), // ARM Macs don't have a corresponding 32-bit target that they can (easily) // build for, so there is no entry for "aarch64-apple-darwin" here. + // Likewise, i686 for macOS is no longer possible to build. ]; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -1932,6 +1932,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } } } + + // Some UI tests trigger behavior in rustc where it reads $CARGO and changes behavior if it exists. + // To make the tests work that rely on it not being set, make sure it is not set. + cmd.env_remove("CARGO"); + cmd.env("RUSTC_BOOTSTRAP", "1"); // Override the rustc version used in symbol hashes to reduce the amount of normalization // needed when diffing test output. diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index e8327b751d38..37cd96e7c63e 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 } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 142116f72765..c755324df1a5 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -810,6 +810,7 @@ impl<'a> Builder<'a> { doc::StyleGuide, doc::Tidy, doc::Bootstrap, + doc::Releases, ), Kind::Dist => describe!( dist::Docs, diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0a9175aa3ea5..fa8b0b20cec4 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 33b8f1a7ce72..a57b09e2ef68 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -98,7 +98,7 @@ const EXTRA_CHECK_CFGS: &[(Option, &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 59bd52a94dc8..a8106ca8ff92 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 07dcb9ea928f..000000000000 --- 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 3372baed999a..341e2de223aa 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/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 01b46118b9c1..3f4e025111ad 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -48,9 +48,6 @@ ENV \ AR_x86_64_pc_solaris=x86_64-pc-solaris2.10-ar \ CC_x86_64_pc_solaris=x86_64-pc-solaris2.10-gcc \ CXX_x86_64_pc_solaris=x86_64-pc-solaris2.10-g++ \ - AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \ - CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \ - CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++ \ CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-9 \ CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-9 \ AR_x86_64_fortanix_unknown_sgx=ar \ @@ -84,8 +81,6 @@ COPY host-x86_64/dist-various-2/build-fuchsia-toolchain.sh /tmp/ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-solaris-toolchain.sh /tmp/ RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 pc -# Build deprecated target 'x86_64-sun-solaris2.10' until removed -RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 sun RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc sun COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh @@ -120,7 +115,6 @@ ENV TARGETS=$TARGETS,wasm32-wasi ENV TARGETS=$TARGETS,wasm32-wasi-preview1-threads ENV TARGETS=$TARGETS,sparcv9-sun-solaris ENV TARGETS=$TARGETS,x86_64-pc-solaris -ENV TARGETS=$TARGETS,x86_64-sun-solaris ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32 ENV TARGETS=$TARGETS,x86_64-fortanix-unknown-sgx ENV TARGETS=$TARGETS,nvptx64-nvidia-cuda diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 821a09feb2d5..205ee263217a 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -25,7 +25,16 @@ cat /tmp/toolstate/toolstates.json python3 "$X_PY" test --stage 2 check-tools python3 "$X_PY" test --stage 2 src/tools/clippy python3 "$X_PY" test --stage 2 src/tools/rustfmt -python3 "$X_PY" test --stage 2 src/tools/miri + +# Testing Miri is a bit more complicated. +# We set the GC interval to the shortest possible value (0 would be off) to increase the chance +# that bugs which only surface when the GC runs at a specific time are more likely to cause CI to fail. +# This significantly increases the runtime of our test suite, or we'd do this in PR CI too. +if [[ -z "${PR_CI_JOB:-}" ]]; then + MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri +else + python3 "$X_PY" test --stage 2 src/tools/miri +fi # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. export BOOTSTRAP_SKIP_TARGET_SANITY=1 # we don't need `cc` for these targets diff --git a/src/ci/run.sh b/src/ci/run.sh index ce0dd6018af8..fa786c6c7e78 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 cbb4d5765ca3..77164ed4117a 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/book b/src/doc/book index 5b6c1ceaa62e..71352deb2072 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 5b6c1ceaa62ecbd6caef08df39b33b3938e99deb +Subproject commit 71352deb20727b4dda9ebfe8182709d5bf17dfea diff --git a/src/doc/index.md b/src/doc/index.md index 7c97c16c20bd..8ad5b427b552 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -4,12 +4,6 @@ nav { display: none; } -body { - font-family: serif; -} -h1, h2, h3, h4, h5, h6 { - font-family: sans-serif; -} h3 { font-size: 1.35rem; } @@ -119,10 +113,14 @@ documentation for your project _and_ all its dependencies in their correct version, and open it in your browser. Add the flag `--document-private-items` to also show items not marked `pub`. -### The Edition Guide +### Rust Version History + +[The Release Notes](releases.html) describes the change history of the Rust +toolchain and language. [The Edition Guide](edition-guide/index.html) describes the Rust editions and -their differences. +their differences. The latest version of the toolchain supports all +historical editions. ### The `rustc` Book diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 311b84962016..a6581246f968 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 311b84962016b28c75525c86e7b3f49fd9101a39 +Subproject commit a6581246f96837113968c02187db24f742af3908 diff --git a/src/doc/rust.css b/src/doc/rust.css index d33ab0326031..e0bf64c33bcf 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -1,6 +1,7 @@ /* General structure */ body { + font-family: serif; margin: 0 auto; padding: 0 15px; font-size: 18px; @@ -17,6 +18,9 @@ body { } } +h1, h2, h3, h4, h5, h6 { + font-family: sans-serif; +} h2, h3, h4, h5, h6 { font-weight: 400; line-height: 1.1; diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 77dbe5782b24..ddb8b1309f9e 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 77dbe5782b2488af3bb489ad702eaff438f465bf +Subproject commit ddb8b1309f9e905804cea1e248a4572fed6b464b diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1b27b77b3e6b..3bd90f7062dc 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -15,6 +15,8 @@ - [Platform Support](platform-support.md) - [Target Tier Policy](target-tier-policy.md) - [Template for Target-specific Documentation](platform-support/TEMPLATE.md) + - [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md) + - [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md) - [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md) - [\*-apple-tvos](platform-support/apple-tvos.md) - [\*-apple-watchos\*](platform-support/apple-watchos.md) diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md index 11d7b5b59381..9daa0810126b 100644 --- a/src/doc/rustc/src/json.md +++ b/src/doc/rustc/src/json.md @@ -11,9 +11,11 @@ If parsing the output with Rust, the [`cargo_metadata`](https://crates.io/crates/cargo_metadata) crate provides some support for parsing the messages. -When parsing, care should be taken to be forwards-compatible with future changes -to the format. Optional values may be `null`. New fields may be added. Enumerated -fields like "level" or "suggestion_applicability" may add new values. +Each type of message has a `$message_type` field which can be used to +distinguish the different formats. When parsing, care should be taken +to be forwards-compatible with future changes to the format. Optional +values may be `null`. New fields may be added. Enumerated fields like +"level" or "suggestion_applicability" may add new values. ## Diagnostics @@ -29,6 +31,8 @@ Diagnostics have the following format: ```javascript { + /* Type of this message */ + "$message_type": "diagnostic", /* The primary message. */ "message": "unused variable: `x`", /* The diagnostic code. @@ -217,6 +221,8 @@ flag][option-emit] documentation. ```javascript { + /* Type of this message */ + "$message_type": "artifact", /* The filename that was generated. */ "artifact": "libfoo.rlib", /* The kind of artifact that was generated. Possible values: @@ -239,6 +245,8 @@ information, even if the diagnostics have been suppressed (such as with an ```javascript { + /* Type of this message */ + "$message_type": "future_incompat", /* An array of objects describing a warning that will become a hard error in the future. */ diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 907e9c59f316..3671fdd3fd2b 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] @@ -166,7 +165,7 @@ target | std | notes `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4, glibc 2.23) -`sparcv9-sun-solaris` | ✓ | SPARC Solaris 10/11, illumos +`sparcv9-sun-solaris` | ✓ | SPARC Solaris 11, illumos `thumbv6m-none-eabi` | * | Bare ARMv6-M `thumbv7em-none-eabi` | * | Bare ARMv7E-M `thumbv7em-none-eabihf` | * | Bare ARMV7E-M, hardfloat @@ -185,7 +184,7 @@ target | std | notes `x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia` [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android -`x86_64-pc-solaris` | ✓ | 64-bit Solaris 10/11, illumos +`x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat `x86_64-unknown-redox` | ✓ | Redox OS @@ -216,6 +215,8 @@ host tools. target | std | host | notes -------|:---:|:----:|------- +[`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS +[`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin `aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64 [`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS [`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS Simulator @@ -341,7 +342,6 @@ target | std | host | notes [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | `x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support -`x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md new file mode 100644 index 000000000000..f93555129a20 --- /dev/null +++ b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md @@ -0,0 +1,36 @@ +# `arm64e-apple-darwin` + +**Tier: 3 (with Host Tools)** + +ARM64e macOS (11.0+, Big Sur+) + +## Target maintainers + +- Artyom Tetyukhin ([@arttet](https://github.com/https://github.com/arttet)) + +## Requirements + +Target for `macOS` on late-generation `M` series Apple chips. + +## Building the target + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: + +```toml +[build] +target = [ "arm64e-apple-darwin" ] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. +To compile for this target, you will need to build Rust with the target enabled (see [Building the target](#building-the-target) above). + +## Testing + +The target does support running binaries on macOS platforms with `arm64e` architecture. + +## Cross-compilation toolchains and C code + +The targets do support `C` code. +To build compatible `C` code, you have to use XCode with the same compiler and flags. diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md new file mode 100644 index 000000000000..2b2e0289975f --- /dev/null +++ b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md @@ -0,0 +1,37 @@ +# `arm64e-apple-ios` + +**Tier: 3** + +ARM64e iOS (12.0+) + +## Target maintainers + +- Artyom Tetyukhin ([@arttet](https://github.com/https://github.com/arttet)) + +## Requirements + +These targets only support cross-compilation. +The targets do support `std`. + +## Building the target + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: + +```toml +[build] +target = [ "arm64e-apple-ios" ] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. +To compile for this target, you will need to build Rust with the target enabled (see [Building the target](#building-the-target) above). + +## Testing + +The target does support running binaries on iOS platforms with `arm64e` architecture. + +## Cross-compilation toolchains and C code + +The targets do support `C` code. +To build compatible `C` code, you have to use XCode with the same compiler and flags. diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index 56a5016d0cee..1f45bd6c6b82 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -72,6 +72,7 @@ the standard library and functions that are included in the results list: | [`stdout, [u8]`][stdoutu8] | `Stdout::write` | | [`any -> !`][] | `panic::panic_any` | | [`vec::intoiter -> [T]`][iterasslice] | `IntoIter::as_slice` and `IntoIter::next_chunk` | +| [`iterator, fnmut -> T`][iterreduce] | `Iterator::reduce` and `Iterator::find` | [`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std [`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std @@ -81,6 +82,7 @@ the standard library and functions that are included in the results list: [`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std [stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std [iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter%20->%20[T]&filter-crate=std +[iterreduce]: ../../std/index.html?search=iterator%2C%20fnmut%20->%20T&filter-crate=std ### How type-based search works @@ -95,7 +97,9 @@ After deciding which items are type parameters and which are actual types, it then searches by matching up the function parameters (written before the `->`) and the return types (written after the `->`). Type matching is order-agnostic, and allows items to be left out of the query, but items that are present in the -query must be present in the function for it to match. +query must be present in the function for it to match. The `self` parameter is +treated the same as any other parameter, and `Self` is resolved to the +underlying type's name. Function signature searches can query generics, wrapped in angle brackets, and traits will be normalized like types in the search engine if no type parameters @@ -103,8 +107,37 @@ match them. For example, a function with the signature `fn my_function>(input: I) -> usize` can be matched with the following queries: -* `Iterator -> usize` -* `Iterator -> usize` +* `Iterator -> usize` +* `Iterator -> usize` (you can leave out the `Item=` part) +* `Iterator -> usize` (you can leave out iterator's generic entirely) +* `T -> usize` (you can match with a generic parameter) + +Each of the above queries is progressively looser, except the last one +would not match `dyn Iterator`, since that's not a type parameter. + +If a bound has multiple associated types, specifying the name allows you to +pick which one gets matched. If no name is specified, then the query will +match of any of them. For example, + +```rust +pub trait MyTrait { + type First; + type Second; +} + +/// This function can be found using the following search queries: +/// +/// MyTrait -> bool +/// MyTrait -> bool +/// MyTrait -> bool +/// MyTrait -> bool +/// +/// The following queries, however, will *not* match it: +/// +/// MyTrait -> bool +/// MyTrait -> bool +pub fn my_fn(x: impl MyTrait) -> bool { true } +``` Generics and function parameters are order-agnostic, but sensitive to nesting and number of matches. For example, a function with the signature @@ -134,6 +167,10 @@ Most of these limitations should be addressed in future version of Rustdoc. with that bound, it'll match, but `option -> T where T: Default` cannot be precisely searched for (use `option -> Default`). + * Supertraits, type aliases, and Deref are all ignored. Search mostly + operates on type signatures *as written*, and not as they are + represented within the compiler. + * Type parameters match type parameters, such that `Option` matches `Option`, but never match concrete types in function signatures. A trait named as if it were a type, such as `Option`, will match @@ -183,7 +220,8 @@ slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!]) type-sep = COMMA/WS *(COMMA/WS) nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) -generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep) +generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep) +generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep) CLOSE-ANGLE-BRACKET return-args = RETURN-ARROW *(type-sep) nonempty-arg-list @@ -230,6 +268,7 @@ DOUBLE-COLON = "::" QUOTE = %x22 COMMA = "," RETURN-ARROW = "->" +EQUAL = "=" ALPHA = %x41-5A / %x61-7A ; A-Z / a-z DIGIT = %x30-39 diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md index 0e15c79076fa..bcfab7904789 100644 --- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md +++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md @@ -35,6 +35,9 @@ and `cfg!(name = "value")` call. It will check that the `"value"` specified is p list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs` lint diagnostic. The default diagnostic level for this lint is `Warn`. +The command line `--cfg` arguments are currently *NOT* checked but may very well be checked in +the future. + To enable checking of values, but to provide an empty set of expected values, use these forms: ```bash diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index d18fdb0c97b7..0445daf0d4d7 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -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/types.rs b/src/librustdoc/clean/types.rs index 871738cdc07d..ded256fd75c5 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1651,6 +1651,13 @@ impl Type { } } + pub(crate) fn generic_args(&self) -> Option<&GenericArgs> { + match self { + Type::Path { path, .. } => path.generic_args(), + _ => None, + } + } + pub(crate) fn generics(&self) -> Option> { match self { Type::Path { path, .. } => path.generics(), @@ -2191,6 +2198,10 @@ impl Path { } } + pub(crate) fn generic_args(&self) -> Option<&GenericArgs> { + self.segments.last().map(|seg| &seg.args) + } + pub(crate) fn generics(&self) -> Option> { self.segments.last().and_then(|seg| { if let GenericArgs::AngleBracketed { ref args, .. } = seg.args { @@ -2232,6 +2243,39 @@ impl GenericArgs { GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), } } + pub(crate) fn bindings<'a>(&'a self) -> Box + 'a> { + match self { + &GenericArgs::AngleBracketed { ref bindings, .. } => Box::new(bindings.iter().cloned()), + &GenericArgs::Parenthesized { ref output, .. } => Box::new( + output + .as_ref() + .map(|ty| TypeBinding { + assoc: PathSegment { + name: sym::Output, + args: GenericArgs::AngleBracketed { + args: Vec::new().into_boxed_slice(), + bindings: ThinVec::new(), + }, + }, + kind: TypeBindingKind::Equality { term: Term::Type((**ty).clone()) }, + }) + .into_iter(), + ), + } + } +} + +impl<'a> IntoIterator for &'a GenericArgs { + type IntoIter = Box + 'a>; + type Item = GenericArg; + fn into_iter(self) -> Self::IntoIter { + match self { + &GenericArgs::AngleBracketed { ref args, .. } => Box::new(args.iter().cloned()), + &GenericArgs::Parenthesized { ref inputs, .. } => { + Box::new(inputs.iter().cloned().map(GenericArg::Type)) + } + } + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index d63bbe5896e8..9b1b6899751e 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -369,6 +369,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { &item, self.tcx, clean_impl_generics(self.cache.parent_stack.last()).as_ref(), + parent, self.cache, ), aliases: item.attrs.get_doc_aliases(), diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index def3a90c8e81..22527876b76f 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -49,6 +49,8 @@ pub(crate) enum ItemType { ProcAttribute = 23, ProcDerive = 24, TraitAlias = 25, + // This number is reserved for use in JavaScript + // Generic = 26, } impl Serialize for ItemType { diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index c49f1a4d37ec..2535668b83f5 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -1,5 +1,4 @@ use rustc_middle::ty::TyCtxt; -use rustc_span::Symbol; use crate::clean; use crate::config::RenderOptions; @@ -68,7 +67,6 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( // Render the crate documentation let mut work = vec![(format_renderer.make_child_renderer(), krate.module)]; - let unknown = Symbol::intern(""); while let Some((mut cx, item)) = work.pop() { if item.is_mod() && T::RUN_ON_MODULE { // modules are special because they add a namespace. We also need to @@ -90,8 +88,10 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( cx.mod_item_out()?; // FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special // cases. Use an explicit match instead. - } else if item.name.is_some() && !item.is_extern_crate() { - prof.generic_activity_with_arg("render_item", item.name.unwrap_or(unknown).as_str()) + } else if let Some(item_name) = item.name + && !item.is_extern_crate() + { + prof.generic_activity_with_arg("render_item", item_name.as_str()) .run(|| cx.item(item))?; } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 90691411f44a..8b3cd9ca6fc0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -113,6 +113,7 @@ pub(crate) struct IndexItem { pub(crate) struct RenderType { id: Option, generics: Option>, + bindings: Option)>>, } impl Serialize for RenderType { @@ -129,10 +130,15 @@ impl Serialize for RenderType { Some(RenderTypeId::Index(idx)) => *idx, _ => panic!("must convert render types to indexes before serializing"), }; - if let Some(generics) = &self.generics { + if self.generics.is_some() || self.bindings.is_some() { let mut seq = serializer.serialize_seq(None)?; seq.serialize_element(&id)?; - seq.serialize_element(generics)?; + seq.serialize_element(self.generics.as_ref().map(Vec::as_slice).unwrap_or_default())?; + if self.bindings.is_some() { + seq.serialize_element( + self.bindings.as_ref().map(Vec::as_slice).unwrap_or_default(), + )?; + } seq.end() } else { id.serialize(serializer) @@ -140,13 +146,31 @@ impl Serialize for RenderType { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum RenderTypeId { DefId(DefId), Primitive(clean::PrimitiveType), + AssociatedType(Symbol), Index(isize), } +impl Serialize for RenderTypeId { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let id = match &self { + // 0 is a sentinel, everything else is one-indexed + // concrete type + RenderTypeId::Index(idx) if *idx >= 0 => idx + 1, + // generic type parameter + RenderTypeId::Index(idx) => *idx, + _ => panic!("must convert render types to indexes before serializing"), + }; + id.serialize(serializer) + } +} + /// Full type of functions/methods in the search index. #[derive(Debug)] pub(crate) struct IndexItemFunctionType { @@ -171,17 +195,22 @@ impl Serialize for IndexItemFunctionType { } else { let mut seq = serializer.serialize_seq(None)?; match &self.inputs[..] { - [one] if one.generics.is_none() => seq.serialize_element(one)?, + [one] if one.generics.is_none() && one.bindings.is_none() => { + seq.serialize_element(one)? + } _ => seq.serialize_element(&self.inputs)?, } match &self.output[..] { [] if self.where_clause.is_empty() => {} - [one] if one.generics.is_none() => seq.serialize_element(one)?, + [one] if one.generics.is_none() && one.bindings.is_none() => { + seq.serialize_element(one)? + } _ => seq.serialize_element(&self.output)?, } for constraint in &self.where_clause { if let [one] = &constraint[..] && one.generics.is_none() + && one.bindings.is_none() { seq.serialize_element(one)?; } else { diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 1284f69e5d77..b3ae720fcf60 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -3,8 +3,10 @@ use std::collections::BTreeMap; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; use rustc_span::symbol::Symbol; use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer}; +use thin_vec::ThinVec; use crate::clean; use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate}; @@ -22,6 +24,7 @@ pub(crate) fn build_index<'tcx>( ) -> String { let mut itemid_to_pathid = FxHashMap::default(); let mut primitives = FxHashMap::default(); + let mut associated_types = FxHashMap::default(); let mut crate_paths = vec![]; // Attach all orphan items to the type's definition if the type @@ -38,7 +41,13 @@ pub(crate) fn build_index<'tcx>( parent: Some(parent), parent_idx: None, impl_id, - search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache), + search_type: get_function_type_for_search( + item, + tcx, + impl_generics.as_ref(), + Some(parent), + cache, + ), aliases: item.attrs.get_doc_aliases(), deprecation: item.deprecation(tcx), }); @@ -76,31 +85,81 @@ pub(crate) fn build_index<'tcx>( let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new()); for item in search_index.iter_mut() { fn insert_into_map( - ty: &mut RenderType, map: &mut FxHashMap, itemid: F, lastpathid: &mut isize, crate_paths: &mut Vec<(ItemType, Vec)>, item_type: ItemType, path: &[Symbol], - ) { + ) -> RenderTypeId { match map.entry(itemid) { - Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())), + Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()), Entry::Vacant(entry) => { let pathid = *lastpathid; entry.insert(pathid); *lastpathid += 1; crate_paths.push((item_type, path.to_vec())); - ty.id = Some(RenderTypeId::Index(pathid)); + RenderTypeId::Index(pathid) } } } + fn convert_render_type_id( + id: RenderTypeId, + cache: &mut Cache, + itemid_to_pathid: &mut FxHashMap, + primitives: &mut FxHashMap, + associated_types: &mut FxHashMap, + lastpathid: &mut isize, + crate_paths: &mut Vec<(ItemType, Vec)>, + ) -> Option { + let Cache { ref paths, ref external_paths, .. } = *cache; + match id { + RenderTypeId::DefId(defid) => { + if let Some(&(ref fqp, item_type)) = + paths.get(&defid).or_else(|| external_paths.get(&defid)) + { + Some(insert_into_map( + itemid_to_pathid, + ItemId::DefId(defid), + lastpathid, + crate_paths, + item_type, + fqp, + )) + } else { + None + } + } + RenderTypeId::Primitive(primitive) => { + let sym = primitive.as_sym(); + Some(insert_into_map( + primitives, + sym, + lastpathid, + crate_paths, + ItemType::Primitive, + &[sym], + )) + } + RenderTypeId::Index(_) => Some(id), + RenderTypeId::AssociatedType(sym) => Some(insert_into_map( + associated_types, + sym, + lastpathid, + crate_paths, + ItemType::AssocType, + &[sym], + )), + } + } + fn convert_render_type( ty: &mut RenderType, cache: &mut Cache, itemid_to_pathid: &mut FxHashMap, primitives: &mut FxHashMap, + associated_types: &mut FxHashMap, lastpathid: &mut isize, crate_paths: &mut Vec<(ItemType, Vec)>, ) { @@ -111,48 +170,54 @@ pub(crate) fn build_index<'tcx>( cache, itemid_to_pathid, primitives, + associated_types, lastpathid, crate_paths, ); } } - let Cache { ref paths, ref external_paths, .. } = *cache; + if let Some(bindings) = &mut ty.bindings { + bindings.retain_mut(|(associated_type, constraints)| { + let converted_associated_type = convert_render_type_id( + *associated_type, + cache, + itemid_to_pathid, + primitives, + associated_types, + lastpathid, + crate_paths, + ); + let Some(converted_associated_type) = converted_associated_type else { + return false; + }; + *associated_type = converted_associated_type; + for constraint in constraints { + convert_render_type( + constraint, + cache, + itemid_to_pathid, + primitives, + associated_types, + lastpathid, + crate_paths, + ); + } + true + }); + } let Some(id) = ty.id.clone() else { assert!(ty.generics.is_some()); return; }; - match id { - RenderTypeId::DefId(defid) => { - if let Some(&(ref fqp, item_type)) = - paths.get(&defid).or_else(|| external_paths.get(&defid)) - { - insert_into_map( - ty, - itemid_to_pathid, - ItemId::DefId(defid), - lastpathid, - crate_paths, - item_type, - fqp, - ); - } else { - ty.id = None; - } - } - RenderTypeId::Primitive(primitive) => { - let sym = primitive.as_sym(); - insert_into_map( - ty, - primitives, - sym, - lastpathid, - crate_paths, - ItemType::Primitive, - &[sym], - ); - } - RenderTypeId::Index(_) => {} - } + ty.id = convert_render_type_id( + id, + cache, + itemid_to_pathid, + primitives, + associated_types, + lastpathid, + crate_paths, + ); } if let Some(search_type) = &mut item.search_type { for item in &mut search_type.inputs { @@ -161,6 +226,7 @@ pub(crate) fn build_index<'tcx>( cache, &mut itemid_to_pathid, &mut primitives, + &mut associated_types, &mut lastpathid, &mut crate_paths, ); @@ -171,6 +237,7 @@ pub(crate) fn build_index<'tcx>( cache, &mut itemid_to_pathid, &mut primitives, + &mut associated_types, &mut lastpathid, &mut crate_paths, ); @@ -182,6 +249,7 @@ pub(crate) fn build_index<'tcx>( cache, &mut itemid_to_pathid, &mut primitives, + &mut associated_types, &mut lastpathid, &mut crate_paths, ); @@ -442,12 +510,39 @@ pub(crate) fn get_function_type_for_search<'tcx>( item: &clean::Item, tcx: TyCtxt<'tcx>, impl_generics: Option<&(clean::Type, clean::Generics)>, + parent: Option, cache: &Cache, ) -> Option { + let mut trait_info = None; + let impl_or_trait_generics = impl_generics.or_else(|| { + if let Some(def_id) = parent + && let Some(trait_) = cache.traits.get(&def_id) + && let Some((path, _)) = + cache.paths.get(&def_id).or_else(|| cache.external_paths.get(&def_id)) + { + let path = clean::Path { + res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, def_id), + segments: path + .iter() + .map(|name| clean::PathSegment { + name: *name, + args: clean::GenericArgs::AngleBracketed { + args: Vec::new().into_boxed_slice(), + bindings: ThinVec::new(), + }, + }) + .collect(), + }; + trait_info = Some((clean::Type::Path { path }, trait_.generics.clone())); + Some(trait_info.as_ref().unwrap()) + } else { + None + } + }); let (mut inputs, mut output, where_clause) = match *item.kind { - clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache), - clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache), - clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache), + clean::FunctionItem(ref f) | clean::MethodItem(ref f, _) | clean::TyMethodItem(ref f) => { + get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache) + } _ => return None, }; @@ -457,14 +552,23 @@ pub(crate) fn get_function_type_for_search<'tcx>( Some(IndexItemFunctionType { inputs, output, where_clause }) } -fn get_index_type(clean_type: &clean::Type, generics: Vec) -> RenderType { +fn get_index_type( + clean_type: &clean::Type, + generics: Vec, + rgen: &mut FxHashMap)>, +) -> RenderType { RenderType { - id: get_index_type_id(clean_type), + id: get_index_type_id(clean_type, rgen), generics: if generics.is_empty() { None } else { Some(generics) }, + bindings: None, } } -fn get_index_type_id(clean_type: &clean::Type) -> Option { +fn get_index_type_id( + clean_type: &clean::Type, + rgen: &mut FxHashMap)>, +) -> Option { + use rustc_hir::def::{DefKind, Res}; match *clean_type { clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())), clean::DynTrait(ref bounds, _) => { @@ -472,18 +576,27 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option { } clean::Primitive(p) => Some(RenderTypeId::Primitive(p)), clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => { - get_index_type_id(type_) + get_index_type_id(type_, rgen) } // The type parameters are converted to generics in `simplify_fn_type` clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)), clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)), clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)), + clean::QPath(ref data) => { + if data.self_type.is_self_type() + && let Some(clean::Path { res: Res::Def(DefKind::Trait, trait_), .. }) = data.trait_ + { + let idx = -isize::try_from(rgen.len() + 1).unwrap(); + let (idx, _) = rgen + .entry(SimplifiedParam::AssociatedType(trait_, data.assoc.name)) + .or_insert_with(|| (idx, Vec::new())); + Some(RenderTypeId::Index(*idx)) + } else { + None + } + } // Not supported yet - clean::BareFunction(_) - | clean::Generic(_) - | clean::ImplTrait(_) - | clean::QPath { .. } - | clean::Infer => None, + clean::BareFunction(_) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, } } @@ -493,6 +606,9 @@ enum SimplifiedParam { Symbol(Symbol), // every argument-position impl trait is its own type parameter Anonymous(isize), + // in a trait definition, the associated types are all bound to + // their own type parameter + AssociatedType(DefId, Symbol), } /// The point of this function is to lower generics and types into the simplified form that the @@ -523,10 +639,17 @@ fn simplify_fn_type<'tcx, 'a>( } // First, check if it's "Self". + let mut is_self = false; let mut arg = if let Some(self_) = self_ { match &*arg { - Type::BorrowedRef { type_, .. } if type_.is_self_type() => self_, - type_ if type_.is_self_type() => self_, + Type::BorrowedRef { type_, .. } if type_.is_self_type() => { + is_self = true; + self_ + } + type_ if type_.is_self_type() => { + is_self = true; + self_ + } arg => arg, } } else { @@ -585,11 +708,19 @@ fn simplify_fn_type<'tcx, 'a>( } } if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) { - res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None }); + res.push(RenderType { + id: Some(RenderTypeId::Index(*idx)), + generics: None, + bindings: None, + }); } else { let idx = -isize::try_from(rgen.len() + 1).unwrap(); rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds)); - res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None }); + res.push(RenderType { + id: Some(RenderTypeId::Index(idx)), + generics: None, + bindings: None, + }); } } else if let Type::ImplTrait(ref bounds) = *arg { let mut type_bounds = Vec::new(); @@ -611,12 +742,16 @@ fn simplify_fn_type<'tcx, 'a>( } if is_return && !type_bounds.is_empty() { // In parameter position, `impl Trait` is a unique thing. - res.push(RenderType { id: None, generics: Some(type_bounds) }); + res.push(RenderType { id: None, generics: Some(type_bounds), bindings: None }); } else { // In parameter position, `impl Trait` is the same as an unnamed generic parameter. let idx = -isize::try_from(rgen.len() + 1).unwrap(); rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds)); - res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None }); + res.push(RenderType { + id: Some(RenderTypeId::Index(idx)), + generics: None, + bindings: None, + }); } } else if let Type::Slice(ref ty) = *arg { let mut ty_generics = Vec::new(); @@ -631,7 +766,7 @@ fn simplify_fn_type<'tcx, 'a>( is_return, cache, ); - res.push(get_index_type(arg, ty_generics)); + res.push(get_index_type(arg, ty_generics, rgen)); } else if let Type::Array(ref ty, _) = *arg { let mut ty_generics = Vec::new(); simplify_fn_type( @@ -645,7 +780,7 @@ fn simplify_fn_type<'tcx, 'a>( is_return, cache, ); - res.push(get_index_type(arg, ty_generics)); + res.push(get_index_type(arg, ty_generics, rgen)); } else if let Type::Tuple(ref tys) = *arg { let mut ty_generics = Vec::new(); for ty in tys { @@ -661,7 +796,7 @@ fn simplify_fn_type<'tcx, 'a>( cache, ); } - res.push(get_index_type(arg, ty_generics)); + res.push(get_index_type(arg, ty_generics, rgen)); } else { // This is not a type parameter. So for example if we have `T, U: Option`, and we're // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't. @@ -669,12 +804,16 @@ fn simplify_fn_type<'tcx, 'a>( // So in here, we can add it directly and look for its own type parameters (so for `Option`, // we will look for them but not for `T`). let mut ty_generics = Vec::new(); - if let Some(arg_generics) = arg.generics() { - for gen in arg_generics.iter() { + let mut ty_bindings = Vec::new(); + if let Some(arg_generics) = arg.generic_args() { + for ty in arg_generics.into_iter().filter_map(|gen| match gen { + clean::GenericArg::Type(ty) => Some(ty), + _ => None, + }) { simplify_fn_type( self_, generics, - gen, + &ty, tcx, recurse + 1, &mut ty_generics, @@ -683,17 +822,180 @@ fn simplify_fn_type<'tcx, 'a>( cache, ); } + for binding in arg_generics.bindings() { + simplify_fn_binding( + self_, + generics, + &binding, + tcx, + recurse + 1, + &mut ty_bindings, + rgen, + is_return, + cache, + ); + } } - let id = get_index_type_id(&arg); + // Every trait associated type on self gets assigned to a type parameter index + // this same one is used later for any appearances of these types + // + // for example, Iterator::next is: + // + // trait Iterator { + // fn next(&mut self) -> Option + // } + // + // Self is technically just Iterator, but we want to pretend it's more like this: + // + // fn next(self: Iterator) -> Option + if is_self + && let Type::Path { path } = arg + && let def_id = path.def_id() + && let Some(trait_) = cache.traits.get(&def_id) + && trait_.items.iter().any(|at| at.is_ty_associated_type()) + { + for assoc_ty in &trait_.items { + if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &*assoc_ty.kind + && let Some(name) = assoc_ty.name + { + let idx = -isize::try_from(rgen.len() + 1).unwrap(); + let (idx, stored_bounds) = rgen + .entry(SimplifiedParam::AssociatedType(def_id, name)) + .or_insert_with(|| (idx, Vec::new())); + let idx = *idx; + if stored_bounds.is_empty() { + // Can't just pass stored_bounds to simplify_fn_type, + // because it also accepts rgen as a parameter. + // Instead, have it fill in this local, then copy it into the map afterward. + let mut type_bounds = Vec::new(); + for bound in bounds { + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + simplify_fn_type( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut type_bounds, + rgen, + is_return, + cache, + ); + } + } + let stored_bounds = &mut rgen + .get_mut(&SimplifiedParam::AssociatedType(def_id, name)) + .unwrap() + .1; + if stored_bounds.is_empty() { + *stored_bounds = type_bounds; + } + } + ty_bindings.push(( + RenderTypeId::AssociatedType(name), + vec![RenderType { + id: Some(RenderTypeId::Index(idx)), + generics: None, + bindings: None, + }], + )) + } + } + } + let id = get_index_type_id(&arg, rgen); if id.is_some() || !ty_generics.is_empty() { res.push(RenderType { id, + bindings: if ty_bindings.is_empty() { None } else { Some(ty_bindings) }, generics: if ty_generics.is_empty() { None } else { Some(ty_generics) }, }); } } } +fn simplify_fn_binding<'tcx, 'a>( + self_: Option<&'a Type>, + generics: &Generics, + binding: &'a clean::TypeBinding, + tcx: TyCtxt<'tcx>, + recurse: usize, + res: &mut Vec<(RenderTypeId, Vec)>, + rgen: &mut FxHashMap)>, + is_return: bool, + cache: &Cache, +) { + let mut ty_binding_constraints = Vec::new(); + let ty_binding_assoc = RenderTypeId::AssociatedType(binding.assoc.name); + for gen in &binding.assoc.args { + match gen { + clean::GenericArg::Type(arg) => simplify_fn_type( + self_, + generics, + &arg, + tcx, + recurse + 1, + &mut ty_binding_constraints, + rgen, + is_return, + cache, + ), + clean::GenericArg::Lifetime(_) + | clean::GenericArg::Const(_) + | clean::GenericArg::Infer => {} + } + } + for binding in binding.assoc.args.bindings() { + simplify_fn_binding( + self_, + generics, + &binding, + tcx, + recurse + 1, + res, + rgen, + is_return, + cache, + ); + } + match &binding.kind { + clean::TypeBindingKind::Equality { term } => { + if let clean::Term::Type(arg) = &term { + simplify_fn_type( + self_, + generics, + arg, + tcx, + recurse + 1, + &mut ty_binding_constraints, + rgen, + is_return, + cache, + ); + } + } + clean::TypeBindingKind::Constraint { bounds } => { + for bound in &bounds[..] { + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + simplify_fn_type( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut ty_binding_constraints, + rgen, + is_return, + cache, + ); + } + } + } + } + res.push((ty_binding_assoc, ty_binding_constraints)); +} + /// Return the full list of types when bounds have been resolved. /// /// i.e. `fn foo>(x: u32, y: B)` will return @@ -701,13 +1003,15 @@ fn simplify_fn_type<'tcx, 'a>( fn get_fn_inputs_and_outputs<'tcx>( func: &Function, tcx: TyCtxt<'tcx>, - impl_generics: Option<&(clean::Type, clean::Generics)>, + impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>, cache: &Cache, ) -> (Vec, Vec, Vec>) { let decl = &func.decl; + let mut rgen: FxHashMap)> = Default::default(); + let combined_generics; - let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics { + let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics { match (impl_generics.is_empty(), func.generics.is_empty()) { (true, _) => (Some(impl_self), &func.generics), (_, true) => (Some(impl_self), impl_generics), @@ -729,8 +1033,6 @@ fn get_fn_inputs_and_outputs<'tcx>( (None, &func.generics) }; - let mut rgen: FxHashMap)> = Default::default(); - let mut arg_types = Vec::new(); for arg in decl.inputs.values.iter() { simplify_fn_type( diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index c7811b43d164..2338931a18fd 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -14,6 +14,7 @@ function initSearch(searchIndex){} * pathWithoutLast: Array, * pathLast: string, * generics: Array, + * bindings: Map<(string|integer), Array>, * }} */ let QueryElement; @@ -24,6 +25,7 @@ let QueryElement; * totalElems: number, * typeFilter: (null|string), * userQuery: string, + * isInBinding: (null|string), * }} */ let ParserState; @@ -191,8 +193,9 @@ let FunctionSearchType; /** * @typedef {{ * id: (null|number), - * ty: (null|number), + * ty: number, * generics: Array, + * bindings: Map>, * }} */ let FunctionType; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index a3606f4302be..22dcb8701431 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -23,27 +23,27 @@ const itemTypes = [ "import", "struct", "enum", - "fn", + "fn", // 5 "type", "static", "trait", "impl", - "tymethod", + "tymethod", // 10 "method", "structfield", "variant", "macro", - "primitive", + "primitive", // 15 "associatedtype", "constant", "associatedconstant", "union", - "foreigntype", + "foreigntype", // 20 "keyword", "existential", "attr", "derive", - "traitalias", + "traitalias", // 25 "generic", ]; @@ -298,7 +298,7 @@ function initSearch(rawSearchIndex) { } function isEndCharacter(c) { - return ",>-]".indexOf(c) !== -1; + return "=,>-]".indexOf(c) !== -1; } function isStopCharacter(c) { @@ -398,7 +398,7 @@ function initSearch(rawSearchIndex) { * @return {boolean} */ function isSeparatorCharacter(c) { - return c === ","; + return c === "," || c === "="; } /** @@ -500,6 +500,8 @@ function initSearch(rawSearchIndex) { " does not accept generic parameters", ]; } + const bindingName = parserState.isInBinding; + parserState.isInBinding = null; return { name: "never", id: null, @@ -507,7 +509,9 @@ function initSearch(rawSearchIndex) { pathWithoutLast: [], pathLast: "never", generics: [], + bindings: new Map(), typeFilter: "primitive", + bindingName, }; } if (path.startsWith("::")) { @@ -542,14 +546,27 @@ function initSearch(rawSearchIndex) { if (isInGenerics) { parserState.genericsElems += 1; } + const bindingName = parserState.isInBinding; + parserState.isInBinding = null; + const bindings = new Map(); return { name: name.trim(), id: null, fullPath: pathSegments, pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), pathLast: pathSegments[pathSegments.length - 1], - generics: generics, + generics: generics.filter(gen => { + // Syntactically, bindings are parsed as generics, + // but the query engine treats them differently. + if (gen.bindingName !== null) { + bindings.set(gen.bindingName.name, [gen, ...gen.bindingName.generics]); + return false; + } + return true; + }), + bindings, typeFilter, + bindingName, }; } @@ -608,6 +625,7 @@ function initSearch(rawSearchIndex) { } } else if ( c === "[" || + c === "=" || isStopCharacter(c) || isSpecialStartCharacter(c) || isSeparatorCharacter(c) @@ -657,6 +675,7 @@ function initSearch(rawSearchIndex) { parserState.pos += 1; getItemsBefore(query, parserState, generics, "]"); const typeFilter = parserState.typeFilter; + const isInBinding = parserState.isInBinding; if (typeFilter !== null && typeFilter !== "primitive") { throw [ "Invalid search type: primitive ", @@ -667,10 +686,16 @@ function initSearch(rawSearchIndex) { ]; } parserState.typeFilter = null; + parserState.isInBinding = null; parserState.totalElems += 1; if (isInGenerics) { parserState.genericsElems += 1; } + for (const gen of generics) { + if (gen.bindingName !== null) { + throw ["Type parameter ", "=", " cannot be within slice ", "[]"]; + } + } elems.push({ name: "[]", id: null, @@ -679,6 +704,8 @@ function initSearch(rawSearchIndex) { pathLast: "[]", generics, typeFilter: "primitive", + bindingName: isInBinding, + bindings: new Map(), }); } else { const isStringElem = parserState.userQuery[start] === "\""; @@ -705,15 +732,38 @@ function initSearch(rawSearchIndex) { if (start >= end && generics.length === 0) { return; } - elems.push( - createQueryElement( - query, - parserState, - parserState.userQuery.slice(start, end), - generics, - isInGenerics - ) - ); + if (parserState.userQuery[parserState.pos] === "=") { + if (parserState.isInBinding) { + throw ["Cannot write ", "=", " twice in a binding"]; + } + if (!isInGenerics) { + throw ["Type parameter ", "=", " must be within generics list"]; + } + const name = parserState.userQuery.slice(start, end).trim(); + if (name === "!") { + throw ["Type parameter ", "=", " key cannot be ", "!", " never type"]; + } + if (name.includes("!")) { + throw ["Type parameter ", "=", " key cannot be ", "!", " macro"]; + } + if (name.includes("::")) { + throw ["Type parameter ", "=", " key cannot contain ", "::", " path"]; + } + if (name.includes(":")) { + throw ["Type parameter ", "=", " key cannot contain ", ":", " type"]; + } + parserState.isInBinding = { name, generics }; + } else { + elems.push( + createQueryElement( + query, + parserState, + parserState.userQuery.slice(start, end), + generics, + isInGenerics + ) + ); + } } } @@ -737,6 +787,8 @@ function initSearch(rawSearchIndex) { // If this is a generic, keep the outer item's type filter around. const oldTypeFilter = parserState.typeFilter; parserState.typeFilter = null; + const oldIsInBinding = parserState.isInBinding; + parserState.isInBinding = null; let extra = ""; if (endChar === ">") { @@ -752,6 +804,9 @@ function initSearch(rawSearchIndex) { while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; if (c === endChar) { + if (parserState.isInBinding) { + throw ["Unexpected ", endChar, " after ", "="]; + } break; } else if (isSeparatorCharacter(c)) { parserState.pos += 1; @@ -791,7 +846,9 @@ function initSearch(rawSearchIndex) { throw [ "Expected ", ",", - " or ", + ", ", + "=", + ", or ", endChar, ...extra, ", found ", @@ -801,6 +858,8 @@ function initSearch(rawSearchIndex) { throw [ "Expected ", ",", + " or ", + "=", ...extra, ", found ", c, @@ -828,6 +887,7 @@ function initSearch(rawSearchIndex) { parserState.pos += 1; parserState.typeFilter = oldTypeFilter; + parserState.isInBinding = oldIsInBinding; } /** @@ -1054,6 +1114,11 @@ function initSearch(rawSearchIndex) { for (const elem2 of elem.generics) { convertTypeFilterOnElem(elem2); } + for (const constraints of elem.bindings.values()) { + for (const constraint of constraints) { + convertTypeFilterOnElem(constraint); + } + } } userQuery = userQuery.trim(); const parserState = { @@ -1063,6 +1128,7 @@ function initSearch(rawSearchIndex) { totalElems: 0, genericsElems: 0, typeFilter: null, + isInBinding: null, userQuery: userQuery.toLowerCase(), }; let query = newParsedQuery(userQuery); @@ -1318,7 +1384,7 @@ function initSearch(rawSearchIndex) { * then this function will try with a different solution, or bail with false if it * runs out of candidates. * - * @param {Array} fnTypes - The objects to check. + * @param {Array} fnTypesIn - The objects to check. * @param {Array} queryElems - The elements from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn @@ -1329,9 +1395,9 @@ function initSearch(rawSearchIndex) { */ function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) { /** - * @type Map + * @type Map|null */ - let mgens = new Map(mgensIn); + const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { return !solutionCb || solutionCb(mgens); } @@ -1339,192 +1405,232 @@ function initSearch(rawSearchIndex) { return false; } const ql = queryElems.length; - let fl = fnTypesIn.length; + const fl = fnTypesIn.length; + + // One element fast path / base case + if (ql === 1 && queryElems[0].generics.length === 0 + && queryElems[0].bindings.size === 0) { + const queryElem = queryElems[0]; + for (const fnType of fnTypesIn) { + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + continue; + } + if (fnType.id < 0 && queryElem.id < 0) { + if (mgens && mgens.has(fnType.id) && + mgens.get(fnType.id) !== queryElem.id) { + continue; + } + const mgensScratch = new Map(mgens); + mgensScratch.set(fnType.id, queryElem.id); + if (!solutionCb || solutionCb(mgensScratch)) { + return true; + } + } else if (!solutionCb || solutionCb(mgens ? new Map(mgens) : null)) { + // unifyFunctionTypeIsMatchCandidate already checks that ids match + return true; + } + } + for (const fnType of fnTypesIn) { + if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + continue; + } + if (fnType.id < 0) { + if (mgens && mgens.has(fnType.id) && + mgens.get(fnType.id) !== 0) { + continue; + } + const mgensScratch = new Map(mgens); + mgensScratch.set(fnType.id, 0); + if (unifyFunctionTypes( + whereClause[(-fnType.id) - 1], + queryElems, + whereClause, + mgensScratch, + solutionCb + )) { + return true; + } + } else if (unifyFunctionTypes( + fnType.generics, + queryElems, + whereClause, + mgens ? new Map(mgens) : null, + solutionCb + )) { + return true; + } + } + return false; + } + + // Multiple element recursive case /** * @type Array */ - let fnTypes = fnTypesIn.slice(); + const fnTypes = fnTypesIn.slice(); /** - * loop works by building up a solution set in the working arrays + * Algorithm works by building up a solution set in the working arrays * fnTypes gets mutated in place to make this work, while queryElems - * is left alone + * is left alone. * - * vvvvvvv `i` points here - * queryElems = [ good, good, good, unknown, unknown ], - * fnTypes = [ good, good, good, unknown, unknown ], - * ---------------- ^^^^^^^^^^^^^^^^ `j` iterates after `i`, - * | looking for candidates - * everything before `i` is the - * current working solution + * It works backwards, because arrays can be cheaply truncated that way. + * + * vvvvvvv `queryElem` + * queryElems = [ unknown, unknown, good, good, good ] + * fnTypes = [ unknown, unknown, good, good, good ] + * ^^^^^^^^^^^^^^^^ loop over these elements to find candidates * * Everything in the current working solution is known to be a good * match, but it might not be the match we wind up going with, because * there might be more than one candidate match, and we need to try them all * before giving up. So, to handle this, it backtracks on failure. - * - * @type Array<{ - * "fnTypesScratch": Array, - * "queryElemsOffset": integer, - * "fnTypesOffset": integer - * }> */ - const backtracking = []; - let i = 0; - let j = 0; - const backtrack = () => { - while (backtracking.length !== 0) { - // this session failed, but there are other possible solutions - // to backtrack, reset to (a copy of) the old array, do the swap or unboxing - const { - fnTypesScratch, - mgensScratch, - queryElemsOffset, - fnTypesOffset, - unbox, - } = backtracking.pop(); - mgens = new Map(mgensScratch); - const fnType = fnTypesScratch[fnTypesOffset]; - const queryElem = queryElems[queryElemsOffset]; - if (unbox) { - if (fnType.id < 0) { - if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { - continue; - } - mgens.set(fnType.id, 0); - } - const generics = fnType.id < 0 ? - whereClause[(-fnType.id) - 1] : - fnType.generics; - fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics); - fl = fnTypes.length; - // re-run the matching algorithm on this item - i = queryElemsOffset - 1; - } else { - if (fnType.id < 0) { - if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) { - continue; - } - mgens.set(fnType.id, queryElem.id); - } - fnTypes = fnTypesScratch.slice(); - fl = fnTypes.length; - const tmp = fnTypes[queryElemsOffset]; - fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset]; - fnTypes[fnTypesOffset] = tmp; - // this is known as a good match; go to the next one - i = queryElemsOffset; + const flast = fl - 1; + const qlast = ql - 1; + const queryElem = queryElems[qlast]; + let queryElemsTmp = null; + for (let i = flast; i >= 0; i -= 1) { + const fnType = fnTypes[i]; + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + continue; + } + let mgensScratch; + if (fnType.id < 0) { + mgensScratch = new Map(mgens); + if (mgensScratch.has(fnType.id) + && mgensScratch.get(fnType.id) !== queryElem.id) { + continue; } + mgensScratch.set(fnType.id, queryElem.id); + } else { + mgensScratch = mgens; + } + // fnTypes[i] is a potential match + // fnTypes[flast] is the last item in the list + // swap them, and drop the potential match from the list + // check if the remaining function types also match + fnTypes[i] = fnTypes[flast]; + fnTypes.length = flast; + if (!queryElemsTmp) { + queryElemsTmp = queryElems.slice(0, qlast); + } + const passesUnification = unifyFunctionTypes( + fnTypes, + queryElemsTmp, + whereClause, + mgensScratch, + mgensScratch => { + if (fnType.generics.length === 0 && queryElem.generics.length === 0 + && fnType.bindings.size === 0 && queryElem.bindings.size === 0) { + return !solutionCb || solutionCb(mgensScratch); + } + const solution = unifyFunctionTypeCheckBindings( + fnType, + queryElem, + whereClause, + mgensScratch + ); + if (!solution) { + return false; + } + const simplifiedGenerics = solution.simplifiedGenerics; + for (const simplifiedMgens of solution.mgens) { + const passesUnification = unifyFunctionTypes( + simplifiedGenerics, + queryElem.generics, + whereClause, + simplifiedMgens, + solutionCb + ); + if (passesUnification) { + return true; + } + } + return false; + } + ); + if (passesUnification) { return true; } - return false; - }; - for (i = 0; i !== ql; ++i) { - const queryElem = queryElems[i]; - /** - * list of potential function types that go with the current query element. - * @type Array - */ - const matchCandidates = []; - let fnTypesScratch = null; - let mgensScratch = null; - // don't try anything before `i`, because they've already been - // paired off with the other query elements - for (j = i; j !== fl; ++j) { - const fnType = fnTypes[j]; - if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { - if (!fnTypesScratch) { - fnTypesScratch = fnTypes.slice(); - } - unifyFunctionTypes( - fnType.generics, - queryElem.generics, - whereClause, - mgens, - mgensScratch => { - matchCandidates.push({ - fnTypesScratch, - mgensScratch, - queryElemsOffset: i, - fnTypesOffset: j, - unbox: false, - }); - return false; // "reject" all candidates to gather all of them - } - ); - } - if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { - if (!fnTypesScratch) { - fnTypesScratch = fnTypes.slice(); - } - if (!mgensScratch) { - mgensScratch = new Map(mgens); - } - backtracking.push({ - fnTypesScratch, - mgensScratch, - queryElemsOffset: i, - fnTypesOffset: j, - unbox: true, - }); - } + // backtrack + fnTypes[flast] = fnTypes[i]; + fnTypes[i] = fnType; + fnTypes.length = fl; + } + for (let i = flast; i >= 0; i -= 1) { + const fnType = fnTypes[i]; + if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + continue; } - if (matchCandidates.length === 0) { - if (backtrack()) { + let mgensScratch; + if (fnType.id < 0) { + mgensScratch = new Map(mgens); + if (mgensScratch.has(fnType.id) && mgensScratch.get(fnType.id) !== 0) { continue; - } else { - return false; } + mgensScratch.set(fnType.id, 0); + } else { + mgensScratch = mgens; } - // use the current candidate - const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop(); - if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) { - mgens.set(fnTypes[candidate].id, queryElems[i].id); - } - for (const [fid, qid] of mgensNew) { - mgens.set(fid, qid); - } - // `i` and `j` are paired off - // `queryElems[i]` is left in place - // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off - const tmp = fnTypes[candidate]; - fnTypes[candidate] = fnTypes[i]; - fnTypes[i] = tmp; - // write other candidates to backtracking queue - for (const otherCandidate of matchCandidates) { - backtracking.push(otherCandidate); - } - // If we're on the last item, check the solution with the callback - // backtrack if the callback says its unsuitable - while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) { - if (!backtrack()) { - return false; - } + const generics = fnType.id < 0 ? + whereClause[(-fnType.id) - 1] : + fnType.generics; + const bindings = fnType.bindings ? + Array.from(fnType.bindings.values()).flat() : + []; + const passesUnification = unifyFunctionTypes( + fnTypes.toSpliced(i, 1, ...generics, ...bindings), + queryElems, + whereClause, + mgensScratch, + solutionCb + ); + if (passesUnification) { + return true; } } - return true; + return false; } - function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) { + /** + * Check if this function is a match candidate. + * + * This function is all the fast checks that don't require backtracking. + * It checks that two items are not named differently, and is load-bearing for that. + * It also checks that, if the query has generics, the function type must have generics + * or associated type bindings: that's not load-bearing, but it prevents unnecessary + * backtracking later. + * + * @param {FunctionType} fnType + * @param {QueryElement} queryElem + * @param {[FunctionSearchType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensIn - Map functions generics to query generics. + * @returns {boolean} + */ + function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgensIn) { // type filters look like `trait:Read` or `enum:Result` if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { return false; } // fnType.id < 0 means generic // queryElem.id < 0 does too - // mgens[fnType.id] = queryElem.id - // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait + // mgensIn[fnType.id] = queryElem.id + // or, if mgensIn[fnType.id] = 0, then we've matched this generic with a bare trait // and should make that same decision everywhere it appears if (fnType.id < 0 && queryElem.id < 0) { - if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) { - return false; - } - for (const [fid, qid] of mgens.entries()) { - if (fnType.id !== fid && queryElem.id === qid) { + if (mgensIn) { + if (mgensIn.has(fnType.id) && mgensIn.get(fnType.id) !== queryElem.id) { return false; } - if (fnType.id === fid && queryElem.id !== qid) { - return false; + for (const [fid, qid] of mgensIn.entries()) { + if (fnType.id !== fid && queryElem.id === qid) { + return false; + } + if (fnType.id === fid && queryElem.id !== qid) { + return false; + } } } + return true; } else { if (queryElem.id === typeNameIdOfArrayOrSlice && (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) @@ -1536,7 +1642,12 @@ function initSearch(rawSearchIndex) { } // If the query elem has generics, and the function doesn't, // it can't match. - if (fnType.generics.length === 0 && queryElem.generics.length !== 0) { + if ((fnType.generics.length + fnType.bindings.size) === 0 && + queryElem.generics.length !== 0 + ) { + return false; + } + if (fnType.bindings.size < queryElem.bindings.size) { return false; } // If the query element is a path (it contains `::`), we need to check if this @@ -1565,9 +1676,87 @@ function initSearch(rawSearchIndex) { return false; } } + return true; } - return true; } + /** + * This function checks the associated type bindings. Any that aren't matched get converted + * to generics, and this function returns an array of the function's generics with these + * simplified bindings added to them. That is, it takes a path like this: + * + * Iterator + * + * ... if queryElem itself has an `Item=` in it, then this function returns an empty array. + * But if queryElem contains no Item=, then this function returns a one-item array with the + * ID of u32 in it, and the rest of the matching engine acts as if `Iterator` were + * the type instead. + * + * @param {FunctionType} fnType + * @param {QueryElement} queryElem + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map} mgensIn - Map functions generics to query generics. + * Never modified. + * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} + */ + function unifyFunctionTypeCheckBindings(fnType, queryElem, whereClause, mgensIn) { + if (fnType.bindings.size < queryElem.bindings.size) { + return false; + } + let simplifiedGenerics = fnType.generics || []; + if (fnType.bindings.size > 0) { + let mgensSolutionSet = [mgensIn]; + for (const [name, constraints] of queryElem.bindings.entries()) { + if (mgensSolutionSet.length === 0) { + return false; + } + if (!fnType.bindings.has(name)) { + return false; + } + const fnTypeBindings = fnType.bindings.get(name); + mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { + const newSolutions = []; + unifyFunctionTypes( + fnTypeBindings, + constraints, + whereClause, + mgens, + newMgens => { + newSolutions.push(newMgens); + // return `false` makes unifyFunctionTypes return the full set of + // possible solutions + return false; + } + ); + return newSolutions; + }); + } + if (mgensSolutionSet.length === 0) { + return false; + } + const binds = Array.from(fnType.bindings.entries()).flatMap(entry => { + const [name, constraints] = entry; + if (queryElem.bindings.has(name)) { + return []; + } else { + return constraints; + } + }); + if (simplifiedGenerics.length > 0) { + simplifiedGenerics = [...simplifiedGenerics, ...binds]; + } else { + simplifiedGenerics = binds; + } + return { simplifiedGenerics, mgens: mgensSolutionSet }; + } + return { simplifiedGenerics, mgens: [mgensIn] }; + } + /** + * @param {FunctionType} fnType + * @param {QueryElement} queryElem + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgens - Map functions generics to query generics. + * @returns {boolean} + */ function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) { if (fnType.id < 0 && queryElem.id >= 0) { if (!whereClause) { @@ -1575,7 +1764,7 @@ function initSearch(rawSearchIndex) { } // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic // mgens[fnType.id] === null indicates that we haven't decided yet - if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { + if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { return false; } // This is only a potential unbox if the search query appears in the where clause @@ -1583,8 +1772,12 @@ function initSearch(rawSearchIndex) { // `fn read_all(R) -> Result` // generic `R` is considered "unboxed" return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause); - } else if (fnType.generics && fnType.generics.length > 0) { - return checkIfInList(fnType.generics, queryElem, whereClause); + } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { + const simplifiedGenerics = [ + ...fnType.generics, + ...Array.from(fnType.bindings.values()).flat(), + ]; + return checkIfInList(simplifiedGenerics, queryElem, whereClause); } return false; } @@ -1619,15 +1812,17 @@ function initSearch(rawSearchIndex) { * @return {boolean} - Returns true if the type matches, false otherwise. */ function checkType(row, elem, whereClause) { - if (elem.id < 0) { - return row.id < 0 || checkIfInList(row.generics, elem, whereClause); - } - 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 (row.bindings.size === 0 && elem.bindings.size === 0) { + if (elem.id < 0) { + return row.id < 0 || checkIfInList(row.generics, elem, whereClause); + } + 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); + } } return unifyFunctionTypes([row], [elem], whereClause); } @@ -1974,7 +2169,7 @@ function initSearch(rawSearchIndex) { elem.id = match; } if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 - && elem.generics.length === 0) + && elem.generics.length === 0 && elem.bindings.size === 0) || elem.typeFilter === TY_GENERIC) { if (genericSymbols.has(elem.name)) { elem.id = genericSymbols.get(elem.name); @@ -2017,6 +2212,23 @@ function initSearch(rawSearchIndex) { for (const elem2 of elem.generics) { convertNameToId(elem2); } + elem.bindings = new Map(Array.from(elem.bindings.entries()) + .map(entry => { + const [name, constraints] = entry; + if (!typeNameIdMap.has(name)) { + parsedQuery.error = [ + "Type parameter ", + name, + " does not exist", + ]; + } + for (const elem2 of constraints) { + convertNameToId(elem2); + } + + return [typeNameIdMap.get(name), constraints]; + }) + ); } for (const elem of parsedQuery.elems) { @@ -2533,16 +2745,39 @@ ${item.displayPath}${name}\ function buildItemSearchType(type, lowercasePaths) { const PATH_INDEX_DATA = 0; const GENERICS_DATA = 1; - let pathIndex, generics; + const BINDINGS_DATA = 2; + let pathIndex, generics, bindings; if (typeof type === "number") { pathIndex = type; generics = []; + bindings = new Map(); } else { pathIndex = type[PATH_INDEX_DATA]; generics = buildItemSearchTypeAll( type[GENERICS_DATA], lowercasePaths ); + if (type.length > BINDINGS_DATA) { + bindings = new Map(type[BINDINGS_DATA].map(binding => { + const [assocType, constraints] = binding; + // Associated type constructors are represented sloppily in rustdoc's + // type search, to make the engine simpler. + // + // MyType=Result> is equivalent to MyType>=T> + // and both are, essentially + // MyType)>, except the tuple isn't actually there. + // It's more like the value of a type binding is naturally an array, + // which rustdoc calls "constraints". + // + // As a result, the key should never have generics on it. + return [ + buildItemSearchType(assocType, lowercasePaths).id, + buildItemSearchTypeAll(constraints, lowercasePaths), + ]; + })); + } else { + bindings = new Map(); + } } if (pathIndex < 0) { // types less than 0 are generic parameters @@ -2552,6 +2787,7 @@ ${item.displayPath}${name}\ ty: TY_GENERIC, path: null, generics, + bindings, }; } if (pathIndex === 0) { @@ -2561,6 +2797,7 @@ ${item.displayPath}${name}\ ty: null, path: null, generics, + bindings, }; } const item = lowercasePaths[pathIndex - 1]; @@ -2569,6 +2806,7 @@ ${item.displayPath}${name}\ ty: item.ty, path: item.path, generics, + bindings, }; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a43ea5582b78..5144bbdaf5e1 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -797,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/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index aed6796fa13c..83c7ccc9edeb 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -50,7 +50,9 @@ static HOSTS: &[&str] = &[ static TARGETS: &[&str] = &[ "aarch64-apple-darwin", + "arm64e-apple-darwin", "aarch64-apple-ios", + "arm64e-apple-ios", "aarch64-apple-ios-sim", "aarch64-unknown-fuchsia", "aarch64-linux-android", @@ -82,7 +84,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", @@ -150,7 +151,6 @@ static TARGETS: &[&str] = &[ "x86_64-linux-android", "x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc", - "x86_64-sun-solaris", "x86_64-pc-solaris", "x86_64-unikraft-linux-musl", "x86_64-unknown-freebsd", diff --git a/src/tools/cargo b/src/tools/cargo index 2c03e0e2dcd0..71cd3a926f0c 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 2c03e0e2dcd05dd064fcf10cc1050d342eaf67e3 +Subproject commit 71cd3a926f0cf41eeaf9f2a7f2194b2aff85b0f6 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 87a96bdeba65..e74df808e064 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 04af1b98b556..b1a59238c826 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 4b6688a76b46..3b138b480b6f 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 55c0e105b307..1803fc2d2f39 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 7c4aa5d45239..54f77b00190b 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 cda8d17eed44..4a1805f75235 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 2d41087b51d1..20f313201276 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 e898221ffa77..df48cc3f5e39 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(&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(deser: D) -> Result where @@ -83,7 +69,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { V: de::MapAccess<'de>, { let mut name = None; - let mut brace: Option = None; + let mut brace: Option = 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 eeea53ce46f8..ddc20f7f37ff 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 4bc27fd48e2f..84246d285c09 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 e3f4cf79d315..98299e1e4bd0 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 192bc7d9ddce..9799e703afe1 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` 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` is an Atomic `RC` and guarantees that updates to the reference counter are + /// Atomic. This is useful in multiprocessing scenarios. To send an `Arc` 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.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`"); + diag.note("if you intend to use `Arc` with `Send` and `Sync` traits"); + + 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 38364af27c7f..2dcaf1f01671 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 { - 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"; + 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; + 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); + 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 !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 04bf541a5bdc..28bd3fc70110 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 0e0d229e877b..2cb599964d2b 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(); @@ -427,8 +424,9 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { improvements.push(suggestion); } } - let nonminimal_bool_lint = |suggestions: Vec<_>| { + let nonminimal_bool_lint = |mut suggestions: Vec<_>| { if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow { + suggestions.sort(); span_lint_hir_and_then( self.cx, NONMINIMAL_BOOL, 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 739ce8f67c23..789cd3b6c212 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; - } - - // 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; - } + 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; } - 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; - } - } - - diag.span_suggestion( - e.span, - "if you would like to deref, try using `&**`", - format!( - "&**{}", - &snippet_opt(cx, deref_target.span).unwrap(), - ), - Applicability::MaybeIncorrect - ); - - } - ); - + // 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, + ); + + // 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, + ); + }, + ); } } } 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 f7a5b1857be2..ec681adb7aef 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 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.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(", "); + 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}"), - ); - } + 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 4dcc9cbe3a75..244e98eb6666 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 ffa571abb391..2ddb0f00ecdd 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 a83dfd94dc22..bd12ee406284 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 d14104029137..2a9f7fec1722 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 badadf2c9f65..3db1e3e6d97e 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 { } 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 82e07c98a7e0..a7d3868f76c6 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 0172e9336494..ff069860a116 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 61bfce07e1a0..849920bb76d5 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 d31c2268a657..69fa0821e3fb 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> { - 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 d21ef195d9b7..e5aaf88ab6c8 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 e3a09636e249..3b6d4886ba31 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 5d04ad0112d5..db850edd6409 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 a2005638d247..637d5aae1be3 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 { 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 { } fn is_crate_keyword(tt: &TokenTree) -> Option { - 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 2bca695c43b1..97b736dfd8fe 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 49452136d6f0..4774917c7b5c 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 { - 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 1a646ba38c35..85854a0dfb76 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 c74b2b8831ec..b325449c5a3b 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 ::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::>(); - let tys_str = generic_args - .iter() - .map(ToString::to_string) - .collect::>() - .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::>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::>() + .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 bf070432ef99..b90d01b765ac 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: // `::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 b296ea20f9c5..fb29703957d8 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, - }; + 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); - } - ); + 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; + 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; + // 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 6c109a51f83b..cbeb0050be0f 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 a450becc647f..9db56fa8ad01 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 3a331564db97..169c2a15db71 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 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; - } + 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"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - // Only care about `impl PartialEq for Foo` - // For `impl PartialEq 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" - ); - } + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq 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 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; - } + 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"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - // Only care about `impl PartialOrd for Foo` - // For `impl PartialOrd 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" - }; + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd 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" - ); - } - } - ); - } - }); - } + 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 5e46b29b6397..a1dd4805b9cd 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) -> 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 8982fca6e89b..ca277e7eded9 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) -> 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, 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, 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 [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 affd08221206..6f5a0cb8801b 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 3d0ddca19c9f..2f22f344a21b 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 02c53fafd696..e0df87e08da1 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 f976cfd3f225..b7e62e082e4d 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 e14b1c556ecc..07d025f68c32 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 4b5bcb06a1e8..08cb2114a2bf 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 efb69476b94a..753f75d83a84 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 506a1191747f..663c33e8ceed 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::().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::().unwrap(); - (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) - }, - FloatTy::F64 => { - let value = sym_str.parse::().unwrap(); + (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) + }, + FloatTy::F64 => { + let value = sym_str.parse::().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_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"); - } - - 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 09a9d9924de3..d522873472ba 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(); + && 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 = 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 { ) = 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) - { - 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, "..")); - } - } - } - 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 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 + { + 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, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 3c1f2d9d5dcd..c9868255dcf7 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 08ee7032c091..ec87629a3441 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 { - 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 2c9c43d3ea7a..70892ce608f6 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 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}`", - ), - ); + && 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; } + + // 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 74a60b6a0d24..18d11ccc0b53 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, "", &mut Applicability::MachineApplicable).maybe_par(); - let sugg = Sugg::hir_with_applicability( - cx, - expr, - "", - &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 ee66c841ed25..8fba41c0e24d 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 18f7368dafb7..bf96c0d62b05 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) .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 485235514ded..5e90fcd72ebe 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 e614a8f694fb..644b9cdaeb24 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 eaf80de38572..6594636d8840 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 24f62490f967..f2fac9a29cbe 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 859404289d97..fc66f86ae867 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 ff27a5d666d3..232d8eeb11b9 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 a84f7351ad66..f6e1281a2916 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 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 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 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, - ) + 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; + } + + 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 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(), ".."), + ); + + 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 c2f1f18e39d1..fa6536db796b 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 32b2cb4385f8..8e84c73666fc 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 90048d96c9c6..2b131d27b7b3 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 505aadd1a110..fce3b0e18b7d 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 000000000000..7755adc4c1d7 --- /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::::new(); + /// for (key, value) in my_map { /* ... */ } + /// ``` + /// Use instead: + /// ```no_run + /// let my_map = std::collections::HashMap::::new(); + /// let mut keys = my_map.keys().clone().collect::>(); + /// 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 a4f3d4983453..7db088f986f2 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 566901de3475..902b72ba5e44 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 0f17d26764c9..6fc14dd89417 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 2f6f36c39604..da269ec61ff6 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) => " ", - _ => "", - }; + let mutability = match mode { + BindingAnnotation(_, Mutability::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, ""), - default=snippet(cx, default.span, ""), - ); - 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, ""), + default=snippet(cx, default.span, ""), + ); + 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 79d728a021c3..d4f410de957c 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 ab978a677c23..c462c9330825 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::::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::::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::>(); 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::::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::::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::::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::::default()); - let disallowed_names = conf.disallowed_names.iter().cloned().collect::>(); - 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::>(); - 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::::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::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::>::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::>(); - 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::::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::::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::::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::::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 7517003be230..bb0edec33736 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) -> bool { - if_chain! { - if let Some(ident) = ident; - if ident.name == kw::SelfLower; - if !func.implicit_self.has_implicit_self(); + 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); - 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 - } + !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 2c14bb72a9e0..8f34a9b1fed7 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 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 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(()) - })(); - - - 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 - } + let consistent = Self::parts_consistent( + integral_group_size, + fractional_group_size, + num_lit.integer.len(), + fraction.len(), + ); + if !consistent { + return Err(WarningType::InconsistentDigitGrouping); }; - if should_warn { - warning_type.display(num_lit.format(), cx, span); - } + } + + 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); } } } @@ -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 1953ee8a7175..277062a84901 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 a9a9058c93f3..d484ce40d785 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), - )[..], - ); - } - // 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, - ); - }, + 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()"); + } + } 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 124a35f8f540..a726b1169563 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 d3fd0e8639e9..40d56240b9de 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 { + 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 7b7d19c753fe..e405829b2f4b 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 2c12d9582d63..227096251a55 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) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option { - 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 c4af46b8fd3a..e2be861a7089 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 5fffb27cda2f..15e11fd386cd 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 dfb800ccf714..d860b297a026 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 62a2ab1ccb4c..dd7fae79d9ba 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 0a2bd89eb3cd..38fdca573c6b 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 7f24f3c5dc28..9fd9b7a16312 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 5153070cfe66..21b9efba54c7 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) - } 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() - }; - - // 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) + && !uses_iter(cx, &iter_expr_struct, if_then) { - ".by_ref()" - } else { - "" - }; + 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() + }; - 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, - ); + // 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(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 9b158f18f62c..9b2e02058a6b 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 20333c150e3d..ea1d25d80e11 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 998de38a9952..a5d91c949bcd 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()); + && 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_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]) - }; + 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 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 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)> { - 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(); + }) + && 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)); - } + 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 cd614c8951c1..69c65cf305c7 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")); + 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::()` to obtain the size of `T` in bits", - "consider using", - sugg, - app, - ); - } + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::()` 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 170a040d4ae8..01eccb56a0aa 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 9a9e6af50849..b41bf2d767ed 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 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 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", }; - 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() { - - 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() = {}.strip_{kind_word}({}) ", - snippet(cx, target_arg.span, ".."), - snippet(cx, pattern.span, "..")))] - .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), + vec![( + test_span, + format!( + "if let Some() = {}.strip_{kind_word}({}) ", + snippet(cx, target_arg.span, ".."), + snippet(cx, pattern.span, "..") + ), + )] + .into_iter() + .chain(strippings.into_iter().map(|span| (span, "".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 817d072b9b3d..147e72ea8940 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 841c020f2d3d..bf0359694776 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.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.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 29b935fb61a8..48fc5746b3cd 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 { .. } - 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 // .... => match { .. } - 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")) - } 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"); - }, - ); } + { + 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")) + } 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 3329f93b73c4..c8a48246e676 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 cdb51c33aaf1..619ec83127ab 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> { - 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 b94501bf0ad3..3e79cabd795f 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 781ee138c76f..0627e458dfe0 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() + 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 { - 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 { - "" - }; - - 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 d51cca040a26..3f737da92c05 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 { - 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 e2ddf11abe2c..56123326fe4a 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) { + 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 - } else { - 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 + } 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 bd53ebd48c88..dd71560e169e 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 6fc79faddbee..745be671b86d 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 = 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 675a85ae5553..bd38648bcf1b 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, - }; + 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); + 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 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 a2903e52ae08..8a4c0ab90627 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 8f0083f812cc..8199366d175f 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 9a7c00823b67..2582f7edcf66 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> { - 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 4efe93d4b542..316b2f63e4ea 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 48efd0230175..86c414dafcca 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; + 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; + // 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 = "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 0fd6f533db0d..dd489fc250b7 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. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - 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>. fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - 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>>. fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - 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 760c8f59cec8..1517223ab9af 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 3a8cc41748eb..08bfa2e009b3 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; + 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 - } + 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 35370355f834..4a2124c74a88 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 649fc46e4adf..34159f2d150e 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 d5897822edaa..a37087d0abff 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}"); - } - - 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, - ); + 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), + ); + + 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 0e41f3c21076..c99cec067bf1 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; - } - - 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; + 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, + ); + + 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 c9d50a5b03db..d07e45434a7c 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 a8d4dd5e4f31..dc978c8a5845 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` 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` 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` 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 a49970b5351d..f0fc925799a3 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 495b266529bd..460ec7b3640a 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 7818be81119b..b05361ab2120 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 5bb8c7a6b948..844ab40cab1a 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; - - if let Some(check_result) = - offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref); - - 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 (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)) - } - }; - - 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) - }; - - (sugg, note_and_span, applicability) - } - CheckResult::PatternMatching { variant_span, variant_ident } => { - let pat = snippet(cx, variant_span, ""); - - (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); - } - }); } + + && let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind + && let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id) + + && 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 + + && 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(); + + ("", "*".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"; + + (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, ""); + + ( + 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); + } + }); } } 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 4040d3a5fe13..917a8e33eb9f 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::>(); - 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 `` in `collections::BTreeSet::::`) - 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::>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - 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::>() + && 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 `` in `collections::BTreeSet::::`) + 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::>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) + 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 2e1dd3ec649b..e1f1e4893558 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 e91ce64d8c8c..78a553eb8c0d 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 6686d42c95f8..efc3ddd20b4b 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 bbd964c10995..80160d17c82d 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 bde6f92b076e..dd741cd43f94 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 625325d4cf5d..c5dbb6ad98be 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) + && (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 { "" }; - 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(); - - 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 8f885e9f729b..fd4650e1e45f 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 39af52141bbc..fbe20dfe54ec 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 3031193e5312..b1af0083e65a 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 540425eef8c5..04bdbc1ea25f 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 { // `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 ab13d30d8452..61e74369cb05 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 e0f8cb1b955f..cc6eeaa86e5a 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 01cdd02e602d..e944eac91e7a 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 e74a764551c1..26ef0d10fed4 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 bcfd0de8efe1..6da9a87f5eec 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 a71a136eba57..31d44bc1b3fb 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, + mut allowed_dotfiles: FxHashSet, ) -> 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, - ); - } + .first() + { + 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); - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - if !ret_ty.contains(self_ty); - - 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 2855e23bf5b6..1a0fce2876d4 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 2ef71be3217f..79ed5515ea29 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 01655e860c43..81df32bdee2b 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 f2ef42933df6..e10bc0216e5f 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 7b81d4571b24..151110061337 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::>(); - 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::>() + && 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 cb6a2306857d..418e6a7d6a0a 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 b89c151461cf..e38c66f6741d 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 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, - ); - } + + 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 + } + }; + 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 1c07d2a3a59c..04a27cc98f3c 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 f253d8de91f9..1148628b0844 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 05a9a06c8c7d..6339011c92f5 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 4d704ec39ebb..3983f0c0cabd 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 9da61bca52c8..0e7ad8fc996e 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 0dc7fe2a2c5a..ed49233acb7f 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 3cb2719e4a03..c45212581eed 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 9eb8d6e6e787..60864902a489 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 bc9c518dbcf0..1ee655d61e1d 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 bb32b1bb7fc4..89cf20c14fc6 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 `>::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 6d51c4ab0544..ebbdde48b450 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 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); + && 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; - 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 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,) + }; - 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, - ); - } + 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 0c72c13a3caa..36497d59a5a8 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 d0c62fb56dc2..e2b389e96dae 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 ::> - 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::>().join("")` on an iterator"#, - "try using", - "collect::()".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::>().join("")` on an iterator"#, + "try using", + "collect::()".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 e62a65a27125..696e5e74d60a 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 { - 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 772686d93dd7..395b22ebc5d8 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::>(); - 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::>() + && 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::>()[..]; - 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::>()[..] + && 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 b5f810eddf4a..84ee64e88a6a 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 9f1f73e60218..9ad4250a141f 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 { - 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 73072718678e..9e87fb45aa65 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 e9f268da6915..0b829d99aef8 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 f4af5f37bf37..814fc3303b07 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 0d79ece087f5..c74d0d623df5 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, + _ => (), }; + } - // 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(); + // 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; + }; - 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 - ); - } + // 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); } } } 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 dccf72d3c84c..ff2792faf57a 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 973caa72b772..b5a884f7c8ba 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) -> 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 16ff98a5922c..f7e428151045 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 215161b04c7c..e5ebdc145103 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 efdc7560ee49..cd45467407eb 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 ebfd53f1ee9a..454fc6f56429 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 97e8f1c030ad..1712262ff3ec 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 dcfb109a4c49..f25475aaa8e3 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 cb2738947d49..6803034f4757 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 c71996131db4..70571d18e786 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); + && 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 (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); - } - }) + // 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 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); + } + }); } } } 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 c8888c744b66..0a95678d31aa 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 7bbf1fb4cd9a..490c3f9c1ab7 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 8fa461ac12c7..7c48b84e4306 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::>::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::>::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, "_")); + 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, + ); + } + + // 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", - slice_ty, + 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, - ); - } - - // cannot be destructured, no need for `*` suggestion - return; } + + 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, - ); + let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - 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, - ); - } + multispan_sugg(diag, "consider taking a reference instead", spans); + }; - return; - } - } - - let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - - 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 074c9fef1b2d..7ec0879ba385 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 56c67406d6fd..30aed8cc04a2 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; - } - }; - - 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_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; + } + }; + + 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 8b69f94cbba2..a6adb7c8a5f2 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 f7f9dccfbceb..abba622a285a 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; } - } - - 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, - ); - }, - ); + }); + self.impling_types = Some(impls); } + + // 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 3a28e511fdda..de8bb123e9be 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 54cec066ba10..3059eb25d29d 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 61622034d1aa..649a23565a96 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 e94e45899660..6cfcc81025de 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 62ef48c8a90c..df1476e68098 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(); + 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_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), - }) - } - } + 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), - ); + 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"), - )), - }; - } - }, - ); - } + 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 11c3a5417b55..1c6a8e16ae2e 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, + macro_braces: FxHashMap, done: FxHashSet, } impl MacroBraces { - pub fn new(conf: &FxHashSet) -> 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> { +fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBraces) -> Option { 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) -> FxHashMap { - 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::>(); +fn macro_braces(conf: &[MacroMatcher]) -> FxHashMap { + 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 f7d9650b2f8d..1a2b20bf4387 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 c4572a09db61..2f85130fba11 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 ec2bb869973f..e278cf9835a9 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, ""); - let rhs_str = snippet(cx, right_cond.span, ""); - // 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, ""); - let rhs_str = snippet(cx, right_const_expr.span, ""); - 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, ""); + let rhs_str = snippet(cx, right_cond.span, ""); + // 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, ""); + let rhs_str = snippet(cx, right_const_expr.span, ""); + 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 bce6bdcaf61c..0561739d160f 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 a0a8b6aabd9e..cace85a24513 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 a2c3a4d8ba77..cb3916484c1d 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 932dd470f9eb..7d8aa3f56fed 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 1229c202f5a0..9db2e24630aa 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 d7cbbe13a261..6e4e0c98d297 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 `[.]*` 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 `[.]*` 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 (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), - ), - }); } + + 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), + }; + + 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 38cd5043adc7..e661bfbb96c8 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 68d3d00ac16d..1b06762415d9 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 4db65b0d04fe..bbca8a123e68 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, 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); @@ -179,7 +177,7 @@ impl<'tcx> PassByRefOrValue { _ => (), } - 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 057b7e30642e..b638e83997a5 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 1d4b4d10d501..621a32d79bf3 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 _; @@ -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 b133635e8835..5c395143b9ac 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 1b3081abc13c..fd9de76bacfc 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 391c77dbf902..98f5a07da0df 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 59ce289e7d2b..b99af44655d6 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(); + 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)); + } - 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 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 8daf085a42fc..698af9fb1924 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; + 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; - } + 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, - ); - 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"); } + + 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", + ); + } + }); + } 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 e679fab5323d..f2a006ebdfc8 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 6bc0d06183f3..15b784039b6e 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 03673eb27f8f..32e0c3749abb 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 7adbd67912c0..c9fc65653c2b 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 c984a8286eb8..0ba898e75a17 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 12da29f11089..69818db7c82f 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)) + }; - if *mutability == Mutability::Mut { - generate_snippet("mut") - } else { - generate_snippet("&") - } + 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 { - 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 cb78eec9e8d7..ae8be0067812 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 1975946c6e6c..b4842e7d48aa 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 = 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 245029a066db..ad22b751befa 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 f2a3dc5099dc..8595205691b9 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 fd1f3d3903ac..4f53cceeecfc 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 = 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 = 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 b92014f68b39..c1edcf509efa 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 ccf8b9977056..3aabcadaa1fe 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 b940cac6047b..0385f1a98e53 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> { 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 2244eab96ca8..733da7904413 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); + 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, + }; - then { - let vi = VecAllocation { - local_id, - allocation_expr: right, - size_expr, - }; - - Self::search_initialization(cx, vi, expr.hir_id); - } + 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); + 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, + }; - then { - let vi = VecAllocation { - local_id, - allocation_expr: init, - size_expr, - }; - - Self::search_initialization(cx, vi, stmt.hir_id); - } + 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 a44adc938559..baa9750cc012 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,129 +250,116 @@ 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 - ) - } + 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_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!") { - 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, - ), - 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 !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_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) + "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 a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) + ), + applicability, + ); + } } } + + 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, + ); + } } } @@ -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 b3db5e9a4221..644664b104d2 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, - }; - - 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, + 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, - "using `libc::strlen` on a `CString` or `CStr` value", - "try", - format!("{val_name}.{method_name}().len()"), - app, - ); - } + .. + })) 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; + }; + + 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 0abc199da164..000000000000 --- 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 bb8cde5b94d1..92de7917871f 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 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, - ); - } + 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 6271ea027314..3244933a124a 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 660e6835e46a..285f2f4f6f90 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 0cfb1c1253c6..9481c78a505f 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 a171d225f1e4..1dca523a9669 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", "", "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", "", "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 87181adc24b0..7eef02b3c654 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 f065d215e489..e624bbe5ff11 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 { + 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 - // 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); } - // 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; + let fixed_trait_snippet = unique_traits + .iter() + .filter_map(|b| snippet_opt(cx, b.span)) + .collect::>() + .join(" + "); - 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::>() - .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, - ); - } + 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, 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::>(); - 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::>() + && !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()); + 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::>(); - let traits = traits.join(" + "); + let traits = comparable_bounds + .iter() + .filter_map(|&(_, span)| snippet_opt(cx, span)) + .collect::>(); + let traits = traits.join(" + "); - span_lint_and_sugg( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - all_trait_span, - msg, - "try", - traits, - Applicability::MachineApplicable - ); - } + 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 6eec40cb5295..a3a50acb6097 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 5ecba512b0fd..aef520923e47 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 ea9ad99618ab..98e9ea2d7751 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" + 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, ".."); + + 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); + } + }, + ); - 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, - ); - }, - ); - - 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 7c2223ca3ab7..a65bc0ce458b 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 306ca5724da1..801e88626199 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` types; see issue #1884 for details. - return false; - } - - let ltopt = if lt.is_anonymous() { - String::new() - } else { - format!("{} ", lt.ident.as_str()) - }; - - if mut_ty.mutbl == Mutability::Mut { - // Ignore `&mut Box` 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`. 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 is_any_trait(cx, inner) { + // Ignore `Box` types; see issue #1884 for details. + return false; } + + let ltopt = if lt.is_anonymous() { + String::new() + } else { + format!("{} ", lt.ident.as_str()) + }; + + if mut_ty.mutbl == Mutability::Mut { + // Ignore `&mut Box` 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`. 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> { - 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 4a5a94f26302..fc3420af0208 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 6a6160c4983d..f333b2cdcb49 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 60622903af1d..d12d14f2b141 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` instead of `Option>` 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` instead of `Option>` 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 a616c3e4ea83..afc319217042 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>`", - None, - "consider using `Rc>` or `Arc>` 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>`", + None, + "consider using `Rc>` or `Arc>` 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 39469841bd40..0bca56b8da78 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 { 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 decc183ad961..9d5066199bde 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, 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` 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` 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 72569e10f058..a7119434517e 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 - 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 + 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> { 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 e76cc65fd46a..bfd30cec3ddd 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> { 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> { 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)> { - 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 462b1aa8153e..44cff78a7936 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 e7355f92304d..2223cbcb0600 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 ca159eb4d5fd..9bd7167db257 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 9107b2b99b88..06c017ea15ab 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 28ea02e4d9a1..14694bb3a28a 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 a1083a0a68e5..1e2b20469eff 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 ab8de17b091f..5599a9dc4e81 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 c43d5dc94b3e..a7b2d2148e92 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 f864c520302e..532207310bc2 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 adbf8281388d..9627f4c7454b 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 cdfcb8500c44..6e1d0e09fe28 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 21592abbf168..df4b42133f8c 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 c3fe16ad5c3e..ac1d9acc70b1 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 for `. 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 for `. 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` with `Vec`, in an `impl Trait - // for u8`, when the trait always uses `Vec`. - // - // 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` with `Vec`, in an `impl Trait + // for u8`, when the trait always uses `Vec`. + // + // 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 28f1d487eb5f..52327b82e849 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(), "", &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(), "", &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 152248afc903..e8842ce10f8a 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 ddcb9f27c6c0..877a77fd6d24 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 d7666b77f6e9..f514f166cff5 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 cacd05262a21..5aa1417cfb48 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 8cdd5ea89037..000000000000 --- 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, -) { - #[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 fc9afe5ca8b9..16d0636b834e 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> { 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 250772238853..66d32087fcd1 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::>>(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } + .collect::>>() + && !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 00e352961bd3..486e8220484d 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 { 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 51abe0c1dc36..8ecdba47f89d 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); + && 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, - )); - } + 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) { 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 86b77a77f173..86b1a0ae624d 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 2b13fad80665..77b95e51f620 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 81be04659b9f..9aa23b6efe37 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::>() + && 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, + 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))", ), - (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, - ), - _ => 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 a9a3aaad366e..f58641289f80 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) - }; + 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); - } + 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 `:` - 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 `:` + 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 d88ede763980..5c1bea744864 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 f2f0699ef489..1d6217d186cf 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 fee100fe1ead..41c1757fde83 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 c9b01a68f42d..d7053d3ff848 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 b581a60de6e5..dcfce45fc9a4 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 { - "" => i8::MAX as u128, - "" => i16::MAX as u128, - "" => i32::MAX as u128, - "" => i64::MAX as u128, - "" => 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::>() + && let ["core", "num", int_impl, "max_value"] = *def_path + { + let value = match int_impl { + "" => i8::MAX as u128, + "" => i16::MAX as u128, + "" => i32::MAX as u128, + "" => i64::MAX as u128, + "" => 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 45c7e3a6e1df..fa56e5b0ba2b 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(cx: &T, lint: &'static Lint, sp: impl Into, 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( help_span: Option, 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( note_span: Option, 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, 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 edea4b3667fe..3135a033648c 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 { - 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> { - 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 2a8b2ebd5fbd..8031e6faa745 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 1181dfc0ef95..2466e8bb339d 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 { /// 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 { + struct BreakTarget { + id: HirId, + unused: bool, + } + + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + break_targets: Vec, + 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 5bca554378e7..0a820a1754ca 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", "", "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 2ff979f2dcb3..20588b63a78d 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> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[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> { fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[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 beea9fd00e7a..8c1150ed0104 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 293fcbf39928..d40e176b4b0a 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 7bb49d08da65..1ae8ac81695f 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 000000000000..3155c0235ffd --- /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, msg: impl Into) { + cx.struct_span_lint(lint, span, msg, |b| b); +} + +pub fn b( + tcx: TyCtxt<'_>, + lint: &'static Lint, + hir_id: HirId, + span: impl Into, + msg: impl Into, +) { + 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 000000000000..76c487fb1352 --- /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 b462b20e04c6..000000000000 --- 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 ea04955323d1..000000000000 --- 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 d03a577c4544..349e81912e3c 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` 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 fd239580d284..a7f91abda4eb 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` - = note: required for `Arc>` to implement `Send` and `Sync` - = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + = note: `Arc>` is not `Send` and `Sync` as: + = note: - the trait `Sync` is not implemented for `RefCell` + = 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` = 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>` to implement `Send` and `Sync` - = help: consider using an `Rc` instead or wrapping the inner type with a `Mutex` + = note: `Arc>` 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 000000000000..5761882273e0 --- /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 000000000000..367cb6998578 --- /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 000000000000..1bb8bf0c746b --- /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>>() { + 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 000000000000..b8289048a8b9 --- /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>>() { + | ^^^^^^^^^^ + | + = 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>, { /* Generic name */ }: Clone>() { + | +++++++++++++++++++++++++++++++ + +error: `impl Trait` used as a function parameter + --> $DIR/ice-11803.rs:5:33 + | +LL | pub fn g>>() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add a type parameter + | +LL | pub fn g>, { /* Generic name */ }: Iterator>() { + | +++++++++++++++++++++++++++++++++++++++++++++++++++++ + +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 000000000000..b1df24737a27 --- /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 similarity index 97% rename from src/tools/clippy/tests/ui/dbg_macro.rs rename to src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs index 149b08476192..3f4770c63d01 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 similarity index 85% rename from src/tools/clippy/tests/ui/dbg_macro.stderr rename to src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index f45a7ba1faeb..4d00421c7118 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 | 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 } | ^^^^^^^^^^^^^^^^^^^^^^ | - = 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 | 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 12158d0d12a2..e6ca4bb66ccd 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); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - pub union Variant0 { - pub anonymous: std::mem::ManuallyDrop, + union U { + u: T, } - pub struct Variant00 { - pub anonymous: Variant000, - } - - pub union Variant000 { - pub val: i32, + #[derive(Clone, Copy)] + struct S8 { + x: &'static str, } unsafe { - let mut p = core::mem::zeroed::(); - (*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 dec021c18344..7531e1f87b71 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); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - pub union Variant0 { - pub anonymous: std::mem::ManuallyDrop, + union U { + u: T, } - pub struct Variant00 { - pub anonymous: Variant000, - } - - pub union Variant000 { - pub val: i32, + #[derive(Clone, Copy)] + struct S8 { + x: &'static str, } unsafe { - let mut p = core::mem::zeroed::(); - (*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 3d2a7b0d9f4f..cc9eeeb50429 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 000000000000..7000c8bf96f8 --- /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::::new(); + let mut hash_map = HashMap::::new(); + let mut fx_hash_map = rustc_data_structures::fx::FxHashMap::::default(); + let mut fx_hash_set = rustc_data_structures::fx::FxHashMap::::default(); + let vec = Vec::::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::::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 000000000000..cf420fb8e996 --- /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 6de869699ec6..bafe8cfddb47 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports.stderr +++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr @@ -1,17 +1,11 @@ -error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:25:5 - | -LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` - | - = 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 | LL | #[macro_use] | ^^^^^^^^^^^^ 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:21:5 @@ -19,6 +13,12 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition LL | #[macro_use] | ^^^^^^^^^^^^ 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:25:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ 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 27717ab3a73a..5d94660ec89a 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!(); }; - // 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. + // 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 { - match () { - _ if panic!() => {}, - _ => panic!(), + 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: + let v = if let Some(v_some) = g() { + //~^ ERROR: this could be rewritten as `let...else` + v_some + } else { + 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 { diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index 2b6504a18278..3beaf766efb1 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,29 +127,21 @@ LL | / let v = if let Some(v_some) = g() { LL | | LL | | v_some LL | | } else { -... | -LL | | } +LL | | panic!(); +LL | | () LL | | }; | |______^ | help: consider writing | LL ~ let Some(v) = g() else { -LL + match () { -LL + _ if panic!() => {}, -LL + _ => panic!(), -LL + } +LL + panic!(); +LL + () LL + }; | error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:96:5 - | -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:94:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -163,8 +155,14 @@ LL | | }; help: consider writing | LL ~ let Some(v) = g() else { -LL + match panic!() { -LL + _ => {}, +LL + panic!(); +LL + if true { +LL + match 0 { +LL + 0 => (), +LL + _ => (), +LL + } +LL + } else { +LL + panic!() LL + } LL + }; | @@ -175,6 +173,116 @@ error: this could be rewritten as `let...else` 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 + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:121: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 + '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 + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:166:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | +LL | | v_some LL | | } else if true { ... | LL | | panic!("diverge"); @@ -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::>> { 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 a224001a3dfd..8146091a2bbe 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 b9dbda6ede71..4b5cd274da78 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 62b0ba018600..53ebfb40ba0d 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` + // match ergonomics makes the binding patterns into references + // so that its type changes to `Iterator` + 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 b7f4c99f2730..c646c0568595 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` + // match ergonomics makes the binding patterns into references + // so that its type changes to `Iterator` + 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 4ca24b0b04c4..ea077d66d640 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 = 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 7866c89bd618..244a88e6691f 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 c2c5f765abff..ff1e2dc88756 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>, + #[derive(Clone, Copy)] + struct Wrap(T); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - union Ocean { - coral: ManuallyDrop, + union U { + 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); + 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; - (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); - ManuallyDrop::drop(&mut (*ocean.coral).crab); + 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; - ManuallyDrop::drop(&mut ocean.coral); + 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 0cd6e41b8a47..597021539acf 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>, + #[derive(Clone, Copy)] + struct Wrap(T); + impl core::ops::Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + impl core::ops::DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } } - union Ocean { - coral: ManuallyDrop, + union U { + 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); + 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; - (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); - ManuallyDrop::drop(&mut (*ocean.coral).crab); + 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; - ManuallyDrop::drop(&mut ocean.coral); + 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 e91b78b0a152..44552ee6abea 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 52d541809214..d5932ebcf124 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 d253cae4dc28..2485e25f05ca 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 0de0633803bc..580970a41aa9 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 b918fdf774b5..26b152515ac7 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 286decf8f358..033de0e92508 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 = 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 = 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::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 = 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 = 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 = <_>::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 4363d2224afd..000000000000 --- 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 = Vec::new(); - static S: Vec = Vec::new(); - - struct StructWithVecBox { - sized_type: Vec, - } - - struct A(Vec); - struct B(Vec>); -} - -/// The following should not trigger the lint -mod should_not_trigger { - use super::{BigStruct, UnsizedStruct}; - - struct C(Vec>); - struct D(Vec>); - - struct StructWithVecBoxButItsUnsized { - unsized_type: Vec>, - } - - struct TraitVec { - // Regression test for #3720. This was causing an ICE. - inner: Vec>, - } -} - -mod inner_mod { - mod inner { - pub struct S; - } - - mod inner2 { - use super::inner::S; - - pub fn f() -> Vec { - vec![] - } - } -} - -// https://github.com/rust-lang/rust-clippy/issues/11417 -fn in_closure() { - let _ = |_: Vec>| {}; -} - -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 f4e27fe4bd50..49eaf8e062af 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, AllocError> { + todo!() + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + todo!() + } +} + /// The following should trigger the lint mod should_trigger { - use super::SizedStruct; + use super::{DummyAllocator, SizedStruct}; const C: Vec> = Vec::new(); static S: Vec> = Vec::new(); @@ -16,11 +32,21 @@ mod should_trigger { struct A(Vec>); struct B(Vec>>); + + fn allocator_global_defined_vec() -> Vec, std::alloc::Global> { + Vec::new() + } + fn allocator_global_defined_box() -> Vec> { + Vec::new() + } + fn allocator_match() -> Vec, 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>); struct D(Vec>); @@ -33,6 +59,13 @@ mod should_not_trigger { // Regression test for #3720. This was causing an ICE. inner: Vec>, } + + fn allocator_mismatch() -> Vec> { + Vec::new() + } + fn allocator_mismatch_2() -> Vec, 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 9118f284bb97..d6479271fa63 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` 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> = Vec::new(); | ^^^^^^^^^^^^^ help: try: `Vec` @@ -8,34 +8,52 @@ LL | const C: Vec> = Vec::new(); = help: to override `-D warnings` add `#[allow(clippy::vec_box)]` error: `Vec` 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> = Vec::new(); | ^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` 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>, | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` 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>); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` 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>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` 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, std::alloc::Global> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:39:42 + | +LL | fn allocator_global_defined_box() -> Vec> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:42:29 + | +LL | fn allocator_match() -> Vec, DummyAllocator> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:79:23 | LL | pub fn f() -> Vec> { | ^^^^^^^^^^^ help: try: `Vec` -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 419b3c30deb0..ab2fb1a32919 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 1e9684555f1f..e7d2e1aab3a1 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 77c2866b366a..3a1b9dff3a6b 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -146,19 +146,13 @@ 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", message: "when the target is WASM" } - 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"), diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 85e745bed112..295134c78dcb 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/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index ccabe6483d7a..6326a9b1e79c 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/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 67b48a3742da..554a12909fc8 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Set the tag GC interval to 1 on linux if: runner.os == 'Linux' - run: echo "MIRIFLAGS=-Zmiri-tag-gc=1" >> $GITHUB_ENV + run: echo "MIRIFLAGS=-Zmiri-provenance-gc=1" >> $GITHUB_ENV # Cache the global cargo directory, but NOT the local `target` directory which # we cannot reuse anyway when the nightly changes (and it grows quite large diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index f90fd1f7fc54..aa0b791bd05f 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -411,10 +411,10 @@ to Miri failing to detect cases of undefined behavior in a program. without an explicit value), `none` means it never recurses, `scalar` means it only recurses for types where we would also emit `noalias` annotations in the generated LLVM IR (types passed as individual scalars or pairs of scalars). Setting this to `none` is **unsound**. -* `-Zmiri-tag-gc=` configures how often the pointer tag garbage collector runs. The default - is to search for and remove unreachable tags once every `10000` basic blocks. Setting this to - `0` disables the garbage collector, which causes some programs to have explosive memory usage - and/or super-linear runtime. +* `-Zmiri-provenance-gc=` configures how often the pointer provenance garbage collector runs. + The default is to search for and remove unreachable provenance once every `10000` basic blocks. Setting + this to `0` disables the garbage collector, which causes some programs to have explosive memory + usage and/or super-linear runtime. * `-Zmiri-track-alloc-id=,,...` shows a backtrace when the given allocations are being allocated or freed. This helps in debugging memory leaks and use after free bugs. Specifying this argument multiple times does not overwrite the previous diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 7f777cd4727a..4517de628931 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -531,10 +531,10 @@ fn main() { Err(err) => show_error!("-Zmiri-report-progress requires a `u32`: {}", err), }; miri_config.report_progress = Some(interval); - } else if let Some(param) = arg.strip_prefix("-Zmiri-tag-gc=") { + } else if let Some(param) = arg.strip_prefix("-Zmiri-provenance-gc=") { let interval = match param.parse::() { Ok(i) => i, - Err(err) => show_error!("-Zmiri-tag-gc requires a `u32`: {}", err), + Err(err) => show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err), }; miri_config.gc_interval = interval; } else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") { diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index f9cc3acbd51b..8fae52692294 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -75,8 +75,8 @@ pub struct FrameState { protected_tags: SmallVec<[(AllocId, BorTag); 2]>, } -impl VisitTags for FrameState { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for FrameState { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { // `protected_tags` are already recorded by `GlobalStateInner`. } } @@ -110,10 +110,10 @@ pub struct GlobalStateInner { unique_is_unique: bool, } -impl VisitTags for GlobalStateInner { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for GlobalStateInner { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for &tag in self.protected_tags.keys() { - visit(tag); + visit(None, Some(tag)); } // The only other candidate is base_ptr_tags, and that does not need visiting since we don't ever // GC the bottommost/root tag. @@ -236,6 +236,10 @@ impl GlobalStateInner { tag }) } + + pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) { + self.base_ptr_tags.retain(|id, _| allocs.is_live(*id)); + } } /// Which borrow tracking method to use @@ -503,11 +507,11 @@ impl AllocState { } } -impl VisitTags for AllocState { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for AllocState { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { match self { - AllocState::StackedBorrows(sb) => sb.visit_tags(visit), - AllocState::TreeBorrows(tb) => tb.visit_tags(visit), + AllocState::StackedBorrows(sb) => sb.visit_provenance(visit), + AllocState::TreeBorrows(tb) => tb.visit_provenance(visit), } } } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index a74c69d52f23..91d924976f78 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -462,10 +462,10 @@ impl Stacks { } } -impl VisitTags for Stacks { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Stacks { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for tag in self.exposed_tags.iter().copied() { - visit(tag); + visit(None, Some(tag)); } } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index b3001b5b88cd..43e6616e34a7 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -200,7 +200,7 @@ impl<'tcx> Tree { /// Climb the tree to get the tag of a distant ancestor. /// Allows operations on tags that are unreachable by the program /// but still exist in the tree. Not guaranteed to perform consistently - /// if `tag-gc=1`. + /// if `provenance-gc=1`. fn nth_parent(&self, tag: BorTag, nth_parent: u8) -> Option { let mut idx = self.tag_mapping.get(&tag).unwrap(); for _ in 0..nth_parent { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 4232cd396c95..6801397b2cec 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -742,11 +742,11 @@ impl Tree { } } -impl VisitTags for Tree { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Tree { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { // To ensure that the root never gets removed, we visit it // (the `root` node of `Tree` is not an `Option<_>`) - visit(self.nodes.get(self.root).unwrap().tag) + visit(None, Some(self.nodes.get(self.root).unwrap().tag)) } } diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 5d109a7d55ce..80d0402fc875 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -790,9 +790,9 @@ pub struct VClockAlloc { alloc_ranges: RefCell>, } -impl VisitTags for VClockAlloc { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { - // No tags here. +impl VisitProvenance for VClockAlloc { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + // No tags or allocIds here. } } @@ -1404,8 +1404,8 @@ pub struct GlobalState { pub track_outdated_loads: bool, } -impl VisitTags for GlobalState { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for GlobalState { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { // We don't have any tags. } } diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 71582c75eae5..9a848d50341a 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -45,10 +45,10 @@ pub(super) struct InitOnce<'mir, 'tcx> { data_race: VClock, } -impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for waiter in self.waiters.iter() { - waiter.callback.visit_tags(visit); + waiter.callback.visit_provenance(visit); } } } diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 62f6d57ef36e..b288b69e0cef 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -181,10 +181,10 @@ pub(crate) struct SynchronizationState<'mir, 'tcx> { pub(super) init_onces: IndexVec>, } -impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl<'mir, 'tcx> VisitProvenance for SynchronizationState<'mir, 'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for init_once in self.init_onces.iter() { - init_once.visit_tags(visit); + init_once.visit_provenance(visit); } } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 6449ed29cf8f..754cfa4d2a82 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -43,7 +43,7 @@ pub enum TlsAllocAction { } /// Trait for callbacks that can be executed when some event happens, such as after a timeout. -pub trait MachineCallback<'mir, 'tcx>: VisitTags { +pub trait MachineCallback<'mir, 'tcx>: VisitProvenance { fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>; } @@ -228,8 +228,8 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> { } } -impl VisitTags for Thread<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Thread<'_, '_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Thread { panic_payloads: panic_payload, last_error, @@ -242,17 +242,17 @@ impl VisitTags for Thread<'_, '_> { } = self; for payload in panic_payload { - payload.visit_tags(visit); + payload.visit_provenance(visit); } - last_error.visit_tags(visit); + last_error.visit_provenance(visit); for frame in stack { - frame.visit_tags(visit) + frame.visit_provenance(visit) } } } -impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Frame { return_place, locals, @@ -266,22 +266,22 @@ impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> { } = self; // Return place. - return_place.visit_tags(visit); + return_place.visit_provenance(visit); // Locals. for local in locals.iter() { match local.as_mplace_or_imm() { None => {} Some(Either::Left((ptr, meta))) => { - ptr.visit_tags(visit); - meta.visit_tags(visit); + ptr.visit_provenance(visit); + meta.visit_provenance(visit); } Some(Either::Right(imm)) => { - imm.visit_tags(visit); + imm.visit_provenance(visit); } } } - extra.visit_tags(visit); + extra.visit_provenance(visit); } } @@ -341,8 +341,8 @@ pub struct ThreadManager<'mir, 'tcx> { timeout_callbacks: FxHashMap>, } -impl VisitTags for ThreadManager<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for ThreadManager<'_, '_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let ThreadManager { threads, thread_local_alloc_ids, @@ -353,15 +353,15 @@ impl VisitTags for ThreadManager<'_, '_> { } = self; for thread in threads { - thread.visit_tags(visit); + thread.visit_provenance(visit); } for ptr in thread_local_alloc_ids.borrow().values() { - ptr.visit_tags(visit); + ptr.visit_provenance(visit); } for callback in timeout_callbacks.values() { - callback.callback.visit_tags(visit); + callback.callback.visit_provenance(visit); } - sync.visit_tags(visit); + sync.visit_provenance(visit); } } diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 2ff344bb1a35..39e89ce7faa7 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -108,15 +108,15 @@ pub struct StoreBufferAlloc { store_buffers: RefCell>, } -impl VisitTags for StoreBufferAlloc { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for StoreBufferAlloc { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Self { store_buffers } = self; for val in store_buffers .borrow() .iter() .flat_map(|buf| buf.buffer.iter().map(|element| &element.val)) { - val.visit_tags(visit); + val.visit_provenance(visit); } } } diff --git a/src/tools/miri/src/intptrcast.rs b/src/tools/miri/src/intptrcast.rs index 9966ee3fd919..cd4d1bcc4647 100644 --- a/src/tools/miri/src/intptrcast.rs +++ b/src/tools/miri/src/intptrcast.rs @@ -46,9 +46,21 @@ pub struct GlobalStateInner { provenance_mode: ProvenanceMode, } -impl VisitTags for GlobalStateInner { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { - // Nothing to visit here. +impl VisitProvenance for GlobalStateInner { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + let GlobalStateInner { + int_to_ptr_map: _, + base_addr: _, + exposed: _, + next_base_addr: _, + provenance_mode: _, + } = self; + // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them. + // int_to_ptr_map and exposed must contain only live allocations, and those + // are never garbage collected. + // base_addr is only relevant if we have a pointer to an AllocId and need to look up its + // base address; so if an AllocId is not reachable from somewhere else we can remove it + // here. } } @@ -62,6 +74,12 @@ impl GlobalStateInner { provenance_mode: config.provenance_mode, } } + + pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) { + // `exposed` and `int_to_ptr_map` are cleared immediately when an allocation + // is freed, so `base_addr` is the only one we have to clean up based on the GC. + self.base_addr.retain(|id, _| allocs.is_live(*id)); + } } /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple @@ -107,7 +125,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We only use this provenance if it has been exposed. if global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. - debug_assert!(!matches!(ecx.get_alloc_info(alloc_id).2, AllocKind::Dead)); + debug_assert!(ecx.is_alloc_live(alloc_id)); Some(alloc_id) } else { None @@ -181,12 +199,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ecx = self.eval_context_mut(); let global_state = ecx.machine.intptrcast.get_mut(); // In strict mode, we don't need this, so we can save some cycles by not tracking it. - if global_state.provenance_mode != ProvenanceMode::Strict { - trace!("Exposing allocation id {alloc_id:?}"); - global_state.exposed.insert(alloc_id); - if ecx.machine.borrow_tracker.is_some() { - ecx.expose_tag(alloc_id, tag)?; - } + if global_state.provenance_mode == ProvenanceMode::Strict { + return Ok(()); + } + // Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation + // via int2ptr. + if !ecx.is_alloc_live(alloc_id) { + return Ok(()); + } + trace!("Exposing allocation id {alloc_id:?}"); + let global_state = ecx.machine.intptrcast.get_mut(); + global_state.exposed.insert(alloc_id); + if ecx.machine.borrow_tracker.is_some() { + ecx.expose_tag(alloc_id, tag)?; } Ok(()) } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index b12aae6d4148..119ec555b2e0 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -77,9 +77,9 @@ mod intptrcast; mod machine; mod mono_hash_map; mod operator; +mod provenance_gc; mod range_map; mod shims; -mod tag_gc; // Establish a "crate-wide prelude": we often import `crate::*`. @@ -125,8 +125,8 @@ pub use crate::machine::{ }; pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as _; +pub use crate::provenance_gc::{EvalContextExt as _, VisitProvenance, VisitWith, LiveAllocs}; pub use crate::range_map::RangeMap; -pub use crate::tag_gc::{EvalContextExt as _, VisitTags}; /// Insert rustc arguments at the beginning of the argument list that Miri wants to be /// set per default, for maximal validation power. diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 2085df7d06f8..66d7dfcf3a10 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -77,12 +77,12 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> { } } -impl VisitTags for FrameExtra<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for FrameExtra<'_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self; - catch_unwind.visit_tags(visit); - borrow_tracker.visit_tags(visit); + catch_unwind.visit_provenance(visit); + borrow_tracker.visit_provenance(visit); } } @@ -311,13 +311,13 @@ pub struct AllocExtra<'tcx> { pub backtrace: Option>>, } -impl VisitTags for AllocExtra<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for AllocExtra<'_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self; - borrow_tracker.visit_tags(visit); - data_race.visit_tags(visit); - weak_memory.visit_tags(visit); + borrow_tracker.visit_provenance(visit); + data_race.visit_provenance(visit); + weak_memory.visit_provenance(visit); } } @@ -793,8 +793,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } } -impl VisitTags for MiriMachine<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for MiriMachine<'_, '_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { #[rustfmt::skip] let MiriMachine { threads, @@ -843,20 +843,20 @@ impl VisitTags for MiriMachine<'_, '_> { allocation_spans: _, } = self; - threads.visit_tags(visit); - tls.visit_tags(visit); - env_vars.visit_tags(visit); - dir_handler.visit_tags(visit); - file_handler.visit_tags(visit); - data_race.visit_tags(visit); - borrow_tracker.visit_tags(visit); - intptrcast.visit_tags(visit); - main_fn_ret_place.visit_tags(visit); - argc.visit_tags(visit); - argv.visit_tags(visit); - cmd_line.visit_tags(visit); + threads.visit_provenance(visit); + tls.visit_provenance(visit); + env_vars.visit_provenance(visit); + dir_handler.visit_provenance(visit); + file_handler.visit_provenance(visit); + data_race.visit_provenance(visit); + borrow_tracker.visit_provenance(visit); + intptrcast.visit_provenance(visit); + main_fn_ret_place.visit_provenance(visit); + argc.visit_provenance(visit); + argv.visit_provenance(visit); + cmd_line.visit_provenance(visit); for ptr in extern_statics.values() { - ptr.visit_tags(visit); + ptr.visit_provenance(visit); } } } @@ -1380,7 +1380,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { // where it mistakenly removes an important tag become visible. if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval { ecx.machine.since_gc = 0; - ecx.garbage_collect_tags()?; + ecx.run_provenance_gc(); } // These are our preemption points. diff --git a/src/tools/miri/src/mono_hash_map.rs b/src/tools/miri/src/mono_hash_map.rs index 81b3153517f1..220233f8ff5f 100644 --- a/src/tools/miri/src/mono_hash_map.rs +++ b/src/tools/miri/src/mono_hash_map.rs @@ -46,6 +46,14 @@ impl AllocMap for MonoHashMap { self.0.get_mut().contains_key(k) } + #[inline(always)] + fn contains_key_ref(&self, k: &Q) -> bool + where + K: Borrow, + { + self.0.borrow().contains_key(k) + } + #[inline(always)] fn insert(&mut self, k: K, v: V) -> Option { self.0.get_mut().insert(k, Box::new(v)).map(|x| *x) diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs new file mode 100644 index 000000000000..eac4aad27a0c --- /dev/null +++ b/src/tools/miri/src/provenance_gc.rs @@ -0,0 +1,209 @@ +use either::Either; + +use rustc_data_structures::fx::FxHashSet; + +use crate::*; + +pub type VisitWith<'a> = dyn FnMut(Option, Option) + 'a; + +pub trait VisitProvenance { + fn visit_provenance(&self, visit: &mut VisitWith<'_>); +} + +impl VisitProvenance for Option { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + if let Some(x) = self { + x.visit_provenance(visit); + } + } +} + +impl VisitProvenance for std::cell::RefCell { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + self.borrow().visit_provenance(visit) + } +} + +impl VisitProvenance for BorTag { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + visit(None, Some(*self)) + } +} + +impl VisitProvenance for AllocId { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + visit(Some(*self), None) + } +} + +impl VisitProvenance for Provenance { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + if let Provenance::Concrete { alloc_id, tag, .. } = self { + visit(Some(*alloc_id), Some(*tag)); + } + } +} + +impl VisitProvenance for Pointer { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + let (prov, _offset) = self.into_parts(); + prov.visit_provenance(visit); + } +} + +impl VisitProvenance for Pointer> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + let (prov, _offset) = self.into_parts(); + prov.visit_provenance(visit); + } +} + +impl VisitProvenance for Scalar { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + match self { + Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit), + Scalar::Int(_) => (), + } + } +} + +impl VisitProvenance for Immediate { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + match self { + Immediate::Scalar(s) => { + s.visit_provenance(visit); + } + Immediate::ScalarPair(s1, s2) => { + s1.visit_provenance(visit); + s2.visit_provenance(visit); + } + Immediate::Uninit => {} + } + } +} + +impl VisitProvenance for MemPlaceMeta { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + match self { + MemPlaceMeta::Meta(m) => m.visit_provenance(visit), + MemPlaceMeta::None => {} + } + } +} + +impl VisitProvenance for ImmTy<'_, Provenance> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + (**self).visit_provenance(visit) + } +} + +impl VisitProvenance for MPlaceTy<'_, Provenance> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + self.ptr().visit_provenance(visit); + self.meta().visit_provenance(visit); + } +} + +impl VisitProvenance for PlaceTy<'_, Provenance> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + match self.as_mplace_or_local() { + Either::Left(mplace) => mplace.visit_provenance(visit), + Either::Right(_) => (), + } + } +} + +impl VisitProvenance for OpTy<'_, Provenance> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + match self.as_mplace_or_imm() { + Either::Left(mplace) => mplace.visit_provenance(visit), + Either::Right(imm) => imm.visit_provenance(visit), + } + } +} + +impl VisitProvenance for Allocation> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + for prov in self.provenance().provenances() { + prov.visit_provenance(visit); + } + + self.extra.visit_provenance(visit); + } +} + +impl VisitProvenance for crate::MiriInterpCx<'_, '_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + // Visit the contents of the allocations and the IDs themselves, to account for all + // live allocation IDs and all provenance in the allocation bytes, even if they are leaked. + // We do *not* visit all the `AllocId` of the live allocations; we tried that and adding + // them all to the live set is too expensive. Instead we later do liveness check by + // checking both "is this alloc id live" and "is it mentioned anywhere else in + // the interpreter state". + self.memory.alloc_map().iter(|it| { + for (_id, (_kind, alloc)) in it { + alloc.visit_provenance(visit); + } + }); + // And all the other machine values. + self.machine.visit_provenance(visit); + } +} + +pub struct LiveAllocs<'a, 'mir, 'tcx> { + collected: FxHashSet, + ecx: &'a MiriInterpCx<'mir, 'tcx>, +} + +impl LiveAllocs<'_, '_, '_> { + pub fn is_live(&self, id: AllocId) -> bool { + self.collected.contains(&id) || + self.ecx.is_alloc_live(id) + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { + fn run_provenance_gc(&mut self) { + + // We collect all tags from various parts of the interpreter, but also + let this = self.eval_context_mut(); + + let mut tags = FxHashSet::default(); + let mut alloc_ids = FxHashSet::default(); + this.visit_provenance(&mut |id, tag| { + if let Some(id) = id { + alloc_ids.insert(id); + } + if let Some(tag) = tag { + tags.insert(tag); + } + }); + self.remove_unreachable_tags(tags); + self.remove_unreachable_allocs(alloc_ids); + } + + fn remove_unreachable_tags(&mut self, tags: FxHashSet) { + let this = self.eval_context_mut(); + this.memory.alloc_map().iter(|it| { + for (_id, (_kind, alloc)) in it { + if let Some(bt) = &alloc.extra.borrow_tracker { + bt.remove_unreachable_tags(&tags); + } + } + }); + } + + fn remove_unreachable_allocs(&mut self, allocs: FxHashSet) { + let this = self.eval_context_ref(); + let allocs = LiveAllocs { + ecx: this, + collected: allocs, + }; + this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id)); + this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs); + if let Some(borrow_tracker) = &this.machine.borrow_tracker { + borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs); + } + } +} diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 154a7f698334..424389859073 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -37,13 +37,13 @@ pub struct EnvVars<'tcx> { pub(crate) environ: Option>, } -impl VisitTags for EnvVars<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for EnvVars<'_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let EnvVars { map, environ } = self; - environ.visit_tags(visit); + environ.visit_provenance(visit); for ptr in map.values() { - ptr.visit_tags(visit); + ptr.visit_provenance(visit); } } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 2d5df3037452..d7aaa08dbf3f 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), @@ -459,6 +459,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // shim, add it to the corresponding submodule. match link_name.as_str() { // Miri-specific extern functions + "miri_run_provenance_gc" => { + let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; + this.run_provenance_gc(); + } "miri_get_alloc_id" => { let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 5c0f828e4e6b..28652c25c245 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -35,12 +35,12 @@ pub struct CatchUnwindData<'tcx> { ret: mir::BasicBlock, } -impl VisitTags for CatchUnwindData<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for CatchUnwindData<'_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let CatchUnwindData { catch_fn, data, dest, ret: _ } = self; - catch_fn.visit_tags(visit); - data.visit_tags(visit); - dest.visit_tags(visit); + catch_fn.visit_provenance(visit); + data.visit_provenance(visit); + dest.visit_provenance(visit); } } diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 611b8bc8ccc6..13ce9d119bc3 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -274,8 +274,8 @@ struct UnblockCallback { thread_to_unblock: ThreadId, } -impl VisitTags for UnblockCallback { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {} +impl VisitProvenance for UnblockCallback { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {} } impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback { diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 62bd087e7e82..b319516c25b9 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -207,15 +207,15 @@ impl<'tcx> TlsData<'tcx> { } } -impl VisitTags for TlsData<'_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for TlsData<'_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let TlsData { keys, macos_thread_dtors, next_key: _ } = self; for scalar in keys.values().flat_map(|v| v.data.values()) { - scalar.visit_tags(visit); + scalar.visit_provenance(visit); } for (_, scalar) in macos_thread_dtors.values() { - scalar.visit_tags(visit); + scalar.visit_provenance(visit); } } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 721fa161232e..d545cb460691 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -288,8 +288,8 @@ pub struct FileHandler { pub handles: BTreeMap>, } -impl VisitTags for FileHandler { - fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for FileHandler { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { // All our FileDescriptor do not have any tags. } } @@ -490,12 +490,12 @@ impl Default for DirHandler { } } -impl VisitTags for DirHandler { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { +impl VisitProvenance for DirHandler { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let DirHandler { streams, next_id: _ } = self; for dir in streams.values() { - dir.entry.visit_tags(visit); + dir.entry.visit_provenance(visit); } } } diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index ff25b8120b1c..10e06226b3fc 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -182,10 +182,10 @@ pub fn futex<'tcx>( dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Callback { thread: _, addr_usize: _, dest } = self; - dest.visit_tags(visit); + dest.visit_provenance(visit); } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 1a91219e0161..45b47450b7b2 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -886,10 +886,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Callback { active_thread: _, mutex_id: _, id: _, dest } = self; - dest.visit_tags(visit); + dest.visit_provenance(visit); } } diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 2c9603097c85..2b9801fea68e 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -204,10 +204,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { pending_place: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Callback { init_once_id: _, pending_place } = self; - pending_place.visit_tags(visit); + pending_place.visit_provenance(visit); } } @@ -337,10 +337,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Callback { thread: _, addr: _, dest } = self; - dest.visit_tags(visit); + dest.visit_provenance(visit); } } @@ -441,10 +441,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: PlaceTy<'tcx, Provenance>, } - impl<'tcx> VisitTags for Callback<'tcx> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { + impl<'tcx> VisitProvenance for Callback<'tcx> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Callback { thread: _, condvar_id: _, lock_id: _, mode: _, dest } = self; - dest.visit_tags(visit); + dest.visit_provenance(visit); } } diff --git a/src/tools/miri/src/tag_gc.rs b/src/tools/miri/src/tag_gc.rs deleted file mode 100644 index 3cccdd363538..000000000000 --- a/src/tools/miri/src/tag_gc.rs +++ /dev/null @@ -1,169 +0,0 @@ -use either::Either; - -use rustc_data_structures::fx::FxHashSet; - -use crate::*; - -pub trait VisitTags { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)); -} - -impl VisitTags for Option { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - if let Some(x) = self { - x.visit_tags(visit); - } - } -} - -impl VisitTags for std::cell::RefCell { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - self.borrow().visit_tags(visit) - } -} - -impl VisitTags for BorTag { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - visit(*self) - } -} - -impl VisitTags for Provenance { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - if let Provenance::Concrete { tag, .. } = self { - visit(*tag); - } - } -} - -impl VisitTags for Pointer { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - let (prov, _offset) = self.into_parts(); - prov.visit_tags(visit); - } -} - -impl VisitTags for Pointer> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - let (prov, _offset) = self.into_parts(); - prov.visit_tags(visit); - } -} - -impl VisitTags for Scalar { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self { - Scalar::Ptr(ptr, _) => ptr.visit_tags(visit), - Scalar::Int(_) => (), - } - } -} - -impl VisitTags for Immediate { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self { - Immediate::Scalar(s) => { - s.visit_tags(visit); - } - Immediate::ScalarPair(s1, s2) => { - s1.visit_tags(visit); - s2.visit_tags(visit); - } - Immediate::Uninit => {} - } - } -} - -impl VisitTags for MemPlaceMeta { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self { - MemPlaceMeta::Meta(m) => m.visit_tags(visit), - MemPlaceMeta::None => {} - } - } -} - -impl VisitTags for ImmTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - (**self).visit_tags(visit) - } -} - -impl VisitTags for MPlaceTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - self.ptr().visit_tags(visit); - self.meta().visit_tags(visit); - } -} - -impl VisitTags for PlaceTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self.as_mplace_or_local() { - Either::Left(mplace) => mplace.visit_tags(visit), - Either::Right(_) => (), - } - } -} - -impl VisitTags for OpTy<'_, Provenance> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - match self.as_mplace_or_imm() { - Either::Left(mplace) => mplace.visit_tags(visit), - Either::Right(imm) => imm.visit_tags(visit), - } - } -} - -impl VisitTags for Allocation> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - for prov in self.provenance().provenances() { - prov.visit_tags(visit); - } - - self.extra.visit_tags(visit); - } -} - -impl VisitTags for crate::MiriInterpCx<'_, '_> { - fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) { - // Memory. - self.memory.alloc_map().iter(|it| { - for (_id, (_kind, alloc)) in it { - alloc.visit_tags(visit); - } - }); - - // And all the other machine values. - self.machine.visit_tags(visit); - } -} - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { - fn garbage_collect_tags(&mut self) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - // No reason to do anything at all if stacked borrows is off. - if this.machine.borrow_tracker.is_none() { - return Ok(()); - } - - let mut tags = FxHashSet::default(); - this.visit_tags(&mut |tag| { - tags.insert(tag); - }); - self.remove_unreachable_tags(tags); - - Ok(()) - } - - fn remove_unreachable_tags(&mut self, tags: FxHashSet) { - let this = self.eval_context_mut(); - this.memory.alloc_map().iter(|it| { - for (_id, (_kind, alloc)) in it { - if let Some(bt) = &alloc.extra.borrow_tracker { - bt.remove_unreachable_tags(&tags); - } - } - }); - } -} diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_shl2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_shl2.rs new file mode 100644 index 000000000000..a5777ebbb7f3 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_shl2.rs @@ -0,0 +1,9 @@ +#![feature(core_intrinsics)] +use std::intrinsics; + +fn main() { + unsafe { + let _n = intrinsics::unchecked_shl(1i8, -1); + //~^ ERROR: overflowing shift by -1 in `unchecked_shl` + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_shl2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_shl2.stderr new file mode 100644 index 000000000000..ab27c9fe9116 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_shl2.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: overflowing shift by -1 in `unchecked_shl` + --> $DIR/unchecked_shl2.rs:LL:CC + | +LL | let _n = intrinsics::unchecked_shl(1i8, -1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/unchecked_shl2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs index 465679b72c34..3769575765e9 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 // Check how a Reserved with interior mutability // responds to a Foreign Write under a Protector diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs index 1e6e2eebd26c..e2956759d0b3 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs new file mode 100644 index 000000000000..716119a0fc34 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs @@ -0,0 +1,21 @@ +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-permissive-provenance + +#[path = "../utils/mod.rs"] +mod utils; + +type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; + +fn main() { + let name = "getentropy\0"; + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize }; + // If the GC does not account for the extra_fn_ptr entry that this dlsym just added, this GC + // run will delete our entry for the base addr of the function pointer we will transmute to, + // and the call through the function pointer will report UB. + utils::run_provenance_gc(); + + let ptr = addr as *mut libc::c_void; + let func: GetEntropyFn = unsafe { std::mem::transmute(ptr) }; + let dest = &mut [0u8]; + unsafe { func(dest.as_mut_ptr(), dest.len()) }; +} diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index abfe3b0adeb4..1cbccb2eebdd 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -1,4 +1,6 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-provenance-gc=10000 +// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we +// override it internally back to the default frequency. // The following tests check whether our weak memory emulation produces // any inconsistent execution outcomes diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs index 398b542ed4c2..e6310f8eda64 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../utils/mod.rs"] #[macro_use] mod utils; diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs index fecc3360434d..4d941850e541 100644 --- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 // Check that a protector goes back to normal behavior when the function // returns. diff --git a/src/tools/miri/tests/pass/tree_borrows/formatting.rs b/src/tools/miri/tests/pass/tree_borrows/formatting.rs index f22c408ad253..c4360f0dbb9a 100644 --- a/src/tools/miri/tests/pass/tree_borrows/formatting.rs +++ b/src/tools/miri/tests/pass/tree_borrows/formatting.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs index a38cd6d28941..7a5fd395c7fd 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.rs b/src/tools/miri/tests/pass/tree_borrows/reserved.rs index 8d0beab66f40..ef47d30e2efc 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reserved.rs +++ b/src/tools/miri/tests/pass/tree_borrows/reserved.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 #[path = "../../utils/mod.rs"] #[macro_use] diff --git a/src/tools/miri/tests/pass/tree_borrows/unique.rs b/src/tools/miri/tests/pass/tree_borrows/unique.rs index 44e2e8136252..6c5ed9efceea 100644 --- a/src/tools/miri/tests/pass/tree_borrows/unique.rs +++ b/src/tools/miri/tests/pass/tree_borrows/unique.rs @@ -1,5 +1,5 @@ //@revisions: default uniq -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 //@[uniq]compile-flags: -Zmiri-unique-is-unique #![feature(ptr_internals)] diff --git a/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs b/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs index e5d0a683a723..8d0f4bd0fe79 100644 --- a/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs +++ b/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs @@ -1,5 +1,5 @@ //@revisions: default uniq -//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0 +//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0 //@[uniq]compile-flags: -Zmiri-unique-is-unique #![feature(vec_into_raw_parts)] diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index c0ef2c506413..7363c189840a 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -84,7 +84,7 @@ extern "Rust" { /// /// The format of what this emits is unstable and may change at any time. In particular, users should be /// aware that Miri will periodically attempt to garbage collect the contents of all stacks. Callers of - /// this function may wish to pass `-Zmiri-tag-gc=0` to disable the GC. + /// this function may wish to pass `-Zmiri-provenance-gc=0` to disable the GC. /// /// This function is extremely unstable. At any time the format of its output may change, its signature may /// change, or it may be removed entirely. @@ -137,4 +137,9 @@ extern "Rust" { out: *mut std::ffi::c_char, out_size: usize, ) -> usize; + + /// Run the provenance GC. The GC will run automatically at some cadence, + /// but in tests we want to for sure run it at certain points to check + /// that it doesn't break anything. + pub fn miri_run_provenance_gc(); } diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs index 7b7dc231a501..6386162e0953 100644 --- a/src/tools/miri/tests/utils/mod.rs +++ b/src/tools/miri/tests/utils/mod.rs @@ -9,3 +9,10 @@ mod miri_extern; pub use fs::*; pub use miri_extern::*; + +pub fn run_provenance_gc() { + // SAFETY: No preconditions. The GC is fine to run at any time. + unsafe { + miri_run_provenance_gc() + } +} diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index b5dbe30c3221..6a3cdfe3a3c7 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 fcb188c0dfab..701e36d74a65 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -28,15 +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" +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" @@ -44,17 +44,6 @@ version = "0.7.4" 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" @@ -101,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" @@ -131,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", @@ -171,21 +160,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "264726159011fc7f22c23eb51f49021ece6e71bc358b96e7f2e842db0b14162b" +checksum = "c0322d5289ceba3217a03c9af72aa403d87542822b753daa1da32e4b992a4e80" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", "synstructure", ] [[package]] name = "chalk-ir" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65c17407d4c756b8f7f84344acb0fb96364d0298822743219bb25769b6d00df" +checksum = "0946cbc6d9136980a24a2dddf1888b5f0aa978dda300a3aa470b55b777b6bf3c" dependencies = [ "bitflags 1.3.2", "chalk-derive", @@ -194,9 +183,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e2cf7b70bedaaf3a8cf3c93b6120c2bb65be89389124028e724d19e209686e" +checksum = "4cd93fedbeeadc0cd4d0eb73bd061b621af99f5324a6a518264c8ef5e526e0ec" dependencies = [ "chalk-derive", "chalk-ir", @@ -207,15 +196,15 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.93.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc67c548d3854f64e97e67dc5b7c88513425c5bfa347cff96b7992ae6379288" +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", @@ -315,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" @@ -344,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" @@ -387,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" @@ -455,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" @@ -468,15 +457,6 @@ dependencies = [ "unicode-segmentation", ] -[[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" @@ -497,7 +477,7 @@ dependencies = [ "hir-def", "hir-expand", "hir-ty", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "rustc-hash", @@ -514,7 +494,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.3.2", + "bitflags 2.4.1", "cfg", "cov-mark", "dashmap", @@ -524,9 +504,9 @@ dependencies = [ "fst", "hashbrown 0.12.3", "hir-expand", - "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", @@ -554,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", @@ -574,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", @@ -586,7 +566,7 @@ dependencies = [ "hir-def", "hir-expand", "intern", - "itertools", + "itertools 0.12.0", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "nohash-hasher", @@ -633,7 +613,7 @@ dependencies = [ "ide-db", "ide-diagnostics", "ide-ssr", - "itertools", + "itertools 0.12.0", "nohash-hasher", "oorandom", "profile", @@ -659,7 +639,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "profile", "smallvec", "sourcegen", @@ -678,7 +658,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "smallvec", @@ -699,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", @@ -731,7 +711,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "once_cell", "profile", "serde_json", @@ -750,7 +730,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools", + "itertools 0.12.0", "nohash-hasher", "parser", "stdx", @@ -782,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]] @@ -838,6 +818,15 @@ dependencies = [ "either", ] +[[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" @@ -888,9 +877,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -942,7 +931,7 @@ dependencies = [ "crossbeam-channel", "ide", "ide-db", - "itertools", + "itertools 0.12.0", "proc-macro-api", "project-model", "tracing", @@ -1020,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" @@ -1123,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", @@ -1138,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]] @@ -1152,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", ] @@ -1186,12 +1174,6 @@ version = "11.1.3" 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" @@ -1289,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]] @@ -1359,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", ] @@ -1389,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", @@ -1454,12 +1436,12 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7082716cb2bbcd8b5f062fe950cbbc87f3aba022d6da4168db35af6732a7f15d" +checksum = "9a2ea80a299f04a896000ce17b76f3aa1d2fe59f347fbc99c4b8970316ef5a0d" dependencies = [ "bitflags 1.3.2", - "ra-ap-rustc_index 0.18.0", + "ra-ap-rustc_index 0.19.0", "tracing", ] @@ -1475,9 +1457,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e14b1fc835d6992b128a03a3f3a8365ba9f03e1c656a1670305f63f30d786d" +checksum = "4556489ef652e5eb6cdad6078fff08507badac80bfc1f79085c85a6d8b77ab5c" dependencies = [ "arrayvec", "smallvec", @@ -1495,9 +1477,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1762abb25eb1e37c1823f62b5da0821bbcd870812318db084c9516c2f78d2dcd" +checksum = "90e573bf707e01fe2841dbdedeed42012004274db0edc0314e6e3e58a40598fc" dependencies = [ "unicode-properties", "unicode-xid", @@ -1515,9 +1497,9 @@ dependencies = [ [[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", @@ -1525,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]] @@ -1583,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", @@ -1634,8 +1614,8 @@ name = "rustc-dependencies" version = "0.0.0" dependencies = [ "ra-ap-rustc_abi", - "ra-ap-rustc_index 0.18.0", - "ra-ap-rustc_lexer 0.18.0", + "ra-ap-rustc_index 0.19.0", + "ra-ap-rustc_lexer 0.19.0", "ra-ap-rustc_parse_format", ] @@ -1721,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", @@ -1759,7 +1739,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] @@ -1831,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", @@ -1848,7 +1828,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", "unicode-xid", ] @@ -1859,8 +1839,8 @@ dependencies = [ "cov-mark", "either", "expect-test", - "indexmap 2.0.0", - "itertools", + "indexmap 2.1.0", + "itertools 0.12.0", "once_cell", "parser", "proc-macro2", @@ -1894,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" @@ -1921,7 +1901,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.39", ] [[package]] @@ -2005,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", @@ -2017,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", @@ -2038,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", @@ -2061,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", @@ -2175,7 +2153,7 @@ name = "vfs" version = "0.0.0" dependencies = [ "fst", - "indexmap 2.0.0", + "indexmap 2.1.0", "nohash-hasher", "paths", "rustc-hash", @@ -2388,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 c382a5a37d23..73bb9c84d2c9 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -90,18 +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 } +xshell = "0.2.5" diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index ed380897278b..4324584df39d 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 e7f7adc78471..4322d2d966a1 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/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 99b8e9bf0e14..e4f2e14c51c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -13,19 +13,19 @@ doctest = false [dependencies] 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 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 fad4d7a4da66..6ecf1c20d6c1 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/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 718f241cf780..72ccc17486f0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -34,7 +34,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct FunctionData { pub name: Name, - pub params: Vec>, + pub params: Box<[Interned]>, pub ret_type: Interned, pub attrs: Attrs, pub visibility: RawVisibility, @@ -177,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>, + pub bounds: Box<[Interned]>, } impl TypeAliasData { @@ -210,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(), }) } } @@ -327,6 +327,7 @@ pub struct ImplData { pub self_ty: Interned, pub items: Vec, pub is_negative: bool, + pub is_unsafe: bool, // box it as the vec is usually empty anyways pub attribute_calls: Option, MacroCallId)>>>, } @@ -348,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)); @@ -357,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/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index b9c5ff72792a..1ebd1ba0e66d 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 { 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 { 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, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { // - 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, 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, prefer_no_std: bool, + prefer_prelude: bool, scope_name: Option, ) -> 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) { + #[track_caller] + fn check_found_path_( + ra_fixture: &str, + path: &str, + prefix_kind: Option, + 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(); + .expect("path does not resolve to a type"); - let found_path = - find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false); - assert_eq!(found_path, Some(mod_path), "{prefix_kind:?}"); + 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 1e2535a8a999..fac90e663045 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/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index c0b507d3911a..d589fbe347a9 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,13 +1,14 @@ //! A map of all publicly exported items in a crate. -use std::{collections::hash_map::Entry, fmt, hash::BuildHasherDefault}; +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::{ @@ -20,31 +21,28 @@ use crate::{ type FxIndexMap = IndexMap>; -// 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, 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, - + map: ImportMapIndex, /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the /// values returned by running `fst`. /// @@ -55,6 +53,12 @@ pub struct ImportMap { fst: fst::Map>, } +#[derive(Copy, Clone, PartialEq, Eq)] +enum IsTraitAssocItem { + Yes, + No, +} + impl ImportMap { pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("import_map_query"); @@ -64,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); } @@ -82,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 { +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); @@ -95,11 +104,13 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap FxIndexMap 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, @@ -193,13 +160,14 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap FxIndexMap, + map: &mut ImportMapIndex, tr: TraitId, is_type_in_ns: bool, trait_import_info: &ImportInfo, @@ -237,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); } } @@ -259,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(); @@ -272,7 +246,7 @@ 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, @@ -345,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, @@ -352,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 { @@ -388,7 +367,7 @@ impl Query { pub fn search_dependencies( db: &dyn DefDatabase, krate: CrateId, - query: Query, + ref query: Query, ) -> FxHashSet { let _p = profile::span("search_dependencies").detail(|| format!("{query:?}")); @@ -406,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); @@ -457,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 { @@ -495,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", @@ -543,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) { @@ -619,6 +631,7 @@ mod tests { main: - publ1 (t) - real_pu2 (t) + - real_pu2::Pub (t) - real_pub (t) - real_pub::Pub (t) "#]], @@ -644,6 +657,7 @@ mod tests { - sub (t) - sub::Def (t) - sub::subsub (t) + - sub::subsub::Def (t) "#]], ); } @@ -743,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 3c4f21d5c69b..70b96b25739c 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 @@ -709,6 +709,7 @@ pub struct Impl { pub target_trait: Option>, pub self_ty: Interned, pub is_negative: bool, + pub is_unsafe: bool, pub items: Box<[AssocItem]>, pub ast_id: FileAstId, } 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 c0a880a64bb6..c898eb5f9211 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 @@ -396,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))) } @@ -499,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 @@ -513,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))) } @@ -637,13 +632,13 @@ impl<'a> Ctx<'a> { Interned::new(generics) } - fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Vec> { + fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Box<[Interned]> { 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 5036c2b88297..ca3785bf28df 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 @@ -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 495e2d476970..fd8f64d6705b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -152,7 +152,7 @@ impl TryFrom 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 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 4aedb22c6bc9..106ead83fad7 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 b416f45ff208..2886b2a366c0 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 1f27204c1917..361bbec4318f 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/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 30b19b6e51b8..a04de10b899c 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 { // 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 { - // 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 204227e33873..ff0d279d8cce 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -12,15 +12,11 @@ use syntax::{ use triomphe::Arc; use crate::{ - ast_id_map::AstIdMap, - builtin_attr_macro::pseudo_derive_attr_expansion, - builtin_fn_macro::EagerExpander, - fixup, - hygiene::HygieneFrame, - name::{name, AsName}, - tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, - ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, - MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, + ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, + builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, AstId, BuiltinAttrExpander, + BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult, + ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, + MacroDefKind, MacroFile, ProcMacroExpander, }; /// Total limit on the number of tokens produced by any macro invocation. @@ -619,20 +615,7 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { - if let Some(name_ref) = - ast_id.to_node(db).path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) - { - name_ref.as_name() == name!(include) - } else { - false - } - } - _ => false, - }; - - if !skip_check_tt_count { + if !loc.def.is_include() { // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value; diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index c30807ad8849..f2d2451511f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -13,20 +13,20 @@ 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.93.0", default-features = false } -chalk-ir = "0.93.0" -chalk-recursive = { version = "0.93.0", default-features = false } -chalk-derive = "0.93.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 @@ -47,11 +47,9 @@ 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 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 c0b243ea2485..c9ab356854b8 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; @@ -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/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9c96b5ab8dbb..410bcbf0356f 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 { &self, def: DefWithBodyId, subst: Substitution, - env: Arc, + env: Arc, ) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] @@ -55,7 +55,7 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: ClosureId, subst: Substitution, - env: Arc, + env: Arc, ) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::borrowck_query)] @@ -81,7 +81,7 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: GeneralConstId, subst: Substitution, - trait_env: Option>, + trait_env: Option>, ) -> Result; #[salsa::invoke(crate::consteval::const_eval_static_query)] @@ -104,16 +104,12 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, def: AdtId, subst: Substitution, - env: Arc, + env: Arc, ) -> Result, 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, - ) -> Result, LayoutError>; + fn layout_of_ty(&self, ty: Ty, env: Arc) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; @@ -121,7 +117,7 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] fn lookup_impl_method( &self, - env: Arc, + env: Arc, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution); @@ -149,10 +145,10 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] - fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; + fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; #[salsa::invoke(crate::lower::trait_environment_query)] - fn trait_environment(&self, def: GenericDefId) -> Arc; + fn trait_environment(&self, def: GenericDefId) -> Arc; #[salsa::invoke(crate::lower::generic_defaults_query)] #[salsa::cycle(crate::lower::generic_defaults_recover)] @@ -249,7 +245,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn normalize_projection( &self, projection: crate::ProjectionTy, - env: Arc, + env: Arc, ) -> 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 ef43ed5c463f..c1b3619009bd 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, -} 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 b432588b2308..f4c079b48c58 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 @@ -19,8 +19,8 @@ use hir_def::{ data::adt::VariantData, hir::{Pat, PatId}, src::HasSource, - AdtId, AttrDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, ItemContainerId, - Lookup, ModuleDefId, ModuleId, StaticId, StructId, + AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, + StaticId, StructId, }; use hir_expand::{ name::{AsName, Name}, @@ -290,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; @@ -568,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; @@ -697,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; } @@ -747,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; } @@ -786,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/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index f6d6b00d740a..9ccf467358ec 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/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 0805e20447a7..af74df1032cb 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 @@ -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) = @@ -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/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 603e58f9d462..b2591f016d42 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::{ @@ -26,12 +28,6 @@ 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; @@ -73,13 +69,38 @@ pub type Variants = hir_def::layout::Variants), + HasErrorConst, + HasErrorType, + HasPlaceholder, + InvalidSimdType, + NotImplemented, + RecursiveTypeWithoutIndirection, SizeOverflow, TargetLayoutNotAvailable, - HasPlaceholder, - HasErrorType, - NotImplemented, 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> { @@ -118,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 @@ -134,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) @@ -146,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: @@ -259,9 +278,7 @@ pub fn layout_of_ty_query( 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)?; @@ -352,7 +369,7 @@ pub fn layout_of_ty_query( 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 } @@ -418,7 +435,7 @@ pub fn layout_of_ty_recover( _: &Ty, _: &Arc, ) -> Result, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result { 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 5e713c17cf81..58a06dc64354 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 @@ -145,7 +145,7 @@ pub fn layout_of_adt_recover( _: &Substitution, _: &Arc, ) -> Result, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } /// Finds the appropriate Integer type and signedness for the given @@ -169,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 ffdbb9de934d..5e3a86c80e3f 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(*mut T); struct Goal(BoxLike); } - 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(Foo); struct Goal(Foo); "#, - LayoutError::UserError("infinite sized recursive type".into()), + LayoutError::RecursiveTypeWithoutIndirection, ); } 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 c14339f6afe0..bcf7bfa0d273 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -81,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 9a61f1535993..c8a85b4a9ff0 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); + + // 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 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 + }) + }) + } + TypeBound::Lifetime(_) | TypeBound::Error => false, + } + } + WherePredicate::Lifetime { .. } => false, + }; 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. - - 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, - }; - - 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, - } - } - WherePredicate::Lifetime { .. } => false, - }) + .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 f3a5f69b2a63..87c93283361f 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 Trait 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, db: &dyn HirDatabase, 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 1e6e946a13f8..d16e0eb0137b 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 d36b885ec14a..48dd9540329d 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) { fn associated_type_in_argument() { check( r#" - trait A { - fn m(&self) -> i32; - } +trait A { + fn m(&self) -> i32; +} - fn x(k: &::Ty) { - k.m(); - } +fn x(k: &::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::(&inp); - //^^^^ expected &X, got &Y - } - "#, +fn ttt() { + let inp = Y; + x::(&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 3c7cfbaed3a4..467b94a2662a 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, // 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, } @@ -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 09ab60dd549f..4c1dfbc294e5 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" diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 67d3169243c6..cf9a2b73d9b1 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, @@ -280,3 +283,30 @@ pub struct MovedOutOfRef { pub ty: Type, pub span: InFile, } + +#[derive(Debug, PartialEq, Eq)] +pub struct IncoherentImpl { + pub file_id: HirFileId, + pub impl_: AstPtr, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplOrphan { + pub file_id: HirFileId, + pub impl_: AstPtr, +} + +// FIXME: Split this off into the corresponding 4 rustc errors +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplIncorrectSafety { + pub file_id: HirFileId, + pub impl_: AstPtr, + pub should_be_safe: bool, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct TraitImplMissingAssocItems { + pub file_id: HirFileId, + pub impl_: AstPtr, + 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 ac171026d5d7..5847c8a9fb52 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 93859611668e..bdc11aa3561b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -34,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}; @@ -54,14 +54,14 @@ 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, @@ -90,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, UnusedVariable, - }, + diagnostics::*, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, }; @@ -604,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()); @@ -614,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, @@ -665,8 +746,15 @@ impl Module { db: &dyn DefDatabase, item: impl Into, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { - 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 @@ -677,6 +765,7 @@ impl Module { item: impl Into, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { hir_def::find_path::find_path_prefixed( db, @@ -684,6 +773,7 @@ impl Module { self.into(), prefix_kind, prefer_no_std, + prefer_prelude, ) } } @@ -1447,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() { @@ -3390,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() } @@ -3398,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)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index 447e38f91f43..a622ec1a9532 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 b273ebc85a50..fbe17dbfd771 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>, 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 c0e5429a22c9..410c623109ea 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 @@ -2245,6 +2245,37 @@ impl b::LocalTrait for B { fn no_skip_default_2() -> Option<()> { todo!() } +} + "#, + ) + } + + #[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 c8b78b094169..2374da9a343f 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) }) @@ -440,11 +458,16 @@ fn build_pat( module: hir::Module, var: ExtendedVariant, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { 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 Some(match var.source(db)?.value.kind() { 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 36f68d176773..88fd0b1b7339 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::()?; - 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::().map(Either::Left).or_else(|| { + let callable_expr = ctx.find_node_at_offset::()?; + + 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::() { 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() } - None => { - let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", "); - let snip = format!("::<{fish_head}>"); - builder.insert(ident.text_range().end(), snip); + Either::Right(method_call) => { + edit.make_mut(method_call).get_or_create_generic_arg_list() + } + }; + + 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::*; @@ -363,6 +405,20 @@ fn main() { ); } + #[test] + fn add_type_ascription_missing_pattern() { + check_assist_not_applicable_by_label( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + let = make$0() +} +"#, + "Add `: _` before assignment operator", + ); + } + #[test] fn add_turbo_fish_function_lifetime_parameter() { check_assist( 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 cafd57a97727..f508c42c53e4 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 @@ -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; 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 index 082839118c58..11facc5bee2a 100644 --- 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 @@ -348,6 +348,7 @@ fn augment_references_with_imports( 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")) 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 872b52c98fff..d649f13d6ee3 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_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 32db5ee8dabf..1f3caa7db33f 100644 --- 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 @@ -205,6 +205,7 @@ fn augment_references_with_imports( 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( 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 f30ca2552d36..65b497e83aa2 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 { 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) -> Option>(); - 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, field_names: Vec, - // field_types: Vec, usages: Option, } 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, -) { - if let Some(usages) = data.usages.as_ref() { - for (file_id, refs) in usages.iter() { - builder.edit_file(*file_id); +) -> Option> { + let mut current_file_usages = None; - for r in refs { - edit_tuple_usage(ctx, builder, r, data, in_sub_pattern); + if let Some(usages) = data.usages.as_ref() { + // 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 (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 { 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 { @@ -296,7 +351,7 @@ fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option Option 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/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index de591cfde949..6b48d1588152 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 e4f64ccc754e..37db27a8fc02 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/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 81545396175b..473c699b595c 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_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 824255e4f8e3..7bfd599660db 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/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 4bf974a5655a..ff65aac82e07 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 239149dc411d..fde46db3058e 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 ac45581b7b44..69a4e748b7c5 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_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index dbbc56958f1e..f03eb6118a52 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/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index cc3e251a8f2a..25b3d6d9da91 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/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index a262570d94e7..f51e99a914e2 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 092fb303668f..60f90a41b962 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 f60ac150164c..7d38c638a8ed 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 9daa6984c3e3..d3c817d4b43a 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 0961021e48e0..d74d3b264ae5 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 @@ -257,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| { @@ -299,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| { @@ -336,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) 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 3d025f284bbb..ed5ddde8fbfe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -19,6 +19,7 @@ pub struct CompletionConfig { pub snippet_cap: Option, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub prefer_prelude: bool, pub snippets: Vec, pub limit: Option, } 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 2fad293d16d7..aaf7cd7843af 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -263,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/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 343719c53694..50618296ee43 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 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 284bdd8af21f..9db8e972dd4b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -68,6 +68,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { 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-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index faec74206811..4a2e770f193a 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/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index da5a951f0b7b..04263d15d0a5 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 @@ -220,9 +220,10 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefix_kind: PrefixKind, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec { 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. @@ -230,9 +231,10 @@ impl ImportAssets { &self, sema: &Semantics<'_, RootDatabase>, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec { 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. @@ -270,6 +272,7 @@ impl ImportAssets { sema: &Semantics<'_, RootDatabase>, prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, ) -> Vec { let _p = profile::span("import_assets::search_for"); @@ -281,6 +284,7 @@ impl ImportAssets { &self.module_with_candidate, prefixed, prefer_no_std, + prefer_prelude, ) }; @@ -594,11 +598,18 @@ fn get_mod_path( module_with_candidate: &Module, prefixed: Option, prefer_no_std: bool, + prefer_prelude: bool, ) -> Option { 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) } } 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 fb75b5b45848..fa9339f30f20 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 9c4f0ac8c9fc..22438a203bd7 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 39763479c65a..c7188f1f7943 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-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 14aa3940199a..f4055024cc32 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/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 85dbb7e6f263..0f12e814ba39 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 @@ -599,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` } } "#, 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 a337e2660d6a..659b74445f8f 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/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index c09be3fee7e1..d7dca1083a07 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 @@ -122,6 +122,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option *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/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 000000000000..251a645292ed --- /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::( + 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; +struct L<'l>; + + impl Drop for S {} + + impl<#[may_dangle] T> Drop for S {} +//^^^^ error: impl for unsafe trait needs to be unsafe + + unsafe impl Drop for S {} +//^^^^^^^^^^^ error: unsafe impl for safe trait + + unsafe impl<#[may_dangle] T> Drop for S {} + + 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 000000000000..40d0b6fdd4b0 --- /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::( + 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 000000000000..159d87d269de --- /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 {} +//- /bar.rs crate:bar +pub struct Bar(T); +//- /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 bar::Bar> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + + impl foo::Foo> for bar::Bar {} + + impl foo::Foo>> for bar::Bar> {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 {} +//- /bar.rs crate:bar +pub struct Bar(T); +#[lang = "owned_box"] +#[fundamental] +pub struct Box(T); +//- /main.rs crate:main deps:foo,bar +struct LocalType; + impl foo::Foo for bar::Box {} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types + impl foo::Foo for &LocalType {} + impl foo::Foo for bar::Box {} +"#, + ); + } + + #[test] + fn dyn_object() { + check_diagnostics( + r#" +//- /foo.rs crate:foo +pub trait Foo {} +//- /bar.rs crate:bar +pub struct Bar; +//- /main.rs crate:main deps:foo,bar +trait LocalTrait {} +impl foo::Foo for dyn LocalTrait {} +impl foo::Foo 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 14454fe8dc40..c92d92ceae8c 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 @@ -278,6 +278,7 @@ struct Foo; struct Bar; impl core::ops::Deref for Foo { type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } } fn main() { @@ -290,6 +291,7 @@ struct Foo; struct Bar; impl core::ops::Deref for Foo { type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } } fn main() { @@ -737,6 +739,21 @@ fn f() -> i32 { 0 } 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/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index fe5567544e83..6744895f3cd2 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; @@ -225,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 { @@ -247,6 +251,7 @@ impl DiagnosticsConfig { skip_glob_imports: false, }, prefer_no_std: false, + prefer_prelude: true, } } } @@ -356,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), 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 ee0e0354906e..c766a018bfd0 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::>(); - let fpos = actual.iter().filter(|x| !expected.contains(x)).collect::>(); + let fneg = expected + .iter() + .filter(|x| !actual.contains(x)) + .map(|(range, s)| (line_index.line_col(range.start()), range, s)) + .collect::>(); + let fpos = actual + .iter() + .filter(|x| !expected.contains(x)) + .map(|(range, s)| (line_index.line_col(range.start()), range, s)) + .collect::>(); 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 70ed6dea5bf2..56b29f92b829 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 60fcbbbd3979..0312a0f11ebd 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 2aee203c4ea5..d5c3439f9563 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/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 119a9c7c3f40..322077456775 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/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index e54bc48d555b..d3d492f3fdef 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", + description: "struct S {\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", + description: "struct S {\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) {} focus_range: 36..37, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -2908,7 +2919,7 @@ fn foo(ar$0g: &impl Foo) {} 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", + description: "struct B {}", }, }, HoverGotoTypeData { @@ -3034,7 +3045,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -3082,7 +3093,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 50..51, name: "B", kind: Struct, - description: "struct B", + description: "struct B {}", }, }, HoverGotoTypeData { @@ -3108,7 +3119,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 65..66, name: "S", kind: Struct, - description: "struct S", + description: "struct S {}", }, }, ], @@ -3335,7 +3346,7 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST(T); ``` "#]], ); @@ -3356,7 +3367,7 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST(T); ``` "#]], ); @@ -3378,7 +3389,7 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST(T); ``` "#]], ); @@ -5935,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 a5d070fe7673..24f44ca06ffb 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 4152e6067551..c9e9a2237867 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: 10752..10760, + range: 0..0, }, ), tooltip: "", @@ -497,7 +499,7 @@ fn main() { file_id: FileId( 1, ), - range: 10784..10788, + range: 0..0, }, ), tooltip: "", @@ -522,7 +524,7 @@ fn main() { file_id: FileId( 1, ), - range: 10752..10760, + range: 0..0, }, ), tooltip: "", @@ -535,7 +537,7 @@ fn main() { file_id: FileId( 1, ), - range: 10784..10788, + range: 0..0, }, ), tooltip: "", @@ -560,7 +562,7 @@ fn main() { file_id: FileId( 1, ), - range: 10752..10760, + range: 0..0, }, ), tooltip: "", @@ -573,7 +575,7 @@ fn main() { file_id: FileId( 1, ), - range: 10784..10788, + 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/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 2d0295692ac0..f387bbf6b014 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -683,6 +683,32 @@ enum Foo { ); } + #[test] + fn test_self() { + check( + r#" +struct S$0 { + t: PhantomData, +} + +impl S { + 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( diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index f041ca88ac08..31b9f6c76d06 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 7a795dd62ab7..68b592ffaa4d 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 82105522ebdd..adab1003d103 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/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs index 577eb0967b42..e38571dd3ec6 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/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 4229f289130f..2c2d2e8a9452 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 77b4afd7d7e1..12d7c07d3ed9 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 937834a82aef..56ce9d11c085 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 75977fc5b04f..3e48de6456b0 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/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index c85b3e53cda2..76f764460329 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" +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" @@ -81,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 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 230ff5f9b86a..0f6539f224d7 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, 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 c8df4255d96b..f28f6ffb8748 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -352,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\"", @@ -1117,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, } } @@ -1486,7 +1489,8 @@ impl Config { 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 @@ -1515,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, } } 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 2c402552919f..b7e839ac2a5e 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 @@ -146,6 +146,7 @@ fn integrated_completion_benchmark() { }, snippets: Vec::new(), prefer_no_std: false, + prefer_prelude: true, limit: None, }; let position = @@ -186,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/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 3fae08b82e27..8dba83ed5edd 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::>(); - // Workspaces are the same, but we've updated build data. self.workspaces = Arc::new(workspaces); } else { diff --git a/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml b/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml index a313507bff31..ba36fb0b0446 100644 --- a/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rustc-dependencies/Cargo.toml @@ -11,10 +11,10 @@ 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.18.0" } +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.18.0", default-features = false } -ra-ap-rustc_abi = { version = "0.18.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/sourcegen/Cargo.toml b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml index fb2b9ebef506..0514af8e7839 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 536f000a44b7..35986799fdd4 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/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index dc92366d1c7a..3b55921dc759 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -14,12 +14,12 @@ 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 @@ -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" diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 3603560d35c3..c3010d090c6b 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 a85e1d1d9d0e..37d8212042da 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; @@ -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] { @@ -559,7 +576,7 @@ impl ast::AssocItemList { None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), }, }; - let elements: Vec> = vec![ + let elements: Vec = vec![ make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ]; @@ -629,6 +646,50 @@ impl ast::MatchArmList { } } +impl ast::LetStmt { + pub fn set_ty(&self, ty: Option) { + 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'); @@ -753,7 +814,7 @@ impl ast::VariantList { None => (IndentLevel::single(), Position::last_child_of(self.syntax())), }, }; - let elements: Vec> = vec![ + let elements: Vec = vec![ make::tokens::whitespace(&format!("{}{indent}", "\n")).into(), variant.syntax().clone().into(), ast::make::token(T![,]).into(), @@ -788,6 +849,53 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } +impl ast::IdentPat { + pub fn set_pat(&self, pat: Option) { + 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() { @@ -889,6 +997,65 @@ mod tests { ); } + #[test] + fn test_ident_pat_set_pat() { + #[track_caller] + fn check(before: &str, expected: &str, pat: Option) { + let pat = pat.map(|it| it.clone_for_update()); + + let ident_pat = ast_mut_from_text::(&format!("fn f() {{ {before} }}")); + ident_pat.set_pat(pat); + + let after = ast_mut_from_text::(&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) { + let ty = ty.map(|it| it.clone_for_update()); + + let let_stmt = ast_mut_from_text::(&format!("fn f() {{ {before} }}")); + let_stmt.set_ty(ty); + + let after = ast_mut_from_text::(&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(); 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 7ba0d4dc656f..6c86e591044a 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 { support::child(&self.syntax) } pub fn ret_type(&self) -> Option { support::child(&self.syntax) } pub fn l_angle_token(&self) -> Option { support::token(&self.syntax, T![<]) } - pub fn path_type(&self) -> Option { support::child(&self.syntax) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) } + pub fn path_type(&self) -> Option { support::child(&self.syntax) } pub fn r_angle_token(&self) -> Option { support::token(&self.syntax, T![>]) } } @@ -1576,14 +1577,6 @@ impl RecordPatField { pub fn pat(&self) -> Option { support::child(&self.syntax) } } -#[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), @@ -1602,6 +1595,14 @@ pub enum Type { TupleType(TupleType), } +#[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), @@ -3319,41 +3320,6 @@ impl AstNode for RecordPatField { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl From for GenericArg { - fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) } -} -impl From for GenericArg { - fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) } -} -impl From for GenericArg { - fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) } -} -impl From 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 { - 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 for Type { fn from(node: ArrayType) -> Type { Type::ArrayType(node) } } @@ -3455,6 +3421,41 @@ impl AstNode for Type { } } } +impl From for GenericArg { + fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) } +} +impl From for GenericArg { + fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) } +} +impl From for GenericArg { + fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) } +} +impl From 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 { + 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 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 31a858b91a7d..ad63cc558629 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -941,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, +) -> 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, ) -> ast::GenericArgList { @@ -1126,7 +1133,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy> = 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", ) }); 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 691d0c618f38..be5b954ad345 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 { + 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/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index 2b5b6f495616..438b599ffaae 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 cc41d87f5d97..f2ca9d82ed0d 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 {} // 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 diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index 76d0ca5ccb60..4620cc72d0a2 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 11055f0280ac..fe6cb0a2c3fd 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 c35785cf98c4..11409f2eb819 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/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 0801e988f5ce..b66c9c943a16 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 7c76ae81bea0..7091ea1ce9a7 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -493,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"`):: + -- diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index b605de4c7b82..18d5ddd4d0af 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -946,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" } @@ -968,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/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 20fe781ae9e4..1c94f13d7451 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 c7b877b2897c..265c63f3e2dc 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -27,7 +27,7 @@ } }, "engines": { - "vscode": "^1.75.0" + "vscode": "^1.78.0" }, "enabledApiProposals": [], "scripts": { @@ -54,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", @@ -156,7 +156,7 @@ }, { "command": "rust-analyzer.expandMacro", - "title": "Expand macro recursively", + "title": "Expand macro recursively at caret", "category": "rust-analyzer" }, { @@ -209,11 +209,6 @@ "title": "Rebuild proc macros and build scripts", "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", @@ -1129,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", diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 7e24de664e9e..3d33d255ad49 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -870,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, diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 84d1ad98bd98..63ae386c8ad1 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 { + // 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") { diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 5de5aabc39f7..3073353674ce 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; } export async function deactivate() { @@ -152,7 +156,6 @@ function createCommands(): Record { 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 }, diff --git a/src/tools/rust-analyzer/editors/code/tsconfig.json b/src/tools/rust-analyzer/editors/code/tsconfig.json index ee353c28dd67..c74284a00d96 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 6c0d06f47178..b7b4a01818ea 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 7ec3247e943a..8d00813b0d7a 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/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 7a34617e2558..1c785b60a3d8 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/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index c7e6dd3615e9..6e630a80454a 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -122,7 +122,31 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position) } function valueCheck(fullPath, expected, result, error_text, queryName) { - if (Array.isArray(expected)) { + if (Array.isArray(expected) && result instanceof Map) { + const expected_set = new Set(); + for (const [key, expected_value] of expected) { + expected_set.add(key); + checkNeededFields(fullPath, expected_value, error_text, queryName, key); + if (result.has(key)) { + valueCheck( + fullPath + "[" + key + "]", + expected_value, + result.get(key), + error_text, + queryName + ); + } else { + error_text.push(`${queryName}==> EXPECTED has extra key in map from field ` + + `\`${fullPath}\` (key ${key}): \`${JSON.stringify(expected_value)}\``); + } + } + for (const [key, result_value] of result.entries()) { + if (!expected_set.has(key)) { + error_text.push(`${queryName}==> EXPECTED missing key in map from field ` + + `\`${fullPath}\` (key ${key}): \`${JSON.stringify(result_value)}\``); + } + } + } else if (Array.isArray(expected)) { let i; for (i = 0; i < expected.length; ++i) { checkNeededFields(fullPath, expected[i], error_text, queryName, i); @@ -153,6 +177,9 @@ function valueCheck(fullPath, expected, result, error_text, queryName) { } let result_v = result[key]; if (result_v !== null && key === "error") { + if (!result_v.forEach) { + throw result_v; + } result_v.forEach((value, index) => { value = value.split(" ").join(" "); if (index % 2 === 1) { diff --git a/src/tools/suggest-tests/src/lib.rs b/src/tools/suggest-tests/src/lib.rs index 44cd3c7f6a84..1c1d9d0333dd 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>(modified_files: &[T]) -> Vec { 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 (globs, sugs) in STATIC_SUGGESTIONS.iter() { + let globs = globs + .iter() + .map(|glob| Pattern::new(glob).expect("Found invalid glob pattern!")) + .collect::>(); + let matches_some_glob = |file: &str| globs.iter().any(|glob| glob.matches(file)); - for file in modified_files { - if glob.matches(file.as_ref()) { - suggestions.extend_from_slice(&sug.1); - } + 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 a84e78254f26..fbd265ea42a2 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)>> - = ::once_cell::unsync::Lazy::new(|| vec![ $( ($glob, vec![ $($suggestion),* ]) ),*]); + ($( [ $( $glob:expr ),* $(,)? ] => [ $( $suggestion:expr ),* $(,)? ] ),* $(,)? ) => { + pub(crate) const STATIC_SUGGESTIONS: ::once_cell::unsync::Lazy, Vec)>> + = ::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/tests/assembly/stack-protector/stack-protector-target-support.rs b/tests/assembly/stack-protector/stack-protector-target-support.rs index e5cbace80b1e..6d87fd1912b0 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 @@ -162,7 +161,7 @@ // [r77] needs-llvm-components: x86 // [r78] compile-flags:--target x86_64-linux-android // [r78] needs-llvm-components: x86 -// [r79] compile-flags:--target x86_64-sun-solaris +// [r79] compile-flags:--target x86_64-pc-solaris // [r79] needs-llvm-components: x86 // [r80] compile-flags:--target x86_64-unknown-freebsd // [r80] needs-llvm-components: x86 diff --git a/tests/incremental/commandline-args.rs b/tests/incremental/commandline-args.rs index 35b7183db7fa..e17e6feae074 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 b01f02444eae..12411a928799 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 367416430f86..f1d7de145593 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 f5cae15a4bc7..8ed469db6e63 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 38ab28461911..a036d3e69fe4 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/inline/exponential_runtime.main.Inline.panic-abort.diff b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff index 217c8b802caa..3ffe2ee0c0b9 100644 --- a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff @@ -69,58 +69,58 @@ + } + + bb2: { -+ StorageDead(_7); -+ StorageDead(_6); -+ StorageDead(_5); -+ _3 = <() as F>::call() -> [return: bb3, unwind unreachable]; -+ } -+ -+ bb3: { + _4 = <() as F>::call() -> [return: bb1, unwind unreachable]; + } + ++ bb3: { ++ StorageDead(_7); ++ StorageDead(_6); ++ StorageDead(_5); ++ _3 = <() as F>::call() -> [return: bb2, unwind unreachable]; ++ } ++ + bb4: { -+ StorageDead(_10); -+ StorageDead(_9); -+ StorageDead(_8); -+ _6 = <() as E>::call() -> [return: bb5, unwind unreachable]; ++ _7 = <() as E>::call() -> [return: bb3, unwind unreachable]; + } + + bb5: { -+ _7 = <() as E>::call() -> [return: bb2, unwind unreachable]; ++ StorageDead(_10); ++ StorageDead(_9); ++ StorageDead(_8); ++ _6 = <() as E>::call() -> [return: bb4, unwind unreachable]; + } + + bb6: { -+ StorageDead(_13); -+ StorageDead(_12); -+ StorageDead(_11); -+ _9 = <() as D>::call() -> [return: bb7, unwind unreachable]; ++ _10 = <() as D>::call() -> [return: bb5, unwind unreachable]; + } + + bb7: { -+ _10 = <() as D>::call() -> [return: bb4, unwind unreachable]; ++ StorageDead(_13); ++ StorageDead(_12); ++ StorageDead(_11); ++ _9 = <() as D>::call() -> [return: bb6, unwind unreachable]; + } + + bb8: { -+ StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_14); -+ _12 = <() as C>::call() -> [return: bb9, unwind unreachable]; ++ _13 = <() as C>::call() -> [return: bb7, unwind unreachable]; + } + + bb9: { -+ _13 = <() as C>::call() -> [return: bb6, unwind unreachable]; ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_14); ++ _12 = <() as C>::call() -> [return: bb8, unwind unreachable]; + } + + bb10: { -+ StorageDead(_19); -+ StorageDead(_18); -+ StorageDead(_17); -+ _15 = <() as B>::call() -> [return: bb11, unwind unreachable]; ++ _16 = <() as B>::call() -> [return: bb9, unwind unreachable]; + } + + bb11: { -+ _16 = <() as B>::call() -> [return: bb8, unwind unreachable]; ++ StorageDead(_19); ++ StorageDead(_18); ++ StorageDead(_17); ++ _15 = <() as B>::call() -> [return: bb10, unwind unreachable]; + } + + bb12: { @@ -128,7 +128,7 @@ + } + + bb13: { -+ _19 = <() as A>::call() -> [return: bb10, unwind unreachable]; ++ _19 = <() as A>::call() -> [return: bb11, unwind unreachable]; } } diff --git a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff index 0a4ce40c529d..3f334779e18f 100644 --- a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff @@ -69,58 +69,58 @@ + } + + bb2: { -+ StorageDead(_7); -+ StorageDead(_6); -+ StorageDead(_5); -+ _3 = <() as F>::call() -> [return: bb3, unwind continue]; -+ } -+ -+ bb3: { + _4 = <() as F>::call() -> [return: bb1, unwind continue]; + } + ++ bb3: { ++ StorageDead(_7); ++ StorageDead(_6); ++ StorageDead(_5); ++ _3 = <() as F>::call() -> [return: bb2, unwind continue]; ++ } ++ + bb4: { -+ StorageDead(_10); -+ StorageDead(_9); -+ StorageDead(_8); -+ _6 = <() as E>::call() -> [return: bb5, unwind continue]; ++ _7 = <() as E>::call() -> [return: bb3, unwind continue]; + } + + bb5: { -+ _7 = <() as E>::call() -> [return: bb2, unwind continue]; ++ StorageDead(_10); ++ StorageDead(_9); ++ StorageDead(_8); ++ _6 = <() as E>::call() -> [return: bb4, unwind continue]; + } + + bb6: { -+ StorageDead(_13); -+ StorageDead(_12); -+ StorageDead(_11); -+ _9 = <() as D>::call() -> [return: bb7, unwind continue]; ++ _10 = <() as D>::call() -> [return: bb5, unwind continue]; + } + + bb7: { -+ _10 = <() as D>::call() -> [return: bb4, unwind continue]; ++ StorageDead(_13); ++ StorageDead(_12); ++ StorageDead(_11); ++ _9 = <() as D>::call() -> [return: bb6, unwind continue]; + } + + bb8: { -+ StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_14); -+ _12 = <() as C>::call() -> [return: bb9, unwind continue]; ++ _13 = <() as C>::call() -> [return: bb7, unwind continue]; + } + + bb9: { -+ _13 = <() as C>::call() -> [return: bb6, unwind continue]; ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_14); ++ _12 = <() as C>::call() -> [return: bb8, unwind continue]; + } + + bb10: { -+ StorageDead(_19); -+ StorageDead(_18); -+ StorageDead(_17); -+ _15 = <() as B>::call() -> [return: bb11, unwind continue]; ++ _16 = <() as B>::call() -> [return: bb9, unwind continue]; + } + + bb11: { -+ _16 = <() as B>::call() -> [return: bb8, unwind continue]; ++ StorageDead(_19); ++ StorageDead(_18); ++ StorageDead(_17); ++ _15 = <() as B>::call() -> [return: bb10, unwind continue]; + } + + bb12: { @@ -128,7 +128,7 @@ + } + + bb13: { -+ _19 = <() as A>::call() -> [return: bb10, unwind continue]; ++ _19 = <() as A>::call() -> [return: bb11, unwind continue]; } } diff --git a/tests/mir-opt/inline/indirect_destination.rs b/tests/mir-opt/inline/indirect_destination.rs new file mode 100644 index 000000000000..2842e23366e0 --- /dev/null +++ b/tests/mir-opt/inline/indirect_destination.rs @@ -0,0 +1,42 @@ +// Test for inlining with an indirect destination place. +// +// unit-test: Inline +// edition: 2021 +// needs-unwind +#![crate_type = "lib"] +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "initial")] +// CHECK-LABEL: fn f( +// CHECK: bb1: { +// CHECK-NEXT: StorageLive([[A:.*]]); +// CHECK-NEXT: [[A]] = &mut (*_1); +// CHECK-NEXT: StorageLive([[B:.*]]); +// CHECK-NEXT: [[B]] = const 42_u8; +// CHECK-NEXT: (*[[A]]) = move [[B]]; +// CHECK-NEXT: StorageDead([[B]]); +// CHECK-NEXT: StorageDead([[A]]); +// CHECK-NEXT: goto -> bb1; +// CHECK-NEXT: } +pub fn f(a: *mut u8) { + mir! { + { + Goto(bb1) + } + bb1 = { + Call(*a = g(), bb1, UnwindUnreachable()) + } + } +} + +#[custom_mir(dialect = "runtime", phase = "initial")] +#[inline(always)] +fn g() -> u8 { + mir! { + { + RET = 42; + Return() + } + } +} diff --git a/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-abort.diff index 40eeda539087..42dd7ba55cf4 100644 --- a/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-abort.diff @@ -50,20 +50,20 @@ bb1: { - _3 = &mut _4; - _2 = Pin::<&mut {coroutine@$DIR/inline_coroutine.rs:19:5: 19:8}>::new(move _3) -> [return: bb2, unwind unreachable]; -+ StorageDead(_7); -+ StorageDead(_6); -+ StorageDead(_5); -+ StorageDead(_2); -+ drop(_4) -> [return: bb2, unwind unreachable]; ++ StorageDead(_4); ++ _0 = const (); ++ StorageDead(_1); ++ return; } bb2: { - StorageDead(_3); - _1 = <{coroutine@$DIR/inline_coroutine.rs:19:5: 19:8} as Coroutine>::resume(move _2, const false) -> [return: bb3, unwind unreachable]; -+ StorageDead(_4); -+ _0 = const (); -+ StorageDead(_1); -+ return; ++ StorageDead(_7); ++ StorageDead(_6); ++ StorageDead(_5); ++ StorageDead(_2); ++ drop(_4) -> [return: bb1, unwind unreachable]; } bb3: { @@ -90,7 +90,7 @@ + bb6: { + _1 = CoroutineState::::Yielded(move _8); + discriminant((*_6)) = 3; -+ goto -> bb1; ++ goto -> bb2; + } + + bb7: { @@ -102,7 +102,7 @@ + StorageDead(_8); + _1 = CoroutineState::::Complete(_5); + discriminant((*_6)) = 1; -+ goto -> bb1; ++ goto -> bb2; + } + + bb9: { diff --git a/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-unwind.diff index fdb42bf3d8a0..7b8958c13fc5 100644 --- a/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_coroutine.main.Inline.panic-unwind.diff @@ -34,18 +34,10 @@ StorageLive(_3); StorageLive(_4); - _4 = g() -> [return: bb1, unwind continue]; -- } -- -- bb1: { + _4 = {coroutine@$DIR/inline_coroutine.rs:19:5: 19:8 (#0)}; - _3 = &mut _4; -- _2 = Pin::<&mut {coroutine@$DIR/inline_coroutine.rs:19:5: 19:8}>::new(move _3) -> [return: bb2, unwind: bb5]; -- } -- -- bb2: { ++ _3 = &mut _4; + _2 = Pin::<&mut {coroutine@$DIR/inline_coroutine.rs:19:5: 19:8}> { pointer: move _3 }; - StorageDead(_3); -- _1 = <{coroutine@$DIR/inline_coroutine.rs:19:5: 19:8} as Coroutine>::resume(move _2, const false) -> [return: bb3, unwind: bb5]; ++ StorageDead(_3); + StorageLive(_5); + _5 = const false; + StorageLive(_6); @@ -55,40 +47,50 @@ + switchInt(move _7) -> [0: bb5, 1: bb9, 3: bb10, otherwise: bb11]; } + bb1: { +- _3 = &mut _4; +- _2 = Pin::<&mut {coroutine@$DIR/inline_coroutine.rs:19:5: 19:8}>::new(move _3) -> [return: bb2, unwind: bb5]; ++ StorageDead(_4); ++ _0 = const (); ++ StorageDead(_1); ++ return; + } + +- bb2: { +- StorageDead(_3); +- _1 = <{coroutine@$DIR/inline_coroutine.rs:19:5: 19:8} as Coroutine>::resume(move _2, const false) -> [return: bb3, unwind: bb5]; ++ bb2 (cleanup): { ++ drop(_4) -> [return: bb3, unwind terminate(cleanup)]; + } + - bb3: { -+ bb1: { +- StorageDead(_2); +- drop(_4) -> [return: bb4, unwind: bb6]; ++ bb3 (cleanup): { ++ resume; + } + + bb4: { +- StorageDead(_4); +- _0 = const (); +- StorageDead(_1); +- return; + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); - StorageDead(_2); -- drop(_4) -> [return: bb4, unwind: bb6]; -+ drop(_4) -> [return: bb2, unwind: bb4]; - } - -- bb4: { -+ bb2: { - StorageDead(_4); - _0 = const (); - StorageDead(_1); - return; ++ StorageDead(_2); ++ drop(_4) -> [return: bb1, unwind: bb3]; } - bb5 (cleanup): { - drop(_4) -> [return: bb6, unwind terminate(cleanup)]; -+ bb3 (cleanup): { -+ drop(_4) -> [return: bb4, unwind terminate(cleanup)]; - } - -- bb6 (cleanup): { -+ bb4 (cleanup): { - resume; -+ } -+ + bb5: { + StorageLive(_8); + switchInt(_5) -> [0: bb6, otherwise: bb7]; -+ } -+ + } + +- bb6 (cleanup): { +- resume; + bb6: { + _8 = const 13_i32; + goto -> bb8; @@ -102,11 +104,11 @@ + bb8: { + _1 = CoroutineState::::Yielded(move _8); + discriminant((*_6)) = 3; -+ goto -> bb1; ++ goto -> bb4; + } + + bb9: { -+ assert(const false, "coroutine resumed after completion") -> [success: bb9, unwind: bb3]; ++ assert(const false, "coroutine resumed after completion") -> [success: bb9, unwind: bb2]; + } + + bb10: { @@ -114,7 +116,7 @@ + StorageDead(_8); + _1 = CoroutineState::::Complete(_5); + discriminant((*_6)) = 1; -+ goto -> bb1; ++ goto -> bb4; + } + + bb11: { diff --git a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff deleted file mode 100644 index b90e0505c54e..000000000000 --- a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff +++ /dev/null @@ -1,209 +0,0 @@ -- // MIR for `main` before Inline -+ // MIR for `main` after Inline - - fn main() -> () { - let mut _0: (); - let _1: std::boxed::Box>; - let mut _2: std::vec::Vec; - scope 1 { - debug _x => _1; - } -+ scope 2 (inlined Vec::::new) { -+ let mut _3: alloc::raw_vec::RawVec; -+ } -+ scope 3 (inlined Box::>::new) { -+ debug x => _2; -+ let mut _4: usize; -+ let mut _5: usize; -+ let mut _6: *mut u8; -+ let mut _7: *const std::vec::Vec; -+ scope 4 { -+ scope 5 (inlined alloc::alloc::exchange_malloc) { -+ debug size => _4; -+ debug align => _5; -+ let _8: std::alloc::Layout; -+ let mut _9: std::result::Result, std::alloc::AllocError>; -+ let mut _10: isize; -+ let mut _12: !; -+ scope 6 { -+ debug layout => _8; -+ let _11: std::ptr::NonNull<[u8]>; -+ let mut _13: &std::alloc::Global; -+ scope 8 { -+ debug ptr => _11; -+ scope 18 (inlined NonNull::<[u8]>::as_mut_ptr) { -+ debug self => _11; -+ let mut _15: std::ptr::NonNull; -+ scope 19 (inlined NonNull::<[u8]>::as_non_null_ptr) { -+ debug self => _11; -+ let mut _16: *mut u8; -+ let mut _17: *mut [u8]; -+ scope 20 { -+ scope 21 (inlined NonNull::<[u8]>::as_ptr) { -+ debug self => _11; -+ let mut _18: *const [u8]; -+ } -+ scope 22 (inlined ptr::mut_ptr::::as_mut_ptr) { -+ debug self => _17; -+ } -+ scope 23 (inlined NonNull::::new_unchecked) { -+ debug ptr => _16; -+ let mut _19: *const u8; -+ scope 24 { -+ scope 25 (inlined NonNull::::new_unchecked::runtime::) { -+ debug ptr => _16; -+ scope 26 (inlined ptr::mut_ptr::::is_null) { -+ debug self => _16; -+ let mut _20: *mut u8; -+ scope 27 { -+ scope 28 (inlined ptr::mut_ptr::::is_null::runtime_impl) { -+ debug ptr => _20; -+ scope 29 (inlined ptr::mut_ptr::::addr) { -+ debug self => _20; -+ scope 30 { -+ scope 31 (inlined ptr::mut_ptr::::cast::<()>) { -+ debug self => _20; -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ scope 32 (inlined NonNull::::as_ptr) { -+ debug self => _15; -+ let mut _21: *const u8; -+ } -+ } -+ } -+ scope 17 (inlined ::allocate) { -+ debug self => const _; -+ debug layout => _8; -+ } -+ } -+ scope 7 { -+ scope 9 (inlined Layout::from_size_align_unchecked) { -+ debug size => _4; -+ debug align => _5; -+ let mut _14: std::ptr::Alignment; -+ scope 10 { -+ scope 11 (inlined std::ptr::Alignment::new_unchecked) { -+ debug align => _5; -+ scope 12 { -+ scope 14 (inlined std::ptr::Alignment::new_unchecked::runtime) { -+ debug align => _5; -+ scope 15 (inlined core::num::::is_power_of_two) { -+ debug self => _5; -+ scope 16 (inlined core::num::::count_ones) { -+ debug self => _5; -+ } -+ } -+ } -+ } -+ scope 13 { -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } - - bb0: { - StorageLive(_1); - StorageLive(_2); -- _2 = Vec::::new() -> [return: bb1, unwind unreachable]; -+ StorageLive(_3); -+ _3 = const _; -+ _2 = Vec:: { buf: move _3, len: const 0_usize }; -+ StorageDead(_3); -+ StorageLive(_4); -+ StorageLive(_5); -+ StorageLive(_6); -+ StorageLive(_7); -+ _4 = SizeOf(std::vec::Vec); -+ _5 = AlignOf(std::vec::Vec); -+ StorageLive(_8); -+ StorageLive(_10); -+ StorageLive(_11); -+ StorageLive(_12); -+ StorageLive(_13); -+ StorageLive(_14); -+ _14 = _5 as std::ptr::Alignment (Transmute); -+ _8 = Layout { size: _4, align: move _14 }; -+ StorageDead(_14); -+ StorageLive(_9); -+ _13 = const _; -+ _9 = std::alloc::Global::alloc_impl(move _13, _8, const false) -> [return: bb5, unwind unreachable]; - } - - bb1: { -- _1 = Box::>::new(move _2) -> [return: bb2, unwind unreachable]; -+ StorageDead(_1); -+ return; - } - - bb2: { -+ _12 = handle_alloc_error(move _8) -> unwind unreachable; -+ } -+ -+ bb3: { -+ unreachable; -+ } -+ -+ bb4: { -+ _11 = ((_9 as Ok).0: std::ptr::NonNull<[u8]>); -+ StorageLive(_15); -+ StorageLive(_16); -+ StorageLive(_17); -+ StorageLive(_18); -+ _18 = (_11.0: *const [u8]); -+ _17 = move _18 as *mut [u8] (PtrToPtr); -+ StorageDead(_18); -+ _16 = _17 as *mut u8 (PtrToPtr); -+ StorageDead(_17); -+ StorageLive(_19); -+ StorageLive(_20); -+ _19 = _16 as *const u8 (PointerCoercion(MutToConstPointer)); -+ _15 = NonNull:: { pointer: _19 }; -+ StorageDead(_20); -+ StorageDead(_19); -+ StorageDead(_16); -+ StorageLive(_21); -+ _21 = (_15.0: *const u8); -+ _6 = move _21 as *mut u8 (PtrToPtr); -+ StorageDead(_21); -+ StorageDead(_15); -+ StorageDead(_9); -+ StorageDead(_13); -+ StorageDead(_12); -+ StorageDead(_11); -+ StorageDead(_10); -+ StorageDead(_8); -+ _1 = ShallowInitBox(move _6, std::vec::Vec); -+ _7 = (((_1.0: std::ptr::Unique>).0: std::ptr::NonNull>).0: *const std::vec::Vec); -+ (*_7) = move _2; -+ StorageDead(_7); -+ StorageDead(_6); -+ StorageDead(_5); -+ StorageDead(_4); - StorageDead(_2); - _0 = const (); -- drop(_1) -> [return: bb3, unwind unreachable]; -+ drop(_1) -> [return: bb1, unwind unreachable]; - } - -- bb3: { -- StorageDead(_1); -- return; -+ bb5: { -+ _10 = discriminant(_9); -+ switchInt(move _10) -> [0: bb4, 1: bb2, otherwise: bb3]; - } - } - diff --git a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff deleted file mode 100644 index f9c637caa18a..000000000000 --- a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff +++ /dev/null @@ -1,222 +0,0 @@ -- // MIR for `main` before Inline -+ // MIR for `main` after Inline - - fn main() -> () { - let mut _0: (); - let _1: std::boxed::Box>; - let mut _2: std::vec::Vec; - scope 1 { - debug _x => _1; - } -+ scope 2 (inlined Vec::::new) { -+ let mut _3: alloc::raw_vec::RawVec; -+ } -+ scope 3 (inlined Box::>::new) { -+ debug x => _2; -+ let mut _4: usize; -+ let mut _5: usize; -+ let mut _6: *mut u8; -+ let mut _7: *const std::vec::Vec; -+ scope 4 { -+ scope 5 (inlined alloc::alloc::exchange_malloc) { -+ debug size => _4; -+ debug align => _5; -+ let _8: std::alloc::Layout; -+ let mut _9: std::result::Result, std::alloc::AllocError>; -+ let mut _10: isize; -+ let mut _12: !; -+ scope 6 { -+ debug layout => _8; -+ let _11: std::ptr::NonNull<[u8]>; -+ let mut _13: &std::alloc::Global; -+ scope 8 { -+ debug ptr => _11; -+ scope 18 (inlined NonNull::<[u8]>::as_mut_ptr) { -+ debug self => _11; -+ let mut _15: std::ptr::NonNull; -+ scope 19 (inlined NonNull::<[u8]>::as_non_null_ptr) { -+ debug self => _11; -+ let mut _16: *mut u8; -+ let mut _17: *mut [u8]; -+ scope 20 { -+ scope 21 (inlined NonNull::<[u8]>::as_ptr) { -+ debug self => _11; -+ let mut _18: *const [u8]; -+ } -+ scope 22 (inlined ptr::mut_ptr::::as_mut_ptr) { -+ debug self => _17; -+ } -+ scope 23 (inlined NonNull::::new_unchecked) { -+ debug ptr => _16; -+ let mut _19: *const u8; -+ scope 24 { -+ scope 25 (inlined NonNull::::new_unchecked::runtime::) { -+ debug ptr => _16; -+ scope 26 (inlined ptr::mut_ptr::::is_null) { -+ debug self => _16; -+ let mut _20: *mut u8; -+ scope 27 { -+ scope 28 (inlined ptr::mut_ptr::::is_null::runtime_impl) { -+ debug ptr => _20; -+ scope 29 (inlined ptr::mut_ptr::::addr) { -+ debug self => _20; -+ scope 30 { -+ scope 31 (inlined ptr::mut_ptr::::cast::<()>) { -+ debug self => _20; -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ scope 32 (inlined NonNull::::as_ptr) { -+ debug self => _15; -+ let mut _21: *const u8; -+ } -+ } -+ } -+ scope 17 (inlined ::allocate) { -+ debug self => const _; -+ debug layout => _8; -+ } -+ } -+ scope 7 { -+ scope 9 (inlined Layout::from_size_align_unchecked) { -+ debug size => _4; -+ debug align => _5; -+ let mut _14: std::ptr::Alignment; -+ scope 10 { -+ scope 11 (inlined std::ptr::Alignment::new_unchecked) { -+ debug align => _5; -+ scope 12 { -+ scope 14 (inlined std::ptr::Alignment::new_unchecked::runtime) { -+ debug align => _5; -+ scope 15 (inlined core::num::::is_power_of_two) { -+ debug self => _5; -+ scope 16 (inlined core::num::::count_ones) { -+ debug self => _5; -+ } -+ } -+ } -+ } -+ scope 13 { -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ } - - bb0: { - StorageLive(_1); - StorageLive(_2); -- _2 = Vec::::new() -> [return: bb1, unwind continue]; -+ StorageLive(_3); -+ _3 = const _; -+ _2 = Vec:: { buf: move _3, len: const 0_usize }; -+ StorageDead(_3); -+ StorageLive(_4); -+ StorageLive(_5); -+ StorageLive(_6); -+ StorageLive(_7); -+ _4 = SizeOf(std::vec::Vec); -+ _5 = AlignOf(std::vec::Vec); -+ StorageLive(_8); -+ StorageLive(_10); -+ StorageLive(_11); -+ StorageLive(_12); -+ StorageLive(_13); -+ StorageLive(_14); -+ _14 = _5 as std::ptr::Alignment (Transmute); -+ _8 = Layout { size: _4, align: move _14 }; -+ StorageDead(_14); -+ StorageLive(_9); -+ _13 = const _; -+ _9 = std::alloc::Global::alloc_impl(move _13, _8, const false) -> [return: bb7, unwind: bb3]; - } - - bb1: { -- _1 = Box::>::new(move _2) -> [return: bb2, unwind: bb4]; -+ StorageDead(_1); -+ return; - } - -- bb2: { -- StorageDead(_2); -- _0 = const (); -- drop(_1) -> [return: bb3, unwind: bb4]; -+ bb2 (cleanup): { -+ resume; - } - -- bb3: { -- StorageDead(_1); -- return; -+ bb3 (cleanup): { -+ drop(_2) -> [return: bb2, unwind terminate(cleanup)]; - } - -- bb4 (cleanup): { -- resume; -+ bb4: { -+ _12 = handle_alloc_error(move _8) -> bb3; -+ } -+ -+ bb5: { -+ unreachable; -+ } -+ -+ bb6: { -+ _11 = ((_9 as Ok).0: std::ptr::NonNull<[u8]>); -+ StorageLive(_15); -+ StorageLive(_16); -+ StorageLive(_17); -+ StorageLive(_18); -+ _18 = (_11.0: *const [u8]); -+ _17 = move _18 as *mut [u8] (PtrToPtr); -+ StorageDead(_18); -+ _16 = _17 as *mut u8 (PtrToPtr); -+ StorageDead(_17); -+ StorageLive(_19); -+ StorageLive(_20); -+ _19 = _16 as *const u8 (PointerCoercion(MutToConstPointer)); -+ _15 = NonNull:: { pointer: _19 }; -+ StorageDead(_20); -+ StorageDead(_19); -+ StorageDead(_16); -+ StorageLive(_21); -+ _21 = (_15.0: *const u8); -+ _6 = move _21 as *mut u8 (PtrToPtr); -+ StorageDead(_21); -+ StorageDead(_15); -+ StorageDead(_9); -+ StorageDead(_13); -+ StorageDead(_12); -+ StorageDead(_11); -+ StorageDead(_10); -+ StorageDead(_8); -+ _1 = ShallowInitBox(move _6, std::vec::Vec); -+ _7 = (((_1.0: std::ptr::Unique>).0: std::ptr::NonNull>).0: *const std::vec::Vec); -+ (*_7) = move _2; -+ StorageDead(_7); -+ StorageDead(_6); -+ StorageDead(_5); -+ StorageDead(_4); -+ StorageDead(_2); -+ _0 = const (); -+ drop(_1) -> [return: bb1, unwind: bb2]; -+ } -+ -+ bb7: { -+ _10 = discriminant(_9); -+ switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5]; - } - } - diff --git a/tests/mir-opt/inline/inline_into_box_place.rs b/tests/mir-opt/inline/inline_into_box_place.rs deleted file mode 100644 index 65f8e2916b63..000000000000 --- a/tests/mir-opt/inline/inline_into_box_place.rs +++ /dev/null @@ -1,11 +0,0 @@ -// ignore-endian-big -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY -// ignore-debug MIR alignment checks in std alter the diff, breaking the test -// compile-flags: -Zmir-opt-level=4 -Zinline-mir-hint-threshold=200 - -// EMIT_MIR inline_into_box_place.main.Inline.diff -fn main() { - // CHECK-LABEL: fn main( - // CHECK: (inlined Box::>::new) - let _x: Box> = Box::new(Vec::new()); -} diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff index f17c9ba3f9fc..f50603a66a56 100644 --- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-abort.diff @@ -60,16 +60,6 @@ } bb1: { - StorageDead(_12); - StorageDead(_11); - StorageDead(_10); - StorageDead(_4); - _5 = discriminant(_3); -- switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; -+ goto -> bb2; - } - - bb2: { StorageLive(_9); _9 = ((_3 as Continue).0: i32); _2 = _9; @@ -77,14 +67,14 @@ _0 = Result::::Ok(move _2); StorageDead(_2); StorageDead(_3); - goto -> bb5; + goto -> bb4; } - bb3: { + bb2: { unreachable; } - bb4: { + bb3: { StorageLive(_6); _6 = ((_3 as Break).0: std::result::Result); StorageLive(_8); @@ -100,11 +90,21 @@ StorageDead(_6); StorageDead(_2); StorageDead(_3); - goto -> bb5; + goto -> bb4; + } + + bb4: { + return; } bb5: { - return; + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_4); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; ++ goto -> bb1; } bb6: { @@ -113,7 +113,7 @@ _13 = Result::::Err(move _12); _3 = ControlFlow::, i32>::Break(move _13); StorageDead(_13); -- goto -> bb1; +- goto -> bb5; + goto -> bb9; } @@ -124,7 +124,7 @@ bb8: { _11 = move ((_4 as Ok).0: i32); _3 = ControlFlow::, i32>::Continue(move _11); - goto -> bb1; + goto -> bb5; + } + + bb9: { @@ -133,7 +133,7 @@ + StorageDead(_10); + StorageDead(_4); + _5 = discriminant(_3); -+ goto -> bb4; ++ goto -> bb3; } } diff --git a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff index f17c9ba3f9fc..f50603a66a56 100644 --- a/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.identity.JumpThreading.panic-unwind.diff @@ -60,16 +60,6 @@ } bb1: { - StorageDead(_12); - StorageDead(_11); - StorageDead(_10); - StorageDead(_4); - _5 = discriminant(_3); -- switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; -+ goto -> bb2; - } - - bb2: { StorageLive(_9); _9 = ((_3 as Continue).0: i32); _2 = _9; @@ -77,14 +67,14 @@ _0 = Result::::Ok(move _2); StorageDead(_2); StorageDead(_3); - goto -> bb5; + goto -> bb4; } - bb3: { + bb2: { unreachable; } - bb4: { + bb3: { StorageLive(_6); _6 = ((_3 as Break).0: std::result::Result); StorageLive(_8); @@ -100,11 +90,21 @@ StorageDead(_6); StorageDead(_2); StorageDead(_3); - goto -> bb5; + goto -> bb4; + } + + bb4: { + return; } bb5: { - return; + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_4); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; ++ goto -> bb1; } bb6: { @@ -113,7 +113,7 @@ _13 = Result::::Err(move _12); _3 = ControlFlow::, i32>::Break(move _13); StorageDead(_13); -- goto -> bb1; +- goto -> bb5; + goto -> bb9; } @@ -124,7 +124,7 @@ bb8: { _11 = move ((_4 as Ok).0: i32); _3 = ControlFlow::, i32>::Continue(move _11); - goto -> bb1; + goto -> bb5; + } + + bb9: { @@ -133,7 +133,7 @@ + StorageDead(_10); + StorageDead(_4); + _5 = discriminant(_3); -+ goto -> bb4; ++ goto -> bb3; } } diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 852dcd0db011..66e5c5d3c11c 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -52,19 +52,19 @@ fn identity(x: Result) -> Result { // CHECK: [[x:_.*]] = _1; // CHECK: switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7]; // CHECK: bb1: { - // CHECK: goto -> bb2; - // CHECK: bb2: { // CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32); // CHECK: _0 = Result::::Ok( - // CHECK: goto -> bb5; - // CHECK: bb3: { + // CHECK: goto -> bb4; + // CHECK: bb2: { // CHECK: unreachable; - // CHECK: bb4: { + // CHECK: bb3: { // CHECK: {{_.*}} = (([[controlflow]] as Break).0: std::result::Result); // CHECK: _0 = Result::::Err( - // CHECK: goto -> bb5; - // CHECK: bb5: { + // CHECK: goto -> bb4; + // CHECK: bb4: { // CHECK: return; + // CHECK: bb5: { + // CHECK: goto -> bb1; // CHECK: bb6: { // CHECK: {{_.*}} = move (([[x]] as Err).0: i32); // CHECK: [[controlflow]] = ControlFlow::, i32>::Break( @@ -74,9 +74,9 @@ fn identity(x: Result) -> Result { // CHECK: bb8: { // CHECK: {{_.*}} = move (([[x]] as Ok).0: i32); // CHECK: [[controlflow]] = ControlFlow::, i32>::Continue( - // CHECK: goto -> bb1; + // CHECK: goto -> bb5; // CHECK: bb9: { - // CHECK: goto -> bb4; + // CHECK: goto -> bb3; Ok(x?) } 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 194478560e92..000000000000 --- 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, _2: &Option) -> () { - debug o => _1; - debug p => _2; - let mut _0: (); - let mut _4: *const std::option::Option; - let mut _6: *const std::option::Option; - 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::(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::(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 194478560e92..000000000000 --- 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, _2: &Option) -> () { - debug o => _1; - debug p => _2; - let mut _0: (); - let mut _4: *const std::option::Option; - let mut _6: *const std::option::Option; - 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::(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::(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 913605cc2b2b..cba2bc18d867 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, p: &Option) { - // 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/separate_const_switch.identity.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff index fe4b33001fc4..d287b20c4ead 100644 --- a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff +++ b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff @@ -52,29 +52,21 @@ StorageLive(_10); StorageLive(_11); _9 = discriminant(_1); - switchInt(move _9) -> [0: bb6, 1: bb5, otherwise: bb3]; + switchInt(move _9) -> [0: bb6, 1: bb5, otherwise: bb2]; } bb1: { - StorageDead(_11); - StorageDead(_10); - StorageDead(_9); - _5 = discriminant(_3); - switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; - } - - bb2: { _8 = ((_3 as Continue).0: i32); _0 = Result::::Ok(_8); StorageDead(_3); return; } - bb3: { + bb2: { unreachable; } - bb4: { + bb3: { _6 = ((_3 as Break).0: std::result::Result); _13 = ((_6 as Err).0: i32); _0 = Result::::Err(move _13); @@ -82,19 +74,27 @@ return; } + bb4: { + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + _5 = discriminant(_3); + switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; + } + bb5: { _11 = ((_1 as Err).0: i32); StorageLive(_12); _12 = Result::::Err(move _11); _3 = ControlFlow::, i32>::Break(move _12); StorageDead(_12); - goto -> bb1; + goto -> bb4; } bb6: { _10 = ((_1 as Ok).0: i32); _3 = ControlFlow::, i32>::Continue(move _10); - goto -> bb1; + goto -> bb4; } } diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs index 1a94649163b2..9be0fdccebec 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/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index db1605ff220a..b52bb0688c1b 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -80,7 +80,7 @@ set-window-size: (851, 600) // Check the size and count in tabs assert-text: ("#search-tabs > button:nth-child(1) > .count", " (25) ") -assert-text: ("#search-tabs > button:nth-child(2) > .count", " (5)  ") +assert-text: ("#search-tabs > button:nth-child(2) > .count", " (6)  ") assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ") store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth}) assert-property: ("#search-tabs > button:nth-child(2)", {"offsetWidth": |buttonWidth|}) diff --git a/tests/rustdoc-js-std/iterator-type-signatures.js b/tests/rustdoc-js-std/iterator-type-signatures.js new file mode 100644 index 000000000000..c18ffc1651c1 --- /dev/null +++ b/tests/rustdoc-js-std/iterator-type-signatures.js @@ -0,0 +1,29 @@ +// ignore-order + +const FILTER_CRATE = "std"; + +const EXPECTED = [ + { + 'query': 'iterator -> option', + 'others': [ + { 'path': 'std::iter::Iterator', 'name': 'max' }, + { 'path': 'std::iter::Iterator', 'name': 'min' }, + { 'path': 'std::iter::Iterator', 'name': 'last' }, + { 'path': 'std::iter::Iterator', 'name': 'next' }, + ], + }, + { + 'query': 'iterator, usize -> option', + 'others': [ + { 'path': 'std::iter::Iterator', 'name': 'nth' }, + ], + }, + { + // Something should be done so that intoiterator is considered a match + // for plain iterator. + 'query': 'iterator, intoiterator -> ordering', + 'others': [ + { 'path': 'std::iter::Iterator', 'name': 'cmp' }, + ], + }, +]; diff --git a/tests/rustdoc-js-std/parser-bindings.js b/tests/rustdoc-js-std/parser-bindings.js new file mode 100644 index 000000000000..faf880c8116c --- /dev/null +++ b/tests/rustdoc-js-std/parser-bindings.js @@ -0,0 +1,245 @@ +const PARSED = [ + { + query: 'A', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + bindings: [ + [ + 'b', + [ + { + name: "c", + fullPath: ["c"], + pathWithoutLast: [], + pathLast: "c", + generics: [], + typeFilter: -1, + }, + ] + ], + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: 'A', + returned: [], + userQuery: 'a', + error: null, + }, + { + query: 'A', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + bindings: [ + [ + 'b', + [{ + name: "c", + fullPath: ["c"], + pathWithoutLast: [], + pathLast: "c", + generics: [], + typeFilter: -1, + }] + ], + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: 'A', + returned: [], + userQuery: 'a', + error: null, + }, + { + query: 'A', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + bindings: [ + [ + 'b', + [{ + name: "never", + fullPath: ["never"], + pathWithoutLast: [], + pathLast: "never", + generics: [], + typeFilter: 15, + }] + ], + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: 'A', + returned: [], + userQuery: 'a', + error: null, + }, + { + query: 'A', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + bindings: [ + [ + 'b', + [{ + name: "[]", + fullPath: ["[]"], + pathWithoutLast: [], + pathLast: "[]", + generics: [], + typeFilter: 15, + }] + ], + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: 'A', + returned: [], + userQuery: 'a', + error: null, + }, + { + query: 'A', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + bindings: [ + [ + 'b', + [{ + name: "[]", + fullPath: ["[]"], + pathWithoutLast: [], + pathLast: "[]", + generics: [ + { + name: "never", + fullPath: ["never"], + pathWithoutLast: [], + pathLast: "never", + generics: [], + typeFilter: 15, + }, + ], + typeFilter: 15, + }] + ], + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: 'A', + returned: [], + userQuery: 'a', + error: null, + }, + { + query: 'A', + elems: [], + foundElems: 0, + original: 'A', + returned: [], + userQuery: 'a', + error: "Cannot write `=` twice in a binding", + }, + { + query: 'A', + elems: [], + foundElems: 0, + original: 'A', + returned: [], + userQuery: 'a', + error: "Unexpected `>` after `=`", + }, + { + query: 'B=C', + elems: [], + foundElems: 0, + original: 'B=C', + returned: [], + userQuery: 'b=c', + error: "Type parameter `=` must be within generics list", + }, + { + query: '[B=C]', + elems: [], + foundElems: 0, + original: '[B=C]', + returned: [], + userQuery: '[b=c]', + error: "Type parameter `=` cannot be within slice `[]`", + }, + { + query: 'A=C>', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + bindings: [ + [ + 'b', + [ + { + name: "c", + fullPath: ["c"], + pathWithoutLast: [], + pathLast: "c", + generics: [], + typeFilter: -1, + }, + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + ], + ], + ], + typeFilter: -1, + }, + ], + foundElems: 1, + original: 'A=C>', + returned: [], + userQuery: 'a=c>', + error: null, + }, +]; diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index b32bfea5439a..ab8d72bf71b1 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -303,7 +303,7 @@ const PARSED = [ original: '->a<>b', returned: [], userQuery: '->a<>b', - error: 'Expected `,` after `>`, found `b`', + error: 'Expected `,` or `=` after `>`, found `b`', }, { query: "a<->", diff --git a/tests/rustdoc-js/assoc-type-backtrack.js b/tests/rustdoc-js/assoc-type-backtrack.js new file mode 100644 index 000000000000..493e1a9910d1 --- /dev/null +++ b/tests/rustdoc-js/assoc-type-backtrack.js @@ -0,0 +1,163 @@ +// exact-check + +const EXPECTED = [ + { + 'query': 'mytrait, mytrait2 -> T', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' }, + { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' }, + ], + }, + { + 'query': 'mytrait, mytrait2 -> T', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' }, + { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' }, + ], + }, + { + 'query': 'mytrait, mytrait2 -> T', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' }, + { 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' }, + ], + }, + { + 'query': 'mytrait, mytrait2 -> T', + 'correction': null, + 'others': [], + }, + { + 'query': 'mytrait, mytrait2 -> T', + 'correction': null, + 'others': [], + }, + { + 'query': 'mytrait -> Option', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'next' }, + ], + }, + { + 'query': 'mytrait -> Option', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyTrait', 'name': 'next' }, + ], + }, + { + 'query': 'mytrait -> Option', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' }, + ], + }, + { + 'query': 'mytrait -> Option', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' }, + ], + }, + // The first two define the base case. + { + 'query': 'myintofuture> -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' }, + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + { + 'query': 'myintofuture>, myintofuture> -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + // Unboxings of the one-argument case. + { + 'query': 'myfuture -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' }, + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + { + 'query': 'myintofuture> -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' }, + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + // Invalid unboxing of the one-argument case. + // If you unbox one of the myfutures, you need to unbox both of them. + { + 'query': 'myintofuture -> myfuture', + 'correction': null, + 'others': [], + }, + // Unboxings of the two-argument case. + { + 'query': 'myintofuture, myintofuture -> t', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + { + 'query': 'myintofuture, myintofuture -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + { + 'query': 'myintofuture, myintofuture -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + { + 'query': 'myfuture, myfuture -> myfuture', + 'correction': null, + 'others': [ + { 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' }, + ], + }, + // Invalid unboxings of the two-argument case. + // If you unbox one of the myfutures, you need to unbox all of them. + { + 'query': 'myintofuture, myintofuture> -> myfuture', + 'correction': null, + 'others': [], + }, + { + 'query': 'myintofuture>, myintofuture -> myfuture', + 'correction': null, + 'others': [], + }, + { + 'query': 'myintofuture>, myintofuture> -> t', + 'correction': null, + 'others': [], + }, + // different generics don't match up either + { + 'query': 'myintofuture>, myintofuture> -> myfuture', + 'correction': null, + 'others': [], + }, + { + 'query': 'myintofuture -> myfuture', + 'correction': null, + 'others': [], + }, +]; diff --git a/tests/rustdoc-js/assoc-type-backtrack.rs b/tests/rustdoc-js/assoc-type-backtrack.rs new file mode 100644 index 000000000000..c3cdd78c6e1c --- /dev/null +++ b/tests/rustdoc-js/assoc-type-backtrack.rs @@ -0,0 +1,38 @@ +pub trait MyTrait2 { + type Output; +} + +pub trait MyTrait { + type Item; + fn next(&mut self) -> Option; + fn fold(self, init: B, f: F) -> B where + Self: Sized, + F: MyTrait2<(B, Self::Item), Output=B>; +} + +pub struct Cloned(I); + +impl<'a, T, I> MyTrait for Cloned where + T: 'a + Clone, + I: MyTrait +{ + type Item = T; + fn next(&mut self) -> Option { loop {} } + fn fold(self, init: B, f: F) -> B where + Self: Sized, + F: MyTrait2<(B, Self::Item), Output=B> + { + loop {} + } +} + +pub trait MyFuture { + type Output; +} + +pub trait MyIntoFuture { + type Output; + type Fut: MyFuture; + fn into_future(self) -> Self::Fut; + fn into_future_2(self, other: Self) -> Self::Fut; +} diff --git a/tests/rustdoc-js/assoc-type.js b/tests/rustdoc-js/assoc-type.js new file mode 100644 index 000000000000..cc3afaa17c07 --- /dev/null +++ b/tests/rustdoc-js/assoc-type.js @@ -0,0 +1,45 @@ +// exact-check + +const EXPECTED = [ + // if I just use generics, then the generics version + // and the type binding version both show up + { + 'query': 'iterator -> u32', + 'correction': null, + 'others': [ + { 'path': 'assoc_type', 'name': 'my_fn' }, + { 'path': 'assoc_type::my', 'name': 'other_fn' }, + ], + }, + { + 'query': 'iterator', + 'correction': null, + 'in_args': [ + { 'path': 'assoc_type', 'name': 'my_fn' }, + { 'path': 'assoc_type::my', 'name': 'other_fn' }, + ], + }, + // if I write an explicit binding, only it shows up + { + 'query': 'iterator -> u32', + 'correction': null, + 'others': [ + { 'path': 'assoc_type', 'name': 'my_fn' }, + ], + }, + // case insensitivity + { + 'query': 'iterator -> u32', + 'correction': null, + 'others': [ + { 'path': 'assoc_type', 'name': 'my_fn' }, + ], + }, + // wrong binding name, no result + { + 'query': 'iterator -> u32', + 'correction': null, + 'in_args': [], + 'others': [], + }, +]; diff --git a/tests/rustdoc-js/assoc-type.rs b/tests/rustdoc-js/assoc-type.rs new file mode 100644 index 000000000000..e12e73cb546d --- /dev/null +++ b/tests/rustdoc-js/assoc-type.rs @@ -0,0 +1,12 @@ +pub fn my_fn>(_x: X) -> u32 { + 3 +} + +pub struct Something; + +pub mod my { + pub trait Iterator {} + pub fn other_fn>(_: X) -> u32 { + 3 + } +} diff --git a/tests/rustdoc-js/gat.js b/tests/rustdoc-js/gat.js new file mode 100644 index 000000000000..7cb6a85d135f --- /dev/null +++ b/tests/rustdoc-js/gat.js @@ -0,0 +1,57 @@ +// exact-check + +const EXPECTED = [ + { + 'query': 'foo=u8> -> u32', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'gat', 'name': 'sample' }, + ], + }, + { + 'query': 'foo=u8> -> !', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'gat', 'name': 'synergy' }, + ], + }, + { + 'query': 'foo=u8>', + 'correction': null, + 'in_args': [ + { 'path': 'gat', 'name': 'sample' }, + { 'path': 'gat', 'name': 'synergy' }, + ], + }, + { + 'query': 'foo=u32>', + 'correction': null, + 'in_args': [ + { 'path': 'gat', 'name': 'consider' }, + ], + }, + { + // This one is arguably a bug, because the way rustdoc + // stores GATs in the search index is sloppy, but it's + // precise enough to match most of the samples in the + // GAT initiative repo + 'query': 'foo=u8>', + 'correction': null, + 'in_args': [ + { 'path': 'gat', 'name': 'consider' }, + ], + }, + { + // This one is arguably a bug, because the way rustdoc + // stores GATs in the search index is sloppy, but it's + // precise enough to match most of the samples in the + // GAT initiative repo + 'query': 'foo=T>', + 'correction': null, + 'in_args': [ + { 'path': 'gat', 'name': 'integrate' }, + ], + }, +]; diff --git a/tests/rustdoc-js/gat.rs b/tests/rustdoc-js/gat.rs new file mode 100644 index 000000000000..b4861cc683ff --- /dev/null +++ b/tests/rustdoc-js/gat.rs @@ -0,0 +1,8 @@ +pub trait Foo { + type Assoc; +} + +pub fn sample = u8>>(_: X) -> u32 { loop {} } +pub fn synergy(_: impl Foo = u8>) -> ! { loop {} } +pub fn consider(_: impl Foo = u32>) -> bool { loop {} } +pub fn integrate(_: impl Foo = T>) -> T { loop {} } diff --git a/tests/rustdoc-js/generics2.js b/tests/rustdoc-js/generics2.js new file mode 100644 index 000000000000..f08704349a44 --- /dev/null +++ b/tests/rustdoc-js/generics2.js @@ -0,0 +1,22 @@ +// exact-check + +const EXPECTED = [ + { + 'query': 'outside, outside -> outside', + 'others': [], + }, + { + 'query': 'outside, outside -> outside', + 'others': [], + }, + { + 'query': 'outside, outside -> outside', + 'others': [], + }, + { + 'query': 'outside, outside -> outside', + 'others': [ + {"path": "generics2", "name": "should_match_3"} + ], + }, +]; diff --git a/tests/rustdoc-js/generics2.rs b/tests/rustdoc-js/generics2.rs new file mode 100644 index 000000000000..1177ade68316 --- /dev/null +++ b/tests/rustdoc-js/generics2.rs @@ -0,0 +1,13 @@ +pub struct Outside(T); + +pub fn no_match(a: Outside, b: Outside) -> (Outside, Outside) { + unimplemented!(); +} + +pub fn no_match_2(a: Outside, b: Outside) -> (Outside, Outside) { + unimplemented!(); +} + +pub fn should_match_3(a: Outside, b: Outside) -> (Outside, Outside) { + unimplemented!(); +} diff --git a/tests/rustdoc-js/never-search.js b/tests/rustdoc-js/never-search.js index ed24d693133b..9f18370c2f51 100644 --- a/tests/rustdoc-js/never-search.js +++ b/tests/rustdoc-js/never-search.js @@ -43,4 +43,14 @@ const EXPECTED = [ { 'path': 'never_search', 'name': 'box_uninteresting' }, ], }, + { + 'query': 'box', + 'in_args': [], + 'returned': [], + }, + { + 'query': 'box', + 'in_args': [], + 'returned': [], + }, ]; diff --git a/tests/rustdoc-js/trait-methods.js b/tests/rustdoc-js/trait-methods.js new file mode 100644 index 000000000000..dafad5e43784 --- /dev/null +++ b/tests/rustdoc-js/trait-methods.js @@ -0,0 +1,12 @@ +// exact-check + +const EXPECTED = [ + { + 'query': 'mytrait -> option', + 'correction': null, + 'in_args': [], + 'others': [ + { 'path': 'trait_methods::MyTrait', 'name': 'next' }, + ], + }, +]; diff --git a/tests/rustdoc-js/trait-methods.rs b/tests/rustdoc-js/trait-methods.rs new file mode 100644 index 000000000000..c88f5edfd55b --- /dev/null +++ b/tests/rustdoc-js/trait-methods.rs @@ -0,0 +1,4 @@ +pub trait MyTrait { + type Item; + fn next(&mut self) -> Option; +} diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index a340877752d8..976dfee774b2 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(()) } @@ -59,10 +59,13 @@ fn test_body(body: mir::Body) { for term in body.blocks.iter().map(|bb| &bb.terminator) { match &term.kind { Call { func, .. } => { - let TyKind::RigidTy(ty) = func.ty(body.locals()).kind() else { unreachable!() }; + let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().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 +108,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 ed6b786f5e1d..4164f041022d 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,14 @@ 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 TyKind::RigidTy(ty) = func.ty(&body.locals()).unwrap().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 +174,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 index 9c649a2effc7..299307635913 100644 --- a/tests/ui-fulldeps/stable-mir/projections.rs +++ b/tests/ui-fulldeps/stable-mir/projections.rs @@ -19,11 +19,11 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_hir::def::DefKind; 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::ty::{RigidTy, TyKind, UintTy}; +use stable_mir::ItemKind; use std::assert_matches::assert_matches; use std::io::Write; use std::ops::ControlFlow; @@ -33,13 +33,13 @@ 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, (DefKind::Fn, "projections")).unwrap().body(); + 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 }, + place @ 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 @@ -48,10 +48,14 @@ fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { 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)) - ), + [ProjectionElem::Deref, ProjectionElem::Field(2, ty)] => { + assert_matches!( + ty.kind(), + TyKind::RigidTy(RigidTy::Uint(stable_mir::ty::UintTy::U8)) + ); + let ty = place.ty(body.locals()).unwrap(); + assert_matches!(ty.kind().rigid(), Some(RigidTy::Ref(..))); + }, other => panic!( "Unable to match against expected rvalue projection. Expected the projection \ for `s.c`, which is a Deref and u8 Field. Got: {:?}", @@ -69,7 +73,7 @@ fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { // 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 }, + place @ stable_mir::mir::Place { local: _, projection: local_proj }, Rvalue::Use(stable_mir::mir::Operand::Copy(stable_mir::mir::Place { local: _, projection: r_proj, @@ -80,6 +84,8 @@ fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { // wildcards. assert!(local_proj.is_empty()); assert_matches!(r_proj[..], [ProjectionElem::Deref, ProjectionElem::Index(_)]); + let ty = place.ty(body.locals()).unwrap(); + assert_matches!(ty.kind().rigid(), Some(RigidTy::Uint(UintTy::U8))); } other => panic!( "Unable to match against expected Assign statement with a Use rvalue. Expected the \ @@ -131,10 +137,10 @@ fn test_place_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { // Use internal API to find a function in a crate. fn get_item<'a>( items: &'a stable_mir::CrateItems, - item: (DefKind, &str), + item: (ItemKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { - crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1 + crate_item.kind() == item.0 && crate_item.name() == item.1 }) } diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs index de5148bb5f42..027b0e7d9e85 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); @@ -92,7 +92,8 @@ impl<'a> mir::MirVisitor for TestVisitor<'a> { fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) { if let mir::TerminatorKind::Call { func, .. } = &term.kind { - let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).kind() else { unreachable! + let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).unwrap().kind() else { + unreachable! () }; let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() }; self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap()); diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 0cdf229711ad..d5f053130783 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -1,5 +1,14 @@ // check-pass // revisions: host +// revisions: i686 +//[i686] compile-flags: --target i686-unknown-linux-gnu +//[i686] needs-llvm-components: x86 +// revisions: x86-64 +//[x86-64] compile-flags: --target x86_64-unknown-linux-gnu +//[x86-64] needs-llvm-components: x86 +// revisions: x86-64-win +//[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc +//[x86-64-win] needs-llvm-components: x86 // revisions: arm //[arm] compile-flags: --target arm-unknown-linux-gnueabi //[arm] needs-llvm-components: arm @@ -37,9 +46,23 @@ // revisions: wasi //[wasi] compile-flags: --target wasm32-wasi //[wasi] needs-llvm-components: webassembly -// revisions: nvptx64 -//[nvptx64] compile-flags: --target nvptx64-nvidia-cuda -//[nvptx64] needs-llvm-components: nvptx +// revisions: bpf +//[bpf] compile-flags: --target bpfeb-unknown-none +//[bpf] needs-llvm-components: bpf +// revisions: m68k +//[m68k] compile-flags: --target m68k-unknown-linux-gnu +//[m68k] needs-llvm-components: m68k +// FIXME: disabled on nvptx64 since the target ABI fails the sanity check +// see https://github.com/rust-lang/rust/issues/117480 +/* revisions: nvptx64 + [nvptx64] compile-flags: --target nvptx64-nvidia-cuda + [nvptx64] needs-llvm-components: nvptx +*/ +// FIXME: disabled since it fails on CI saying the csky component is missing +/* revisions: csky + [csky] compile-flags: --target csky-unknown-linux-gnuabiv2 + [csky] needs-llvm-components: csky +*/ #![feature(rustc_attrs, unsized_fn_params, transparent_unions)] #![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)] #![allow(unused, improper_ctypes_definitions, internal_features)] @@ -231,8 +254,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); test_abi_compatible!(ref_mut, &i32, &mut i32); @@ -241,14 +263,15 @@ test_abi_compatible!(box_ptr, Box, *const i32); test_abi_compatible!(nonnull_ptr, NonNull, *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, *mut i32); test_abi_compatible!(arc, Arc, *mut i32); @@ -324,6 +347,7 @@ mod unsized_ { use super::*; test_transparent_unsized!(str_, str); test_transparent_unsized!(slice, [u8]); + test_transparent_unsized!(slice_with_prefix, (usize, [u8])); test_transparent_unsized!(dyn_trait, dyn Any); } diff --git a/tests/ui/abi/issue-94223.rs b/tests/ui/abi/issue-94223.rs deleted file mode 100644 index 79d6b94031bc..000000000000 --- a/tests/ui/abi/issue-94223.rs +++ /dev/null @@ -1,8 +0,0 @@ -// check-pass -#![allow(improper_ctypes_definitions)] -#![crate_type = "lib"] - -// Check that computing the fn abi for `bad`, with a external ABI fn ptr that is not FFI-safe, does -// not ICE. - -pub fn bad(f: extern "C" fn([u8])) {} diff --git a/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs b/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs new file mode 100644 index 000000000000..a32cc6500f82 --- /dev/null +++ b/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs @@ -0,0 +1,30 @@ +// check-pass +#![allow(improper_ctypes_definitions)] +#![feature(unsized_tuple_coercion)] +#![feature(unsized_fn_params)] +#![crate_type = "lib"] + +// Check that computing the fn abi for `bad`, with a external ABI fn ptr that is not FFI-safe, does +// not ICE. + +pub fn bad(f: extern "C" fn([u8])) {} + +// While these get accepted, they should also not ICE. +// (If we ever reject them, remove them from this test to ensure the `bad` above +// is still tested. Do *not* make this a check/build-fail test.) + +pub extern "C" fn declare_bad(_x: str) {} + +#[no_mangle] +pub extern "system" fn declare_more_bad(f: dyn FnOnce()) { +} + +fn make_bad() -> extern "C" fn(([u8],)) { + todo!() +} + +pub fn call_bad() { + let f = make_bad(); + let slice: Box<([u8],)> = Box::new(([1; 8],)); + f(*slice); +} diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index a952ea077932..1862177005f9 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` 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/associated-types/missing-associated-types.stderr b/tests/ui/associated-types/missing-associated-types.stderr index e9669afe8c70..0636667ea1fd 100644 --- a/tests/ui/associated-types/missing-associated-types.stderr +++ b/tests/ui/associated-types/missing-associated-types.stderr @@ -49,7 +49,7 @@ LL | type B; LL | type Bar = dyn Add + Sub + X + Z; | ^^^^^^^^ ^^^^^^^^ ^^^^^^ ^^^^^^ associated types `A`, `B`, `Output` must be specified | | | | - | | | associated types `Output` (from trait `Mul`), `Output` (from trait `Div`) must be specified + | | | associated types `Output` (from trait `Div`), `Output` (from trait `Mul`) must be specified | | associated type `Output` must be specified | associated type `Output` must be specified | @@ -119,7 +119,7 @@ error[E0191]: the value of the associated types `Output` in `Div`, `Output` in ` --> $DIR/missing-associated-types.rs:24:21 | LL | type Bal = dyn X; - | ^^^^^^ associated types `Output` (from trait `Mul`), `Output` (from trait `Div`) must be specified + | ^^^^^^ associated types `Output` (from trait `Div`), `Output` (from trait `Mul`) must be specified | = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types diff --git a/tests/ui/async-await/issue-60709.rs b/tests/ui/async-await/issue-60709.rs index 2cda40e9e11b..c206f01b98f7 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 4919e0a051dc..4a3195174df4 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>) { fn await_on_apit(x: impl Future) { 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 409eb179e83a..dd863ca541c9 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>` - --> $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` - --> $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 9852e8fc918f..10e245796ee3 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>) { fn await_on_apit(x: impl Future) { 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 ef3334381b71..0ed256b059ff 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>` - --> $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` - --> $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 e6d17def1477..5f7e357eeb2a 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/borrowck/generic_const_early_param.rs b/tests/ui/borrowck/generic_const_early_param.rs new file mode 100644 index 000000000000..f601e45d21fe --- /dev/null +++ b/tests/ui/borrowck/generic_const_early_param.rs @@ -0,0 +1,16 @@ +#![feature(generic_const_exprs)] +//~^ WARN the feature `generic_const_exprs` is incomplete + +struct DataWrapper<'static> { + //~^ ERROR invalid lifetime parameter name: `'static` + data: &'a [u8; Self::SIZE], + //~^ ERROR use of undeclared lifetime name `'a` + //~^^ ERROR lifetime may not live long enough +} + +impl DataWrapper<'a> { + //~^ ERROR undeclared lifetime + const SIZE: usize = 14; +} + +fn main(){} diff --git a/tests/ui/borrowck/generic_const_early_param.stderr b/tests/ui/borrowck/generic_const_early_param.stderr new file mode 100644 index 000000000000..a71ab09396e7 --- /dev/null +++ b/tests/ui/borrowck/generic_const_early_param.stderr @@ -0,0 +1,42 @@ +error[E0262]: invalid lifetime parameter name: `'static` + --> $DIR/generic_const_early_param.rs:4:20 + | +LL | struct DataWrapper<'static> { + | ^^^^^^^ 'static is a reserved lifetime name + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/generic_const_early_param.rs:6:12 + | +LL | struct DataWrapper<'static> { + | - help: consider introducing lifetime `'a` here: `'a,` +LL | +LL | data: &'a [u8; Self::SIZE], + | ^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/generic_const_early_param.rs:11:18 + | +LL | impl DataWrapper<'a> { + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `<'a>` + +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_const_early_param.rs:1:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: lifetime may not live long enough + --> $DIR/generic_const_early_param.rs:6:20 + | +LL | data: &'a [u8; Self::SIZE], + | ^^^^^^^^^^ requires that `'_` must outlive `'static` + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0261, E0262. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr index 9f9d4bd8d6c0..b3bf2f924fc3 100644 --- a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr +++ b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr @@ -77,6 +77,8 @@ LL | reg.register_univ(Box::new(CapturePass::new(®.sess_mut))); | | | immutable borrow occurs here | | cast requires that `reg.sess_mut` is borrowed for `'a` | mutable borrow occurs here + | + = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as immutable --> $DIR/two-phase-surprise-no-conflict.rs:144:5 @@ -119,6 +121,8 @@ LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut))); | | | first mutable borrow occurs here | | cast requires that `reg.sess_mut` is borrowed for `'a` | second mutable borrow occurs here + | + = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time --> $DIR/two-phase-surprise-no-conflict.rs:158:53 diff --git a/tests/ui/cfg/conditional-compile-arch.rs b/tests/ui/cfg/conditional-compile-arch.rs index e59e06f801b7..c6ecf4807364 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/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index 53ccc0f4d317..12a055d02a7b 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `unknown_key` - --> $DIR/exhaustive-names-values.rs:12:7 + --> $DIR/exhaustive-names-values.rs:11:7 | LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[cfg(unknown_key = "value")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: `value` - --> $DIR/exhaustive-names-values.rs:16:7 + --> $DIR/exhaustive-names-values.rs:15:7 | LL | #[cfg(test = "value")] | ^^^^---------- @@ -17,9 +17,5 @@ LL | #[cfg(test = "value")] | = note: no expected value for `test` -warning: unexpected `empty_cfg` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr index 5e8b74054ceb..12a055d02a7b 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_names_values.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `unknown_key` - --> $DIR/exhaustive-names-values.rs:12:7 + --> $DIR/exhaustive-names-values.rs:11:7 | LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[cfg(unknown_key = "value")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: `value` - --> $DIR/exhaustive-names-values.rs:16:7 + --> $DIR/exhaustive-names-values.rs:15:7 | LL | #[cfg(test = "value")] | ^^^^---------- @@ -17,9 +17,5 @@ LL | #[cfg(test = "value")] | = note: no expected value for `test` -warning: unexpected `empty_names_values` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 7705a665eb77..d71ca0958948 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `unknown_key` - --> $DIR/exhaustive-names-values.rs:12:7 + --> $DIR/exhaustive-names-values.rs:11:7 | LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[cfg(unknown_key = "value")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: `value` - --> $DIR/exhaustive-names-values.rs:16:7 + --> $DIR/exhaustive-names-values.rs:15:7 | LL | #[cfg(test = "value")] | ^^^^---------- @@ -18,16 +18,12 @@ LL | #[cfg(test = "value")] = note: no expected value for `test` warning: unexpected `cfg` condition value: `unk` - --> $DIR/exhaustive-names-values.rs:20:7 + --> $DIR/exhaustive-names-values.rs:19:7 | LL | #[cfg(feature = "unk")] | ^^^^^^^^^^^^^^^ | = note: expected values for `feature` are: `std` -warning: unexpected condition value `` for condition name `feature` - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected values - -warning: 4 warnings emitted +warning: 3 warnings emitted diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index f0224a2e33c7..d71ca0958948 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `unknown_key` - --> $DIR/exhaustive-names-values.rs:12:7 + --> $DIR/exhaustive-names-values.rs:11:7 | LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[cfg(unknown_key = "value")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: `value` - --> $DIR/exhaustive-names-values.rs:16:7 + --> $DIR/exhaustive-names-values.rs:15:7 | LL | #[cfg(test = "value")] | ^^^^---------- @@ -18,16 +18,12 @@ LL | #[cfg(test = "value")] = note: no expected value for `test` warning: unexpected `cfg` condition value: `unk` - --> $DIR/exhaustive-names-values.rs:20:7 + --> $DIR/exhaustive-names-values.rs:19:7 | LL | #[cfg(feature = "unk")] | ^^^^^^^^^^^^^^^ | = note: expected values for `feature` are: `std` -warning: unexpected `full` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - -warning: 4 warnings emitted +warning: 3 warnings emitted diff --git a/tests/ui/check-cfg/exhaustive-names-values.rs b/tests/ui/check-cfg/exhaustive-names-values.rs index f553d93cae2a..ef1e458b2498 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.rs +++ b/tests/ui/check-cfg/exhaustive-names-values.rs @@ -1,5 +1,4 @@ -// Check warning for unexpected cfg in the code and in the CLI -// arguments (here the revision cfg). +// Check warning for unexpected cfg in the code. // // check-pass // revisions: empty_names_values empty_cfg feature full diff --git a/tests/ui/check-cfg/exhaustive-names.empty_names.stderr b/tests/ui/check-cfg/exhaustive-names.empty_names.stderr index 6190ff71464c..6bc7845c8328 100644 --- a/tests/ui/check-cfg/exhaustive-names.empty_names.stderr +++ b/tests/ui/check-cfg/exhaustive-names.empty_names.stderr @@ -7,9 +7,5 @@ LL | #[cfg(unknown_key = "value")] = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows` = note: `#[warn(unexpected_cfgs)]` on by default -warning: unexpected `empty_names` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - -warning: 2 warnings emitted +warning: 1 warning emitted diff --git a/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr b/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr index f338434cd299..6bc7845c8328 100644 --- a/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr +++ b/tests/ui/check-cfg/exhaustive-names.exhaustive_names.stderr @@ -7,9 +7,5 @@ LL | #[cfg(unknown_key = "value")] = help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows` = note: `#[warn(unexpected_cfgs)]` on by default -warning: unexpected `exhaustive_names` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - -warning: 2 warnings emitted +warning: 1 warning emitted diff --git a/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr index 999b27028497..77ddc35100ae 100644 --- a/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr @@ -9,9 +9,5 @@ LL | #[cfg(test = "value")] = note: no expected value for `test` = note: `#[warn(unexpected_cfgs)]` on by default -warning: unexpected `empty_cfg` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - -warning: 2 warnings emitted +warning: 1 warning emitted diff --git a/tests/ui/check-cfg/mix.cfg.stderr b/tests/ui/check-cfg/mix.cfg.stderr index daa200440cc9..21c0c7da1dd3 100644 --- a/tests/ui/check-cfg/mix.cfg.stderr +++ b/tests/ui/check-cfg/mix.cfg.stderr @@ -38,14 +38,6 @@ LL | #[cfg_attr(uu, test)] | = help: expected names are: `cfg`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `names_values`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows` -warning: unexpected condition value `bar` for condition name `feature` - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected values - -warning: unexpected `unknown_name` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - warning: unexpected `cfg` condition name: `widnows` --> $DIR/mix.rs:43:10 | @@ -188,5 +180,5 @@ LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra")); | = note: expected values for `feature` are: `foo` -warning: 28 warnings emitted +warning: 26 warnings emitted diff --git a/tests/ui/check-cfg/mix.names_values.stderr b/tests/ui/check-cfg/mix.names_values.stderr index daa200440cc9..21c0c7da1dd3 100644 --- a/tests/ui/check-cfg/mix.names_values.stderr +++ b/tests/ui/check-cfg/mix.names_values.stderr @@ -38,14 +38,6 @@ LL | #[cfg_attr(uu, test)] | = help: expected names are: `cfg`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `names_values`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows` -warning: unexpected condition value `bar` for condition name `feature` - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected values - -warning: unexpected `unknown_name` as condition name - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected names - warning: unexpected `cfg` condition name: `widnows` --> $DIR/mix.rs:43:10 | @@ -188,5 +180,5 @@ LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra")); | = note: expected values for `feature` are: `foo` -warning: 28 warnings emitted +warning: 26 warnings emitted diff --git a/tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr b/tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr index 2ed7f9005573..2855aa75966b 100644 --- a/tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr +++ b/tests/ui/check-cfg/unexpected-cfg-value.cfg.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition value: `sedre` - --> $DIR/unexpected-cfg-value.rs:11:7 + --> $DIR/unexpected-cfg-value.rs:9:7 | LL | #[cfg(feature = "sedre")] | ^^^^^^^^^^------- @@ -10,16 +10,12 @@ LL | #[cfg(feature = "sedre")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: `rand` - --> $DIR/unexpected-cfg-value.rs:18:7 + --> $DIR/unexpected-cfg-value.rs:16:7 | LL | #[cfg(feature = "rand")] | ^^^^^^^^^^^^^^^^ | = note: expected values for `feature` are: `full`, `serde` -warning: unexpected condition value `rand` for condition name `feature` - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected values - -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/unexpected-cfg-value.rs b/tests/ui/check-cfg/unexpected-cfg-value.rs index a84458071de8..1b8ead956bef 100644 --- a/tests/ui/check-cfg/unexpected-cfg-value.rs +++ b/tests/ui/check-cfg/unexpected-cfg-value.rs @@ -1,10 +1,8 @@ -// Check warning for invalid configuration value in the code and -// in the cli +// Check for unexpected configuration value in the code. // // check-pass // revisions: values cfg -// compile-flags: --cfg=feature="rand" -Z unstable-options -// compile-flags: --check-cfg=cfg(values,cfg) +// compile-flags: -Z unstable-options // [values]compile-flags: --check-cfg=values(feature,"serde","full") // [cfg]compile-flags: --check-cfg=cfg(feature,values("serde","full")) diff --git a/tests/ui/check-cfg/unexpected-cfg-value.values.stderr b/tests/ui/check-cfg/unexpected-cfg-value.values.stderr index 2ed7f9005573..2855aa75966b 100644 --- a/tests/ui/check-cfg/unexpected-cfg-value.values.stderr +++ b/tests/ui/check-cfg/unexpected-cfg-value.values.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition value: `sedre` - --> $DIR/unexpected-cfg-value.rs:11:7 + --> $DIR/unexpected-cfg-value.rs:9:7 | LL | #[cfg(feature = "sedre")] | ^^^^^^^^^^------- @@ -10,16 +10,12 @@ LL | #[cfg(feature = "sedre")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: `rand` - --> $DIR/unexpected-cfg-value.rs:18:7 + --> $DIR/unexpected-cfg-value.rs:16:7 | LL | #[cfg(feature = "rand")] | ^^^^^^^^^^^^^^^^ | = note: expected values for `feature` are: `full`, `serde` -warning: unexpected condition value `rand` for condition name `feature` - | - = help: was set with `--cfg` but isn't in the `--check-cfg` expected values - -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs b/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs index 0e16d12a1811..531977d6dac2 100644 --- a/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs +++ b/tests/ui/coherence/coherence-negative-outlives-lifetimes.rs @@ -1,5 +1,9 @@ // revisions: stock with_negative_coherence + //[with_negative_coherence] known-bug: unknown +// Ideally this would work, but we don't use `&'a T` to imply that `T: 'a` +// which is required for `&'a T: !MyPredicate` to hold. This is similar to the +// test `negative-coherence-placeholder-region-constraints-on-unification.explicit.stderr` #![feature(negative_impls)] #![cfg_attr(with_negative_coherence, feature(with_negative_coherence))] diff --git a/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr b/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr index 097cc4e0fe3e..6d6e163b2066 100644 --- a/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr +++ b/tests/ui/coherence/coherence-negative-outlives-lifetimes.stock.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait<'_>` for type `&_` - --> $DIR/coherence-negative-outlives-lifetimes.rs:14:1 + --> $DIR/coherence-negative-outlives-lifetimes.rs:18:1 | LL | impl<'a, T: MyPredicate<'a>> MyTrait<'a> for T {} | ---------------------------------------------- first implementation here diff --git a/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr b/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr index 097cc4e0fe3e..6d6e163b2066 100644 --- a/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr +++ b/tests/ui/coherence/coherence-negative-outlives-lifetimes.with_negative_coherence.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait<'_>` for type `&_` - --> $DIR/coherence-negative-outlives-lifetimes.rs:14:1 + --> $DIR/coherence-negative-outlives-lifetimes.rs:18:1 | LL | impl<'a, T: MyPredicate<'a>> MyTrait<'a> for T {} | ---------------------------------------------- first implementation here diff --git a/tests/ui/coherence/coherence-overlap-with-regions.rs b/tests/ui/coherence/coherence-overlap-with-regions.rs index 9945c8e6cfd7..32f01f418010 100644 --- a/tests/ui/coherence/coherence-overlap-with-regions.rs +++ b/tests/ui/coherence/coherence-overlap-with-regions.rs @@ -1,10 +1,4 @@ -// known-bug: unknown - -// This fails because we currently perform negative coherence in coherence mode. -// This means that when looking for a negative predicate, we also assemble a -// coherence-unknowable predicate. Since confirming the negative impl has region -// obligations, we don't prefer the impl over the unknowable predicate -// unconditionally and instead flounder. +// check-pass #![feature(negative_impls)] #![feature(rustc_attrs)] diff --git a/tests/ui/coherence/coherence-overlap-with-regions.stderr b/tests/ui/coherence/coherence-overlap-with-regions.stderr deleted file mode 100644 index fd25f0978bad..000000000000 --- a/tests/ui/coherence/coherence-overlap-with-regions.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0119]: conflicting implementations of trait `Bar` for type `&_` - --> $DIR/coherence-overlap-with-regions.rs:20:1 - | -LL | impl Bar for T {} - | ---------------------- first implementation here -LL | impl Bar for &T where T: 'static {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/negative-coherence-check-placeholder-outlives.rs b/tests/ui/coherence/negative-coherence-check-placeholder-outlives.rs new file mode 100644 index 000000000000..5d8beb451328 --- /dev/null +++ b/tests/ui/coherence/negative-coherence-check-placeholder-outlives.rs @@ -0,0 +1,14 @@ +#![feature(negative_impls)] +#![feature(with_negative_coherence)] + +struct Wrap(T); + +trait Foo {} +impl !Foo for Box {} + +trait Bar {} +impl Bar for T where T: Foo {} +impl Bar for Box {} +//~^ ERROR conflicting implementations of trait `Bar` for type `Box<_>` + +fn main() {} diff --git a/tests/ui/coherence/negative-coherence-considering-regions.static_lt.stderr b/tests/ui/coherence/negative-coherence-check-placeholder-outlives.stderr similarity index 59% rename from tests/ui/coherence/negative-coherence-considering-regions.static_lt.stderr rename to tests/ui/coherence/negative-coherence-check-placeholder-outlives.stderr index 87e7be2aa44a..52d36c92f996 100644 --- a/tests/ui/coherence/negative-coherence-considering-regions.static_lt.stderr +++ b/tests/ui/coherence/negative-coherence-check-placeholder-outlives.stderr @@ -1,11 +1,10 @@ -error[E0119]: conflicting implementations of trait `Bar` for type `&'static _` - --> $DIR/negative-coherence-considering-regions.rs:26:1 +error[E0119]: conflicting implementations of trait `Bar` for type `Box<_>` + --> $DIR/negative-coherence-check-placeholder-outlives.rs:11:1 | LL | impl Bar for T where T: Foo {} | ------------------------------ first implementation here -... -LL | impl Bar for &'static T {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&'static _` +LL | impl Bar for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>` error: aborting due to previous error diff --git a/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr b/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr index 4cf50b4f208e..442934a89492 100644 --- a/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr +++ b/tests/ui/coherence/negative-coherence-considering-regions.any_lt.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Bar` for type `&_` - --> $DIR/negative-coherence-considering-regions.rs:22:1 + --> $DIR/negative-coherence-considering-regions.rs:16:1 | LL | impl Bar for T where T: Foo {} | ------------------------------ first implementation here diff --git a/tests/ui/coherence/negative-coherence-considering-regions.rs b/tests/ui/coherence/negative-coherence-considering-regions.rs index 597a59726284..a43ad19eca7c 100644 --- a/tests/ui/coherence/negative-coherence-considering-regions.rs +++ b/tests/ui/coherence/negative-coherence-considering-regions.rs @@ -1,11 +1,5 @@ // revisions: any_lt static_lt -//[static_lt] known-bug: unknown - -// This fails because we currently perform negative coherence in coherence mode. -// This means that when looking for a negative predicate, we also assemble a -// coherence-unknowable predicate. Since confirming the negative impl has region -// obligations, we don't prefer the impl over the unknowable predicate -// unconditionally and instead flounder. +//[static_lt] check-pass #![feature(negative_impls)] #![feature(with_negative_coherence)] diff --git a/tests/ui/coherence/negative-coherence-placeholder-region-constraints-on-unification.explicit.stderr b/tests/ui/coherence/negative-coherence-placeholder-region-constraints-on-unification.explicit.stderr new file mode 100644 index 000000000000..34f3904443c3 --- /dev/null +++ b/tests/ui/coherence/negative-coherence-placeholder-region-constraints-on-unification.explicit.stderr @@ -0,0 +1,19 @@ +error: conflicting implementations of trait `FnMarker` for type `fn(&_)` + --> $DIR/negative-coherence-placeholder-region-constraints-on-unification.rs:21:1 + | +LL | impl FnMarker for fn(T) {} + | ------------------------------------------- first implementation here +LL | impl FnMarker for fn(&T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `fn(&_)` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #56105 + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details +note: the lint level is defined here + --> $DIR/negative-coherence-placeholder-region-constraints-on-unification.rs:4:11 + | +LL | #![forbid(coherence_leak_check)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/coherence/negative-coherence-placeholder-region-constraints-on-unification.rs b/tests/ui/coherence/negative-coherence-placeholder-region-constraints-on-unification.rs new file mode 100644 index 000000000000..26d9d84d8f0c --- /dev/null +++ b/tests/ui/coherence/negative-coherence-placeholder-region-constraints-on-unification.rs @@ -0,0 +1,25 @@ +// revisions: explicit implicit +//[implicit] check-pass + +#![forbid(coherence_leak_check)] +#![feature(negative_impls, with_negative_coherence)] + +pub trait Marker {} + +#[cfg(implicit)] +impl !Marker for &T {} + +#[cfg(explicit)] +impl<'a, T: ?Sized + 'a> !Marker for &'a T {} + +trait FnMarker {} + +// Unifying these two impls below results in a `T: '!0` obligation +// that we shouldn't need to care about. Ideally, we'd treat that +// as an assumption when proving `&'!0 T: Marker`... +impl FnMarker for fn(T) {} +impl FnMarker for fn(&T) {} +//[explicit]~^ ERROR conflicting implementations of trait `FnMarker` for type `fn(&_)` +//[explicit]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +fn main() {} diff --git a/tests/ui/consts/const-int-unchecked.stderr b/tests/ui/consts/const-int-unchecked.stderr index ad880d56d904..ad14c8f68f8c 100644 --- a/tests/ui/consts/const-int-unchecked.stderr +++ b/tests/ui/consts/const-int-unchecked.stderr @@ -62,61 +62,61 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:41:33 | LL | const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 255 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:43:35 | LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 65535 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:45:35 | LL | const SHL_I32_NEG: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 4294967295 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:47:35 | LL | const SHL_I64_NEG: i64 = unsafe { intrinsics::unchecked_shl(5_i64, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 18446744073709551615 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:49:37 | LL | const SHL_I128_NEG: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:55:40 | LL | const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 250 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -6 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:57:42 | LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 65523 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:59:42 | LL | const SHL_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -25) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 4294967271 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -25 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:61:42 | LL | const SHL_I64_NEG_RANDOM: i64 = unsafe { intrinsics::unchecked_shl(5_i64, -30) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 18446744073709551586 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -30 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:63:44 | LL | const SHL_I128_NEG_RANDOM: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -93) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shl` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -93 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:70:29 @@ -182,61 +182,61 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:96:33 | LL | const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 255 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:98:35 | LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 65535 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:100:35 | LL | const SHR_I32_NEG: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 4294967295 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:102:35 | LL | const SHR_I64_NEG: i64 = unsafe { intrinsics::unchecked_shr(5_i64, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 18446744073709551615 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:104:37 | LL | const SHR_I128_NEG: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:110:40 | LL | const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 250 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -6 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:112:42 | LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 65523 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:114:42 | LL | const SHR_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -25) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 4294967271 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -25 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:116:42 | LL | const SHR_I64_NEG_RANDOM: i64 = unsafe { intrinsics::unchecked_shr(5_i64, -30) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 18446744073709551586 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -30 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:118:44 | LL | const SHR_I128_NEG_RANDOM: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -93) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shr` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -93 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:123:25 diff --git a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs index 2491071d1e1d..6285427f59cb 100644 --- a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs +++ b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs @@ -23,10 +23,18 @@ fn foo2(x: *const u8) { const D: *const [u8; 4] = b"abcd"; +const STR: *const str = "abcd"; + fn main() { match D { D => {} //~ERROR: behave unpredictably //~| previously accepted _ => {} } + + match STR { + STR => {} //~ERROR: behave unpredictably + //~| previously accepted + _ => {} + } } diff --git a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr index ab53346b5eed..1546f23908c6 100644 --- a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr +++ b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr @@ -22,7 +22,7 @@ LL | C_INNER => {} = note: for more information, see issue #62411 error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:28:9 + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:30:9 | LL | D => {} | ^ @@ -30,5 +30,14 @@ LL | D => {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #62411 -error: aborting due to 3 previous errors +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:36:9 + | +LL | STR => {} + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +error: aborting due to 4 previous errors diff --git a/tests/ui/consts/effect_param.rs b/tests/ui/consts/effect_param.rs index f11ec739fce5..bfe0a519037d 100644 --- a/tests/ui/consts/effect_param.rs +++ b/tests/ui/consts/effect_param.rs @@ -3,9 +3,13 @@ fn main() { i8::checked_sub::(42, 43); //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied + i8::checked_sub::(42, 43); + //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied } const FOO: () = { i8::checked_sub::(42, 43); //~^ ERROR: method takes 0 generic arguments but 1 generic argument was supplied + i8::checked_sub::(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 f8c4bfc02e54..dba5d49b7921 100644 --- a/tests/ui/consts/effect_param.stderr +++ b/tests/ui/consts/effect_param.stderr @@ -1,11 +1,19 @@ 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::(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:13:9 + | +LL | i8::checked_sub::(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 | @@ -14,6 +22,14 @@ LL | i8::checked_sub::(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::(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 af8e60ce460c..9c699c7bb829 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 cfbbb9c1b318..10f988cc0666 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 7bd0eaf77a95..4e60368c387c 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-width/flag-json.stderr b/tests/ui/diagnostic-width/flag-json.stderr index b21391d1640e..16c17526809d 100644 --- a/tests/ui/diagnostic-width/flag-json.stderr +++ b/tests/ui/diagnostic-width/flag-json.stderr @@ -1,4 +1,4 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -33,8 +33,8 @@ LL | ..._: () = 42; | expected due to this "} -{"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error +{"$message_type":"diagnostic","message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error "} -{"message":"For more information about this error, try `rustc --explain E0308`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0308`. +{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0308`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0308`. "} 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 8410b3eb1051..0893f29c4a3b 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 906472beb49d..fc4aa8ef7d89 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 000000000000..a7becd2f88f6 --- /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 000000000000..f9395ae85bc2 --- /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 6040f3f30a7b..ffc37b260a6b 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 b7fe3b79b475..55f6d0fa3238 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 7066d29760e7..473c9a339fc2 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 98876e305ca0..9f82d4c44cf0 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 2ed0a0924006..4f216568979a 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 c20bbce3f24a..d60db01a4653 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/dropck/dropck_trait_cycle_checked.stderr b/tests/ui/dropck/dropck_trait_cycle_checked.stderr index 4d4f7b9df117..63fd07a91633 100644 --- a/tests/ui/dropck/dropck_trait_cycle_checked.stderr +++ b/tests/ui/dropck/dropck_trait_cycle_checked.stderr @@ -8,6 +8,8 @@ LL | o1.set0(&o2); ... LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o3` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:112:13 @@ -20,6 +22,8 @@ LL | o1.set1(&o3); ... LL | } | - `o3` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:113:13 @@ -32,6 +36,8 @@ LL | o2.set0(&o2); ... LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o3` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:114:13 @@ -44,6 +50,8 @@ LL | o2.set1(&o3); ... LL | } | - `o3` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o1` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:115:13 @@ -56,6 +64,8 @@ LL | o3.set0(&o1); LL | o3.set1(&o2); LL | } | - `o1` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:116:13 @@ -67,6 +77,8 @@ LL | o3.set1(&o2); | ^^^ borrowed value does not live long enough LL | } | - `o2` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box>` actually means `Box<(dyn Obj<'_> + 'static)>` error: aborting due to 6 previous errors diff --git a/tests/ui/error-codes/E0609.stderr b/tests/ui/error-codes/E0609.stderr index 797e95d02dda..cad857553da3 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 b21b6fdfcf1f..4dd9848cf07b 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 9d96b4f63fb6..248efc93d008 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 7cef5b3497b5..d3b3bef6dae6 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 7f67adbdb19c..a296751994ea 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/hygiene/extern-prelude-from-opaque-fail-2018.stderr b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr index 7ed15e89caaf..78e6376bca2e 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr @@ -25,8 +25,8 @@ LL | a!(); | ---- in this macro invocation | = help: consider importing one of these items: - std::mem core::mem + std::mem = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0433]: failed to resolve: use of undeclared crate or module `my_core` diff --git a/tests/ui/impl-trait/auto-trait-coherence.next.stderr b/tests/ui/impl-trait/auto-trait-coherence.next.stderr index cee359997b4f..7833ac688bab 100644 --- a/tests/ui/impl-trait/auto-trait-coherence.next.stderr +++ b/tests/ui/impl-trait/auto-trait-coherence.next.stderr @@ -6,8 +6,6 @@ LL | impl AnotherTrait for T {} ... LL | impl AnotherTrait for D { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D` - | - = 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/recursive-coroutine.stderr b/tests/ui/impl-trait/recursive-coroutine.current.stderr similarity index 91% rename from tests/ui/impl-trait/recursive-coroutine.stderr rename to tests/ui/impl-trait/recursive-coroutine.current.stderr index d36a58a86434..45911b7fc11f 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 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 000000000000..45911b7fc11f --- /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 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 6351cef95a61..f0bee4f120ff 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 similarity index 78% rename from tests/ui/impl-trait/two_tait_defining_each_other.stderr rename to tests/ui/impl-trait/two_tait_defining_each_other.current.stderr index 1a42ac525a6a..e5f7e5e5c449 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 6eb2a11b22c5..b43a7cabd058 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 similarity index 76% rename from tests/ui/impl-trait/two_tait_defining_each_other2.stderr rename to tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr index 4d8f96de1626..33866451c6b4 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 000000000000..e3a4797e44cb --- /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 05b09668016c..817de109fbe9 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 similarity index 76% rename from tests/ui/impl-trait/two_tait_defining_each_other3.stderr rename to tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr index b06dc16d5e70..451ba407b718 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 37f8ae1b84b5..6fb9e6e71889 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 000000000000..a591d0f5d4d0 --- /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 `::Ty2<'lt>` does not fulfill the required lifetime + --> $DIR/normalization-placeholder-leak.rs:36:44 + | +LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo>) {} + | ^^^^^^^^^^^^^^^^^^ + +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 000000000000..5350dcbb2331 --- /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`, +// 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 Trait for T { + type Ty<'a> = () where Self: 'a; +} + +struct Foo(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>) {} + //[fail]~^ ERROR `::Ty2<'lt>` does not fulfill the required lifetime +} + + +mod pass { + use super::*; + + // implied_bound: 'static: placeholder('x). + // don't ice. + fn test_lifetime(_: 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(_: Foo>) { + None::<&'static T::Ty2<'_>>; + } +} + +fn main() {} diff --git a/tests/ui/issues/issue-11004.stderr b/tests/ui/issues/issue-11004.stderr index b5831e42e399..ea141e61df8d 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 52b8dc049702..ded927693b20 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 49ebb2976e8a..f07bc7ad4f18 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 c36048ea5450..47c3f13d23ca 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 1eb530542c02..3358cac82e06 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 54529fdf5ba7..71655457f901 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 3fff91acbc25..fb2b9b42859f 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 be5714cd91a2..44f01c1c1673 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 988ebe03ccf6..1ce05ba390d7 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 f9eead8a4f21..3f6ed0231d8b 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 f08bcdfe6d16..773792c7a3f1 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 0f92fc2f7f31..cf3834952c6a 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 2a8750d3e431..3b40b3dd8f07 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 bool>(_f: F) { print!(""); diff --git a/tests/ui/issues/issue-31011.stderr b/tests/ui/issues/issue-31011.stderr index 58c170409fd4..4971e3357b4b 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(context: &T) -> () | - type parameter 'T' declared here diff --git a/tests/ui/issues/issue-33096.rs b/tests/ui/issues/issue-33096.rs index 2501e1430b3d..f0b472e2fe82 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 f8d703dc3b16..ee9f4d4c3016 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 88dcdd411380..1f68560509e8 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 5657ba69f944..f2dfaf3dd367 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 1964d739989e..2a059120e817 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 387ef0776ff4..f98fde437848 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 01a5d563639b..318e3099f98b 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 5c5ac59873a3..d20c07276a8c 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 5e1b816defda..0aa1ae7222f5 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 af93f76221d4..9573c9b703aa 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/json/json-bom-plus-crlf-multifile.stderr b/tests/ui/json/json-bom-plus-crlf-multifile.stderr index 84040e8050e9..0c6c654d60a1 100644 --- a/tests/ui/json/json-bom-plus-crlf-multifile.stderr +++ b/tests/ui/json/json-bom-plus-crlf-multifile.stderr @@ -1,4 +1,4 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -26,7 +26,7 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":622,"byte_end":622,"line_start":17,"line_end":17,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types "} -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -54,7 +54,7 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":682,"byte_end":682,"line_start":19,"line_end":19,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types "} -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -82,7 +82,7 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":746,"byte_end":746,"line_start":23,"line_end":23,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types "} -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -110,5 +110,5 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":801,"byte_end":809,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":792,"byte_end":798,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types "} -{"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors +{"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors "} diff --git a/tests/ui/json/json-bom-plus-crlf.stderr b/tests/ui/json/json-bom-plus-crlf.stderr index b0f450e9ecc1..31dbacb59e1e 100644 --- a/tests/ui/json/json-bom-plus-crlf.stderr +++ b/tests/ui/json/json-bom-plus-crlf.stderr @@ -1,4 +1,4 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -26,7 +26,7 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":597,"byte_end":603,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":607,"byte_end":607,"line_start":16,"line_end":16,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types "} -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -54,7 +54,7 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":666,"byte_end":667,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":657,"byte_end":663,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":667,"byte_end":667,"line_start":18,"line_end":18,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types "} -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -82,7 +82,7 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":730,"byte_end":731,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":720,"byte_end":726,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":731,"byte_end":731,"line_start":22,"line_end":22,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types "} -{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. +{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. Erroneous code examples: @@ -110,5 +110,5 @@ most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. "},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":786,"byte_end":794,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":777,"byte_end":783,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types "} -{"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors +{"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors "} diff --git a/tests/ui/json/json-multiple.stderr b/tests/ui/json/json-multiple.stderr index 55ccfd5fa70d..7689fb94a6c8 100644 --- a/tests/ui/json/json-multiple.stderr +++ b/tests/ui/json/json-multiple.stderr @@ -1 +1 @@ -{"artifact":"$TEST_BUILD_DIR/json/json-multiple/libjson_multiple.rlib","emit":"link"} +{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/json/json-multiple/libjson_multiple.rlib","emit":"link"} diff --git a/tests/ui/json/json-options.stderr b/tests/ui/json/json-options.stderr index 645a26f5ad40..668fc18097f0 100644 --- a/tests/ui/json/json-options.stderr +++ b/tests/ui/json/json-options.stderr @@ -1 +1 @@ -{"artifact":"$TEST_BUILD_DIR/json/json-options/libjson_options.rlib","emit":"link"} +{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/json/json-options/libjson_options.rlib","emit":"link"} diff --git a/tests/ui/json/json-short.stderr b/tests/ui/json/json-short.stderr index 3bd85b083d00..030af0c5ada6 100644 --- a/tests/ui/json/json-short.stderr +++ b/tests/ui/json/json-short.stderr @@ -1,4 +1,4 @@ -{"message":"`main` function not found in crate `json_short`","code":{"code":"E0601","explanation":"No `main` function was found in a binary crate. +{"$message_type":"diagnostic","message":"`main` function not found in crate `json_short`","code":{"code":"E0601","explanation":"No `main` function was found in a binary crate. To fix this error, add a `main` function: @@ -15,5 +15,5 @@ If you don't know the basics of Rust, you can look at the [rust-book]: https://doc.rust-lang.org/book/ "},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":62,"byte_end":62,"line_start":1,"line_end":1,"column_start":63,"column_end":63,"is_primary":true,"text":[{"text":"// compile-flags: --json=diagnostic-short --error-format=json","highlight_start":63,"highlight_end":63}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:1:63: error[E0601]: `main` function not found in crate `json_short` "} -{"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error +{"$message_type":"diagnostic","message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error "} diff --git a/tests/ui/lint/future-incompat-json-test.rs b/tests/ui/lint/future-incompat-json-test.rs new file mode 100644 index 000000000000..6ccd670294c6 --- /dev/null +++ b/tests/ui/lint/future-incompat-json-test.rs @@ -0,0 +1,10 @@ +// compile-flags: -Zfuture-incompat-test --json=future-incompat --error-format=json +// check-pass + +// The `-Zfuture-incompat-test flag causes any normal warning to be included +// in the future-incompatible report. The stderr output here should mention +// the future incompatible report (as extracted by compiletest). + +fn main() { + let x = 1; +} diff --git a/tests/ui/lint/future-incompat-json-test.stderr b/tests/ui/lint/future-incompat-json-test.stderr new file mode 100644 index 000000000000..c4ab5a00d16c --- /dev/null +++ b/tests/ui/lint/future-incompat-json-test.stderr @@ -0,0 +1,10 @@ +{"$message_type":"future_incompat","future_incompat_report":[{"diagnostic":{"$message_type":"diagnostic","message":"unused variable: `x`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"$DIR/future-incompat-json-test.rs","byte_start":338,"byte_end":339,"line_start":9,"line_end":9,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let x = 1;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`-A unused-variables` implied by `-A unused`","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"to override `-A unused` add `#[allow(unused_variables)]`","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"$DIR/future-incompat-json-test.rs","byte_start":338,"byte_end":339,"line_start":9,"line_end":9,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" let x = 1;","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":"_x","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"warning: unused variable: `x` + --> $DIR/future-incompat-json-test.rs:9:9 + | +LL | let x = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | + = note: `-A unused-variables` implied by `-A unused` + = help: to override `-A unused` add `#[allow(unused_variables)]` + +"}}]} diff --git a/tests/ui/lint/unused/must_use-pin.rs b/tests/ui/lint/unused/must_use-pin.rs new file mode 100644 index 000000000000..b08515428b1b --- /dev/null +++ b/tests/ui/lint/unused/must_use-pin.rs @@ -0,0 +1,45 @@ +#![deny(unused_must_use)] + +use std::{ops::Deref, pin::Pin}; + +#[must_use] +struct MustUse; + +#[must_use] +struct MustUsePtr<'a, T>(&'a T); + +impl<'a, T> Deref for MustUsePtr<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +fn pin_ref() -> Pin<&'static ()> { + Pin::new(&()) +} + +fn pin_ref_mut() -> Pin<&'static mut ()> { + Pin::new(unimplemented!()) +} + +fn pin_must_use_ptr() -> Pin> { + Pin::new(MustUsePtr(&())) +} + +fn pin_box() -> Pin> { + Box::pin(()) +} + +fn pin_box_must_use() -> Pin> { + Box::pin(MustUse) +} + +fn main() { + pin_ref(); + pin_ref_mut(); + pin_must_use_ptr(); //~ ERROR unused pinned `MustUsePtr` that must be used + pin_box(); + pin_box_must_use(); //~ ERROR unused pinned boxed `MustUse` that must be used +} diff --git a/tests/ui/lint/unused/must_use-pin.stderr b/tests/ui/lint/unused/must_use-pin.stderr new file mode 100644 index 000000000000..c04f8fef4873 --- /dev/null +++ b/tests/ui/lint/unused/must_use-pin.stderr @@ -0,0 +1,20 @@ +error: unused pinned `MustUsePtr` that must be used + --> $DIR/must_use-pin.rs:42:5 + | +LL | pin_must_use_ptr(); + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/must_use-pin.rs:1:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ + +error: unused pinned boxed `MustUse` that must be used + --> $DIR/must_use-pin.rs:44:5 + | +LL | pin_box_must_use(); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/unused_parens_json_suggestion.stderr b/tests/ui/lint/unused_parens_json_suggestion.stderr index ea19e0cdc0cb..ac3b2b5b21a7 100644 --- a/tests/ui/lint/unused_parens_json_suggestion.stderr +++ b/tests/ui/lint/unused_parens_json_suggestion.stderr @@ -1,4 +1,4 @@ -{"message":"unnecessary parentheses around assigned value","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":577,"byte_end":578,"line_start":16,"line_end":16,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3)); +{"$message_type":"diagnostic","message":"unnecessary parentheses around assigned value","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":577,"byte_end":578,"line_start":16,"line_end":16,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3)); --> $DIR/unused_parens_json_suggestion.rs:16:14 | LL | let _a = (1 / (2 + 3)); @@ -16,6 +16,6 @@ LL + let _a = 1 / (2 + 3); | "} -{"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error +{"$message_type":"diagnostic","message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error "} diff --git a/tests/ui/lint/unused_parens_remove_json_suggestion.stderr b/tests/ui/lint/unused_parens_remove_json_suggestion.stderr index f4c6ceaf1dc1..7521d41cc939 100644 --- a/tests/ui/lint/unused_parens_remove_json_suggestion.stderr +++ b/tests/ui/lint/unused_parens_remove_json_suggestion.stderr @@ -1,4 +1,4 @@ -{"message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":481,"byte_end":482,"line_start":17,"line_end":17,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (_b) { +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":481,"byte_end":482,"line_start":17,"line_end":17,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (_b) { --> $DIR/unused_parens_remove_json_suggestion.rs:17:8 | LL | if (_b) { @@ -16,7 +16,7 @@ LL + if _b { | "} -{"message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":612,"byte_end":613,"line_start":28,"line_end":28,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" if(c) { +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":612,"byte_end":613,"line_start":28,"line_end":28,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" if(c) { --> $DIR/unused_parens_remove_json_suggestion.rs:28:7 | LL | if(c) { @@ -29,7 +29,7 @@ LL + if c { | "} -{"message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":692,"byte_end":693,"line_start":32,"line_end":32,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (c){ +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":692,"byte_end":693,"line_start":32,"line_end":32,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (c){ --> $DIR/unused_parens_remove_json_suggestion.rs:32:8 | LL | if (c){ @@ -42,7 +42,7 @@ LL + if c { | "} -{"message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":774,"byte_end":775,"line_start":36,"line_end":36,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":788,"byte_end":789,"line_start":36,"line_end":36,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":774,"byte_end":775,"line_start":36,"line_end":36,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":788,"byte_end":789,"line_start":36,"line_end":36,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition +{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":774,"byte_end":775,"line_start":36,"line_end":36,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":788,"byte_end":789,"line_start":36,"line_end":36,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":774,"byte_end":775,"line_start":36,"line_end":36,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":788,"byte_end":789,"line_start":36,"line_end":36,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition --> $DIR/unused_parens_remove_json_suggestion.rs:36:11 | LL | while (false && true){ @@ -55,7 +55,7 @@ LL + while false && true { | "} -{"message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":802,"byte_end":803,"line_start":37,"line_end":37,"column_start":12,"column_end":13,"is_primary":true,"text":[{"text":" if (c) { +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":802,"byte_end":803,"line_start":37,"line_end":37,"column_start":12,"column_end":13,"is_primary":true,"text":[{"text":" if (c) { --> $DIR/unused_parens_remove_json_suggestion.rs:37:12 | LL | if (c) { @@ -68,7 +68,7 @@ LL + if c { | "} -{"message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":899,"byte_end":900,"line_start":43,"line_end":43,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" while(true && false) { +{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":899,"byte_end":900,"line_start":43,"line_end":43,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" while(true && false) { --> $DIR/unused_parens_remove_json_suggestion.rs:43:10 | LL | while(true && false) { @@ -81,7 +81,7 @@ LL + while true && false { | "} -{"message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":968,"byte_end":969,"line_start":44,"line_end":44,"column_start":18,"column_end":19,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){ +{"$message_type":"diagnostic","message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":968,"byte_end":969,"line_start":44,"line_end":44,"column_start":18,"column_end":19,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){ --> $DIR/unused_parens_remove_json_suggestion.rs:44:18 | LL | for _ in (0 .. 3){ @@ -94,7 +94,7 @@ LL + for _ in 0 .. 3 { | "} -{"message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1069,"byte_end":1070,"line_start":49,"line_end":49,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) { +{"$message_type":"diagnostic","message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1069,"byte_end":1070,"line_start":49,"line_end":49,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) { --> $DIR/unused_parens_remove_json_suggestion.rs:49:14 | LL | for _ in (0 .. 3) { @@ -107,7 +107,7 @@ LL + for _ in 0 .. 3 { | "} -{"message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1128,"byte_end":1129,"line_start":50,"line_end":50,"column_start":15,"column_end":16,"is_primary":true,"text":[{"text":" while (true && false) { +{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1128,"byte_end":1129,"line_start":50,"line_end":50,"column_start":15,"column_end":16,"is_primary":true,"text":[{"text":" while (true && false) { --> $DIR/unused_parens_remove_json_suggestion.rs:50:15 | LL | while (true && false) { @@ -120,6 +120,6 @@ LL + while true && false { | "} -{"message":"aborting due to 9 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 9 previous errors +{"$message_type":"diagnostic","message":"aborting due to 9 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 9 previous errors "} diff --git a/tests/ui/lint/use_suggestion_json.stderr b/tests/ui/lint/use_suggestion_json.stderr index d17514303abf..7dfd269a4e24 100644 --- a/tests/ui/lint/use_suggestion_json.stderr +++ b/tests/ui/lint/use_suggestion_json.stderr @@ -1,3 +1,3 @@ -{"message":"`--error-format=pretty-json` is unstable","code":null,"level":"error","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: `--error-format=pretty-json` is unstable\u001b[0m +{"$message_type":"diagnostic","message":"`--error-format=pretty-json` is unstable","code":null,"level":"error","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: `--error-format=pretty-json` is unstable\u001b[0m "} diff --git a/tests/ui/lto/debuginfo-lto.rs b/tests/ui/lto/debuginfo-lto.rs index 43f75b0344be..e4beee9e737b 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 c2b047022ed5..88c2c1c773d3 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 ae6de3c5046c..f6178c137db9 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 c42ee9b295e1..6218bf43a1ef 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 b2fdd2e19658..00eb7593799e 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 21e47da07571..6a89d3bfd095 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 0f694c587fcb..d0e50f3ae16b 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/mir/validate/critical-edge.rs b/tests/ui/mir/validate/critical-edge.rs new file mode 100644 index 000000000000..9ef655cd1bb4 --- /dev/null +++ b/tests/ui/mir/validate/critical-edge.rs @@ -0,0 +1,31 @@ +// Optimized MIR shouldn't have critical call edges +// +// build-fail +// edition: 2021 +// compile-flags: --crate-type=lib +// failure-status: 101 +// dont-check-compiler-stderr +// error-pattern: encountered critical edge in `Call` terminator +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "optimized")] +#[inline(always)] +pub fn f(a: u32) -> u32 { + mir!( + { + match a { + 0 => bb1, + _ => bb2, + } + } + bb1 = { + Call(RET = f(1), bb2, UnwindTerminate(ReasonAbi)) + } + + bb2 = { + RET = 2; + Return() + } + ) +} diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index d63cec489176..142a52aef13d 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/never_type/never-from-impl-is-reserved.stderr b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr similarity index 92% rename from tests/ui/never_type/never-from-impl-is-reserved.stderr rename to tests/ui/never_type/never-from-impl-is-reserved.current.stderr index 871c51205282..6c71785de289 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.stderr +++ b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` - --> $DIR/never-from-impl-is-reserved.rs:10:1 + --> $DIR/never-from-impl-is-reserved.rs:13:1 | LL | impl MyTrait for MyFoo {} | ---------------------- first implementation here diff --git a/tests/ui/never_type/never-from-impl-is-reserved.next.stderr b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr new file mode 100644 index 000000000000..6c71785de289 --- /dev/null +++ b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` + --> $DIR/never-from-impl-is-reserved.rs:13:1 + | +LL | impl MyTrait for MyFoo {} + | ---------------------- first implementation here +LL | // This will conflict with the first impl if we impl `for T: From`. +LL | impl MyTrait for T where T: From {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo` + | + = note: permitting this impl would forbid us from adding `impl From for T` later; see rust-lang/rust#64715 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/never_type/never-from-impl-is-reserved.rs b/tests/ui/never_type/never-from-impl-is-reserved.rs index 9d16015bdc12..f358e045cf01 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.rs +++ b/tests/ui/never_type/never-from-impl-is-reserved.rs @@ -1,5 +1,8 @@ // check that the `for T: From` impl is reserved +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next-coherence + #![feature(never_type)] pub struct MyFoo; diff --git a/tests/ui/offset-of/offset-of-self.stderr b/tests/ui/offset-of/offset-of-self.stderr index df555463f988..2dc17189a702 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/attribute/multiple-tail-expr-behind-cfg.rs b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs new file mode 100644 index 000000000000..d97f24a3d294 --- /dev/null +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs @@ -0,0 +1,19 @@ +#![feature(stmt_expr_attributes)] + +fn foo() -> String { + #[cfg(feature = "validation")] + [1, 2, 3].iter().map(|c| c.to_string()).collect::() //~ ERROR expected `;`, found `#` + #[cfg(not(feature = "validation"))] + String::new() +} + +fn bar() -> String { + #[attr] + [1, 2, 3].iter().map(|c| c.to_string()).collect::() //~ ERROR expected `;`, found `#` + #[attr] //~ ERROR cannot find attribute `attr` in this scope + String::new() +} + +fn main() { + println!("{}", foo()); +} diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr new file mode 100644 index 000000000000..a71253a5e428 --- /dev/null +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr @@ -0,0 +1,54 @@ +error: expected `;`, found `#` + --> $DIR/multiple-tail-expr-behind-cfg.rs:5:64 + | +LL | #[cfg(feature = "validation")] + | ------------------------------ only `;` terminated statements or tail expressions are allowed after this attribute +LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::() + | ^ expected `;` here +LL | #[cfg(not(feature = "validation"))] + | - unexpected token + | +help: add `;` here + | +LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::(); + | + +help: alternatively, consider surrounding the expression with a block + | +LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::() } + | + + +help: it seems like you are trying to provide different expressions depending on `cfg`, consider using `if cfg!(..)` + | +LL ~ if cfg!(feature = "validation") { +LL ~ [1, 2, 3].iter().map(|c| c.to_string()).collect::() +LL ~ } else if cfg!(not(feature = "validation")) { +LL ~ String::new() +LL + } + | + +error: expected `;`, found `#` + --> $DIR/multiple-tail-expr-behind-cfg.rs:12:64 + | +LL | #[attr] + | ------- only `;` terminated statements or tail expressions are allowed after this attribute +LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::() + | ^ expected `;` here +LL | #[attr] + | - unexpected token + | +help: add `;` here + | +LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::(); + | + +help: alternatively, consider surrounding the expression with a block + | +LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::() } + | + + + +error: cannot find attribute `attr` in this scope + --> $DIR/multiple-tail-expr-behind-cfg.rs:13:7 + | +LL | #[attr] + | ^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/bad-pointer-type.stderr b/tests/ui/parser/bad-pointer-type.stderr index b7225ca887dd..e843c49886bc 100644 --- a/tests/ui/parser/bad-pointer-type.stderr +++ b/tests/ui/parser/bad-pointer-type.stderr @@ -6,10 +6,10 @@ LL | fn foo(_: *()) { | help: add `mut` or `const` here | -LL | fn foo(_: *const ()) { - | +++++ LL | fn foo(_: *mut ()) { | +++ +LL | fn foo(_: *const ()) { + | +++++ error: aborting due to previous error diff --git a/tests/ui/parser/double-pointer.stderr b/tests/ui/parser/double-pointer.stderr index 28037f932655..10aedbb92a1f 100644 --- a/tests/ui/parser/double-pointer.stderr +++ b/tests/ui/parser/double-pointer.stderr @@ -6,10 +6,10 @@ LL | let dptr: **const i32 = &ptr; | help: add `mut` or `const` here | -LL | let dptr: *const *const i32 = &ptr; - | +++++ LL | let dptr: *mut *const i32 = &ptr; | +++ +LL | let dptr: *const *const i32 = &ptr; + | +++++ error: aborting due to previous error diff --git a/tests/ui/parser/float-field-interpolated.rs b/tests/ui/parser/float-field-interpolated.rs index a30532035365..990f2926dc86 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 664adb35818a..2a1a4926cb3c 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 7090efc5014b..d67d270ef7b9 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 d53302449a80..df12f7768d42 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 1e7db9df814b..b66e09620f4e 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 8c032e588e31..2bd87ee0c38f 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 000000000000..7b73dfb02df8 --- /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 000000000000..92143fcf3f76 --- /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 000000000000..2df5cca24f06 --- /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 f179d8c9e0a8..66985c9f5e4f 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-hrtb-before-dyn-impl-kw.rs b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs new file mode 100644 index 000000000000..fe363a6887f5 --- /dev/null +++ b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs @@ -0,0 +1,9 @@ +trait Trait {} + +fn test(_: &for<'a> dyn Trait) {} +//~^ ERROR `for<...>` expected after `dyn`, not before + +fn test2(_: for<'a> impl Trait) {} +//~^ ERROR `for<...>` expected after `impl`, not before + +fn main() {} diff --git a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr new file mode 100644 index 000000000000..6fc1259b9106 --- /dev/null +++ b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr @@ -0,0 +1,26 @@ +error: `for<...>` expected after `dyn`, not before + --> $DIR/recover-hrtb-before-dyn-impl-kw.rs:3:21 + | +LL | fn test(_: &for<'a> dyn Trait) {} + | ^^^ + | +help: move `dyn` before the `for<...>` + | +LL - fn test(_: &for<'a> dyn Trait) {} +LL + fn test(_: &dyn for<'a> Trait) {} + | + +error: `for<...>` expected after `impl`, not before + --> $DIR/recover-hrtb-before-dyn-impl-kw.rs:6:21 + | +LL | fn test2(_: for<'a> impl Trait) {} + | ^^^^ + | +help: move `impl` before the `for<...>` + | +LL - fn test2(_: for<'a> impl Trait) {} +LL + fn test2(_: impl for<'a> Trait) {} + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/recover-assoc-const-constraint.rs b/tests/ui/parser/recover/recover-assoc-const-constraint.rs similarity index 100% rename from tests/ui/parser/recover-assoc-const-constraint.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-assoc-const-constraint.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-assoc-eq-missing-term.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-assoc-eq-missing-term.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-assoc-lifetime-constraint.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-assoc-lifetime-constraint.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-const-async-fn-ptr.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-const-async-fn-ptr.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-enum.rs rename to tests/ui/parser/recover/recover-enum.rs diff --git a/tests/ui/parser/recover-enum.stderr b/tests/ui/parser/recover/recover-enum.stderr similarity index 100% rename from tests/ui/parser/recover-enum.stderr rename to tests/ui/parser/recover/recover-enum.stderr diff --git a/tests/ui/parser/recover-enum2.rs b/tests/ui/parser/recover/recover-enum2.rs similarity index 100% rename from tests/ui/parser/recover-enum2.rs rename to tests/ui/parser/recover/recover-enum2.rs diff --git a/tests/ui/parser/recover-enum2.stderr b/tests/ui/parser/recover/recover-enum2.stderr similarity index 100% rename from tests/ui/parser/recover-enum2.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-field-extra-angle-brackets-in-struct-with-a-field.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-field-extra-angle-brackets-in-struct-with-a-field.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-field-extra-angle-brackets.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-field-extra-angle-brackets.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-field-semi.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-field-semi.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-fn-ptr-with-generics.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-fn-ptr-with-generics.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-fn-trait-from-fn-kw.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-fn-trait-from-fn-kw.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-for-loop-parens-around-head.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-for-loop-parens-around-head.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-from-bad-variant.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-from-bad-variant.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-from-homoglyph.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-from-homoglyph.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-labeled-non-block-expr.fixed rename to 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 similarity index 100% rename from tests/ui/parser/recover-labeled-non-block-expr.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-labeled-non-block-expr.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-missing-semi-before-item.fixed rename to 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 similarity index 100% rename from tests/ui/parser/recover-missing-semi-before-item.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-missing-semi-before-item.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-missing-semi.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-missing-semi.stderr rename to 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 000000000000..9ed733bf0794 --- /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 000000000000..6542f440e4b0 --- /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 similarity index 100% rename from tests/ui/parser/recover-quantified-closure.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-quantified-closure.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-range-pats.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-range-pats.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-ref-dyn-mut.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-ref-dyn-mut.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-struct.rs rename to tests/ui/parser/recover/recover-struct.rs diff --git a/tests/ui/parser/recover-struct.stderr b/tests/ui/parser/recover/recover-struct.stderr similarity index 100% rename from tests/ui/parser/recover-struct.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-tuple-pat.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-tuple-pat.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-tuple.rs rename to tests/ui/parser/recover/recover-tuple.rs diff --git a/tests/ui/parser/recover-tuple.stderr b/tests/ui/parser/recover/recover-tuple.stderr similarity index 100% rename from tests/ui/parser/recover-tuple.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-unticked-labels.fixed rename to 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 similarity index 100% rename from tests/ui/parser/recover-unticked-labels.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-unticked-labels.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.fixed rename to 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 similarity index 100% rename from tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-where-clause-before-tuple-struct-body-0.stderr rename to 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 similarity index 100% rename from tests/ui/parser/recover-where-clause-before-tuple-struct-body-1.rs rename to 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 similarity index 100% rename from tests/ui/parser/recover-where-clause-before-tuple-struct-body-1.stderr rename to 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 000000000000..a619fcafc861 --- /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 000000000000..cddd01212798 --- /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 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/privacy/suggest-box-new.fixed b/tests/ui/privacy/suggest-box-new.fixed deleted file mode 100644 index f5ae5c2abfd9..000000000000 --- a/tests/ui/privacy/suggest-box-new.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix -#![allow(dead_code)] -struct U { - wtf: Option>>, - x: T, -} -fn main() { - U { - wtf: Some(Box::new(U { //~ ERROR cannot initialize a tuple struct which contains private fields - wtf: None, - x: (), - })), - x: () - }; -} diff --git a/tests/ui/privacy/suggest-box-new.rs b/tests/ui/privacy/suggest-box-new.rs index 2e18dba8b9fb..7125285fc776 100644 --- a/tests/ui/privacy/suggest-box-new.rs +++ b/tests/ui/privacy/suggest-box-new.rs @@ -1,4 +1,3 @@ -// run-rustfix #![allow(dead_code)] struct U { wtf: Option>>, @@ -12,4 +11,9 @@ fn main() { })), x: () }; + let _ = std::collections::HashMap(); + //~^ ERROR expected function, tuple struct or tuple variant, found struct `std::collections::HashMap` + let _ = std::collections::HashMap {}; + //~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields + let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields } diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index ed7fa036408a..2224f1c60d63 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -1,5 +1,29 @@ +error[E0423]: expected function, tuple struct or tuple variant, found struct `std::collections::HashMap` + --> $DIR/suggest-box-new.rs:14:13 + | +LL | let _ = std::collections::HashMap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL + | + = note: `std::collections::HashMap` defined here + | +help: you might have meant to use an associated function to build this type + | +LL | let _ = std::collections::HashMap::new(); + | ~~~~~~~ +LL | let _ = std::collections::HashMap::with_capacity(_); + | ~~~~~~~~~~~~~~~~~~ +LL | let _ = std::collections::HashMap::with_hasher(_); + | ~~~~~~~~~~~~~~~~ +LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: consider using the `Default` trait + | +LL | let _ = ::default(); + | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + error[E0423]: cannot initialize a tuple struct which contains private fields - --> $DIR/suggest-box-new.rs:9:19 + --> $DIR/suggest-box-new.rs:8:19 | LL | wtf: Some(Box(U { | ^^^ @@ -10,11 +34,67 @@ note: constructor is not visible here due to private fields = note: private field | = note: private field -help: you might have meant to use the `new` associated function +help: you might have meant to use an associated function to build this type | -LL | wtf: Some(Box::new(U { - | +++++ +LL | wtf: Some(Box::new(_)), + | ~~~~~~~~ +LL | wtf: Some(Box::new_uninit()), + | ~~~~~~~~~~~~~~ +LL | wtf: Some(Box::new_zeroed()), + | ~~~~~~~~~~~~~~ +LL | wtf: Some(Box::new_in(_, _)), + | ~~~~~~~~~~~~~~ + and 10 other candidates +help: consider using the `Default` trait + | +LL | wtf: Some(::default()), + | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to previous error +error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields + --> $DIR/suggest-box-new.rs:16:13 + | +LL | let _ = std::collections::HashMap {}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... and other private field `base` that was not provided +help: you might have meant to use an associated function to build this type + | +LL | let _ = std::collections::HashMap::new(); + | ~~~~~~~ +LL | let _ = std::collections::HashMap::with_capacity(_); + | ~~~~~~~~~~~~~~~~~~ +LL | let _ = std::collections::HashMap::with_hasher(_); + | ~~~~~~~~~~~~~~~~ +LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: consider using the `Default` trait + | +LL | let _ = ::default(); + | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: cannot construct `Box<_, _>` with struct literal syntax due to private fields + --> $DIR/suggest-box-new.rs:18:13 + | +LL | let _ = Box {}; + | ^^^ + | + = note: ... and other private fields `0` and `1` that were not provided +help: you might have meant to use an associated function to build this type + | +LL | let _ = Box::new(_); + | ~~~~~~~~ +LL | let _ = Box::new_uninit(); + | ~~~~~~~~~~~~~~ +LL | let _ = Box::new_zeroed(); + | ~~~~~~~~~~~~~~ +LL | let _ = Box::new_in(_, _); + | ~~~~~~~~~~~~~~ + and 10 other candidates +help: consider using the `Default` trait + | +LL | let _ = ::default(); + | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/proc-macro/auxiliary/edition-gated-async-move-syntax.rs b/tests/ui/proc-macro/auxiliary/edition-gated-async-move-syntax.rs new file mode 100644 index 000000000000..ce7e60356f27 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/edition-gated-async-move-syntax.rs @@ -0,0 +1,39 @@ +// force-host +// no-prefer-dynamic + +// Proc macro helper for issue #89699, used by tests/ui/proc-macro/edition-gated-async-move- +// syntax-issue89699.rs, emitting an `async move` closure. This syntax is only available in +// editions 2018 and up, but is used in edition 2015 in the test. + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_attribute] +pub fn foo(_attr: TokenStream, item: TokenStream) -> TokenStream { + let tt = item.into_iter().next().unwrap(); + let sp = tt.span(); + let mut arg = TokenStream::new(); + let mut g = Group::new(Delimiter::Brace, TokenStream::new()); + g.set_span(sp); + arg.extend([ + TokenTree::Ident(Ident::new("async", sp)), + TokenTree::Ident(Ident::new("move", sp)), + TokenTree::Group(g), + ]); + let mut body = TokenStream::new(); + body.extend([ + TokenTree::Ident(Ident::new("async_main", sp)), + TokenTree::Group(Group::new(Delimiter::Parenthesis, arg)), + ]); + + let mut ret = TokenStream::new(); + ret.extend([ + TokenTree::Ident(Ident::new("fn", sp)), + TokenTree::Ident(Ident::new("main", sp)), + TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), + TokenTree::Group(Group::new(Delimiter::Brace, body)), + ]); + ret +} diff --git a/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.rs b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.rs new file mode 100644 index 000000000000..1a9d4601acaf --- /dev/null +++ b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.rs @@ -0,0 +1,10 @@ +// aux-build:edition-gated-async-move-syntax.rs +// edition: 2015 + +// Non-regression test for issue #89699, where a proc-macro emitting syntax only available in +// edition 2018 and up (`async move`) is used on edition 2015 + +extern crate edition_gated_async_move_syntax; + +#[edition_gated_async_move_syntax::foo] +fn foo() {} //~ ERROR `async move` blocks are only allowed in Rust 2018 or later diff --git a/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.stderr b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.stderr new file mode 100644 index 000000000000..a0dcc84eef8f --- /dev/null +++ b/tests/ui/proc-macro/edition-gated-async-move-syntax-issue89699.stderr @@ -0,0 +1,8 @@ +error: `async move` blocks are only allowed in Rust 2018 or later + --> $DIR/edition-gated-async-move-syntax-issue89699.rs:10:1 + | +LL | fn foo() {} + | ^^ + +error: aborting due to previous error + diff --git a/tests/ui/rmeta/emit-artifact-notifications.stderr b/tests/ui/rmeta/emit-artifact-notifications.stderr index b2f0aa7575c0..4f68a2d74ed3 100644 --- a/tests/ui/rmeta/emit-artifact-notifications.stderr +++ b/tests/ui/rmeta/emit-artifact-notifications.stderr @@ -1 +1 @@ -{"artifact":"$TEST_BUILD_DIR/rmeta/emit-artifact-notifications/libemit_artifact_notifications.rmeta","emit":"metadata"} +{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/rmeta/emit-artifact-notifications/libemit_artifact_notifications.rmeta","emit":"metadata"} diff --git a/tests/ui/rmeta/rmeta_meta_main.stderr b/tests/ui/rmeta/rmeta_meta_main.stderr index 0c6ed9afd358..a4af319e339d 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 6e65fae808de..fed853a7e1fd 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 51a572899f81..164ae79c254f 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 1a88f269347c..e109980e0bba 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 3b8a2b5c7bad..ed3650e5298d 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 6949a0a4a682..aef0e0e8e5ca 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 a91e47657ab9..78bb018cb4be 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 cddc1356194b..bbe5bae29bbe 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 Foo { - | - found type parameter + | - ------ this is the type of the `Self` literal + | | + | found type parameter LL | fn new(u: U) -> Foo { | - ------ expected `Foo` because of return type | | @@ -40,6 +42,10 @@ LL | | } found struct `Foo` = 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:: { + | ~~~~~~~~ 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 d628bd162083..f67a4ed78e29 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 ca9abc7e9068..1bab075dc9c1 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 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 2471b11c61ef..b12ad495e9fd 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 9ece31e76f00..457d77906468 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 LL + bar: cell::Cell | -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/non-existent-field-present-in-subfield.stderr b/tests/ui/suggestions/non-existent-field-present-in-subfield.stderr index cc991b915d33..65353ed923e0 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 cafddbe26242..9f185f5dd52b 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 c38c795e07ae..0db426588bd2 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-field-through-deref.fixed b/tests/ui/suggestions/suggest-field-through-deref.fixed new file mode 100644 index 000000000000..07ba3aa911f4 --- /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 000000000000..6e24b425e956 --- /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 000000000000..cc9fe2044c91 --- /dev/null +++ b/tests/ui/suggestions/suggest-field-through-deref.stderr @@ -0,0 +1,69 @@ +error[E0609]: no field `longname` on type `Arc` + --> $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>` + --> $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` + --> $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, ()>` + --> $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` + --> $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/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index 671f5efddd97..057e37dbe109 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -4,8 +4,8 @@ error[E0433]: failed to resolve: use of undeclared type `TryFrom` LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryFrom` | - = note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 = note: 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 + = note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 help: consider importing one of these items | LL + use core::convert::TryFrom; @@ -19,8 +19,8 @@ error[E0433]: failed to resolve: use of undeclared type `TryInto` LL | let _i: i16 = TryInto::try_into(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryInto` | - = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 = note: 'core::convert::TryInto' is included in the prelude starting in Edition 2021 + = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 help: consider importing one of these items | LL + use core::convert::TryInto; @@ -34,8 +34,8 @@ error[E0433]: failed to resolve: use of undeclared type `FromIterator` LL | let _v: Vec<_> = FromIterator::from_iter(&[1]); | ^^^^^^^^^^^^ use of undeclared type `FromIterator` | - = note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 = note: 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 + = note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 help: a trait with a similar name exists | LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]); diff --git a/tests/ui/suggestions/too-many-field-suggestions.stderr b/tests/ui/suggestions/too-many-field-suggestions.stderr index 63ad6fdb1699..ac5c8cb60ccd 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 12466868f002..fb3573ee2a4e 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 000000000000..4abce7b57d50 --- /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 Identity for T { + type Assoc = T; +} + +type Id = ::Assoc; + +type Five = Id>>>>; +type Ty = Five>>>>; + +trait Trait {} + +impl Trait for Ty {} +impl Trait for Ty {} + +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 000000000000..29a73e1a9679 --- /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(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 73d46c4df960..5d5f325e4b47 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: <::Assoc as std::marker::Sized> -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <::Assoc as std::marker::Sized> -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <::Assoc as std::marker::Sized> -WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <::Assoc as std::marker::Sized> error[E0119]: conflicting implementations of trait `Trait` for type `::Assoc` --> $DIR/trait_ref_is_knowable-norm-overflow.rs:17:1 | @@ -10,8 +6,6 @@ LL | impl Trait for T {} LL | struct LocalTy; LL | impl Trait for ::Assoc {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `::Assoc` - | - = note: upstream crates may add a new impl of trait `std::marker::Copy` for type `::Assoc` in future versions error: aborting due to previous error diff --git a/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.bad.stderr b/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.bad.stderr new file mode 100644 index 000000000000..e6c36129b285 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.bad.stderr @@ -0,0 +1,26 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/placeholders-dont-outlive-static.rs:6:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0310]: the placeholder type `!1_"T"` may not live long enough + --> $DIR/placeholders-dont-outlive-static.rs:13:5 + | +LL | foo(); + | ^^^^^ + | | + | the placeholder type `!1_"T"` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn bad() where !1_"T": 'static { + | +++++++++++++++++++++ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.good.stderr b/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.good.stderr new file mode 100644 index 000000000000..31441ef4db86 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.good.stderr @@ -0,0 +1,26 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/placeholders-dont-outlive-static.rs:6:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0310]: the placeholder type `!1_"T"` may not live long enough + --> $DIR/placeholders-dont-outlive-static.rs:19:5 + | +LL | foo(); + | ^^^^^ + | | + | the placeholder type `!1_"T"` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn good() where for T: 'static, !1_"T": 'static { + | +++++++++++++++++ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.rs b/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.rs new file mode 100644 index 000000000000..ae6866511e27 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/placeholders-dont-outlive-static.rs @@ -0,0 +1,22 @@ +// revisions: good bad + +//[good] known-bug: unknown +// `for T: 'static` doesn't imply itself when processing outlives obligations + +#![feature(non_lifetime_binders)] +//[bad]~^ WARN the feature `non_lifetime_binders` is incomplete + +fn foo() where for T: 'static {} + +#[cfg(bad)] +fn bad() { + foo(); + //[bad]~^ ERROR the placeholder type `!1_"T"` may not live long enough +} + +#[cfg(good)] +fn good() where for T: 'static { + foo(); +} + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.rs b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.rs index 5ff7089b993a..53957914e3ab 100644 --- a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.rs +++ b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.rs @@ -1,10 +1,9 @@ // edition:2021 -// check-pass +// known-bug: unknown // Checks that test_type_match code doesn't ICE when predicates have late-bound types #![feature(non_lifetime_binders)] -//~^ WARN is incomplete and may not be safe to use async fn walk2<'a, T: 'a>(_: T) where diff --git a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr index 3609bed28dff..ddb6a7987113 100644 --- a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr +++ b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr @@ -7,5 +7,20 @@ LL | #![feature(non_lifetime_binders)] = note: see issue #108185 for more information = note: `#[warn(incomplete_features)]` on by default -warning: 1 warning emitted +error[E0309]: the placeholder type `!1_"F"` may not live long enough + --> $DIR/type-match-with-late-bound.rs:11:1 + | +LL | async fn walk2<'a, T: 'a>(_: T) + | -- the placeholder type `!1_"F"` must be valid for the lifetime `'a` as defined here... +... +LL | {} + | ^^ ...so that the type `F` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | for F: 'a, !1_"F": 'a + | ~~~~~~~~~~~~ +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0309`. diff --git a/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr b/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr index e5a3c3f5cc4b..393350ea3f12 100644 --- a/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr +++ b/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr @@ -5,6 +5,8 @@ LL | impl OtherTrait for () {} | ---------------------- first implementation here LL | impl OtherTrait for T {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` + | + = note: this impl is reserved error: aborting due to previous error diff --git a/tests/ui/traits/trait-object-lifetime-default-note.rs b/tests/ui/traits/trait-object-lifetime-default-note.rs new file mode 100644 index 000000000000..31e3eb4ba415 --- /dev/null +++ b/tests/ui/traits/trait-object-lifetime-default-note.rs @@ -0,0 +1,16 @@ +trait A {} + +impl A for T {} + +fn main() { + let local = 0; //~ NOTE binding `local` declared here + let r = &local; //~ ERROR `local` does not live long enough + //~| NOTE borrowed value does not live long enough + //~| NOTE due to object lifetime defaults, `Box` actually means `Box<(dyn A + 'static)>` + require_box(Box::new(r)); + //~^ NOTE cast requires that `local` is borrowed for `'static` + + let _ = 0; +} //~ NOTE `local` dropped here while still borrowed + +fn require_box(_a: Box) {} diff --git a/tests/ui/traits/trait-object-lifetime-default-note.stderr b/tests/ui/traits/trait-object-lifetime-default-note.stderr new file mode 100644 index 000000000000..b6dd67538993 --- /dev/null +++ b/tests/ui/traits/trait-object-lifetime-default-note.stderr @@ -0,0 +1,19 @@ +error[E0597]: `local` does not live long enough + --> $DIR/trait-object-lifetime-default-note.rs:7:13 + | +LL | let local = 0; + | ----- binding `local` declared here +LL | let r = &local; + | ^^^^^^ borrowed value does not live long enough +... +LL | require_box(Box::new(r)); + | ----------- cast requires that `local` is borrowed for `'static` +... +LL | } + | - `local` dropped here while still borrowed + | + = note: due to object lifetime defaults, `Box` actually means `Box<(dyn A + 'static)>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs new file mode 100644 index 000000000000..79fb643eacd0 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs @@ -0,0 +1,36 @@ +#![deny(deref_into_dyn_supertrait)] +#![feature(trait_upcasting)] // remove this and the test compiles + +use std::ops::Deref; + +trait Bar {} +impl Bar for T {} + +trait Foo: Bar { + fn as_dyn_bar_u32<'a>(&self) -> &(dyn Bar + 'a); +} + +impl Foo for () { + fn as_dyn_bar_u32<'a>(&self) -> &(dyn Bar + 'a) { + self + } +} + +impl<'a> Deref for dyn Foo + 'a { + type Target = dyn Bar + 'a; + + fn deref(&self) -> &Self::Target { + self.as_dyn_bar_u32() + } +} + +fn take_dyn(x: &dyn Bar) -> T { + todo!() +} + +fn main() { + let x: &dyn Foo = &(); + let y = take_dyn(x); + let z: u32 = y; + //~^ ERROR mismatched types +} diff --git a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr new file mode 100644 index 000000000000..cfd6f7f175ff --- /dev/null +++ b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/inference-behavior-change-deref.rs:34:18 + | +LL | let z: u32 = y; + | --- ^ expected `u32`, found `i32` + | | + | expected due to this + | +help: you can convert an `i32` to a `u32` and panic if the converted value doesn't fit + | +LL | let z: u32 = y.try_into().unwrap(); + | ++++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs new file mode 100644 index 000000000000..b3f14a5f256e --- /dev/null +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs @@ -0,0 +1,18 @@ +#![deny(deref_into_dyn_supertrait)] + +use std::ops::Deref; + +trait Bar<'a> {} +trait Foo<'a>: Bar<'a> {} + +impl<'a> Deref for dyn Foo<'a> { + //~^ ERROR dyn Foo<'_>` implements `Deref` with supertrait `Bar<'_>` as target + //~| WARN this will change its meaning in a future release! + type Target = dyn Bar<'a>; + + fn deref(&self) -> &Self::Target { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr new file mode 100644 index 000000000000..250bcabc6982 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr @@ -0,0 +1,19 @@ +error: `dyn Foo<'_>` implements `Deref` with supertrait `Bar<'_>` as target + --> $DIR/migrate-lint-deny-regions.rs:8:1 + | +LL | impl<'a> Deref for dyn Foo<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | type Target = dyn Bar<'a>; + | -------------------------- target type is set here + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #89460 +note: the lint level is defined here + --> $DIR/migrate-lint-deny-regions.rs:1:9 + | +LL | #![deny(deref_into_dyn_supertrait)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs b/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs index d624187561ed..114d2c249da0 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs @@ -1,16 +1,14 @@ #![deny(deref_into_dyn_supertrait)] -extern crate core; - -use core::ops::Deref; +use std::ops::Deref; // issue 89190 trait A {} trait B: A {} impl<'a> Deref for dyn 'a + B { - //~^ ERROR `(dyn B + 'a)` implements `Deref` with supertrait `A` as target - //~| WARN this was previously accepted by the compiler but is being phased out; + //~^ ERROR `dyn B` implements `Deref` with supertrait `A` as target + //~| WARN this will change its meaning in a future release! type Target = dyn A; fn deref(&self) -> &Self::Target { diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr index 4533b1163425..c8b683c23c18 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr @@ -1,5 +1,5 @@ -error: `(dyn B + 'a)` implements `Deref` with supertrait `A` as target - --> $DIR/migrate-lint-deny.rs:11:1 +error: `dyn B` implements `Deref` with supertrait `A` as target + --> $DIR/migrate-lint-deny.rs:9:1 | LL | impl<'a> Deref for dyn 'a + B { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | impl<'a> Deref for dyn 'a + B { LL | type Target = dyn A; | -------------------- target type is set here | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = warning: this will change its meaning in a future release! = note: for more information, see issue #89460 note: the lint level is defined here --> $DIR/migrate-lint-deny.rs:1:9 diff --git a/tests/ui/treat-err-as-bug/delay_span_bug.rs b/tests/ui/treat-err-as-bug/delay_span_bug.rs index 533c8d1ec8f5..f667f8eb9962 100644 --- a/tests/ui/treat-err-as-bug/delay_span_bug.rs +++ b/tests/ui/treat-err-as-bug/delay_span_bug.rs @@ -1,7 +1,7 @@ // compile-flags: -Ztreat-err-as-bug // failure-status: 101 // error-pattern: aborting due to `-Z treat-err-as-bug=1` -// error-pattern: [trigger_delay_span_bug] triggering a delay span bug +// error-pattern: [trigger_delay_span_bug] triggering a delay span bug for testing incremental // normalize-stderr-test "note: .*\n\n" -> "" // normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> "" // rustc-env:RUST_BACKTRACE=0 diff --git a/tests/ui/treat-err-as-bug/delay_span_bug.stderr b/tests/ui/treat-err-as-bug/delay_span_bug.stderr index 22c6175048a6..06a31ae86b25 100644 --- a/tests/ui/treat-err-as-bug/delay_span_bug.stderr +++ b/tests/ui/treat-err-as-bug/delay_span_bug.stderr @@ -7,5 +7,5 @@ LL | fn main() {} error: the compiler unexpectedly panicked. this is a bug. query stack during panic: -#0 [trigger_delay_span_bug] triggering a delay span bug +#0 [trigger_delay_span_bug] triggering a delay span bug for testing incremental end of query stack diff --git a/tests/ui/tuple/index-invalid.stderr b/tests/ui/tuple/index-invalid.stderr index 8d22f458a6c6..ae2c275f52cd 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 a1bcdfaedbc6..a267e41b1bc3 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 7d7c5cd7892e..96090435d06b 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 62f66914ee33..6632a3450e5d 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/issue-78450.rs b/tests/ui/type-alias-impl-trait/issue-78450.rs index 2a984c1ed713..236e9f4e88ce 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 7d72c9f811af..c4ad8434ed14 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 31fbef9f78f8..b966ca4bff0e 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 { type Opaque1; diff --git a/tests/ui/typeck/issue-105946.stderr b/tests/ui/typeck/issue-105946.stderr index 26c3b7fbc841..2220271e5812 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-114529-illegal-break-with-value.rs b/tests/ui/typeck/issue-114529-illegal-break-with-value.rs index 613d1b6343a3..353a935e3731 100644 --- a/tests/ui/typeck/issue-114529-illegal-break-with-value.rs +++ b/tests/ui/typeck/issue-114529-illegal-break-with-value.rs @@ -17,4 +17,10 @@ fn main() { }; 51 }]; + + while true { + break (|| { //~ ERROR `break` with value from a `while` loop + let local = 9; + }); + } } diff --git a/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr b/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr index 4d6c27bbbd03..731f234c162a 100644 --- a/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr +++ b/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr @@ -24,6 +24,21 @@ help: use `break` on its own without a value inside this `while` loop LL | break; | ~~~~~ -error: aborting due to 2 previous errors +error[E0571]: `break` with value from a `while` loop + --> $DIR/issue-114529-illegal-break-with-value.rs:22:9 + | +LL | while true { + | ---------- you can't `break` with a value in a `while` loop +LL | / break (|| { +LL | | let local = 9; +LL | | }); + | |__________^ can only break with a value inside `loop` or breakable block + | +help: use `break` on its own without a value inside this `while` loop + | +LL | break; + | ~~~~~ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0571`. 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 4be4c91dfc2c..e1091c8f5ca0 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(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(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(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(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(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(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(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(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 db85919afcb5..7ed9cb103798 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 003c630790d2..2278450a6d8e 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 0a7d30b615a6..16afac4bd6bc 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 547cffffa2ee..2bc8453dda74 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 b50241fb1a05..b990b5f951f2 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 4b7016def9d6..94bb44d2cf50 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 58b1f5cb0786..efe4987bd025 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 58b1f5cb0786..efe4987bd025 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 20a88c356108..f563118e8ac0 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/tests/ui/unused-crate-deps/deny-cmdline-json-silent.stderr b/tests/ui/unused-crate-deps/deny-cmdline-json-silent.stderr index 595619f3a8a4..f6594fdaa911 100644 --- a/tests/ui/unused-crate-deps/deny-cmdline-json-silent.stderr +++ b/tests/ui/unused-crate-deps/deny-cmdline-json-silent.stderr @@ -1 +1 @@ -{"lint_level":"deny","unused_extern_names":["bar"]} +{"$message_type":"unused_extern","lint_level":"deny","unused_extern_names":["bar"]} diff --git a/tests/ui/unused-crate-deps/deny-cmdline-json.stderr b/tests/ui/unused-crate-deps/deny-cmdline-json.stderr index 595619f3a8a4..f6594fdaa911 100644 --- a/tests/ui/unused-crate-deps/deny-cmdline-json.stderr +++ b/tests/ui/unused-crate-deps/deny-cmdline-json.stderr @@ -1 +1 @@ -{"lint_level":"deny","unused_extern_names":["bar"]} +{"$message_type":"unused_extern","lint_level":"deny","unused_extern_names":["bar"]} diff --git a/tests/ui/unused-crate-deps/warn-cmdline-json.stderr b/tests/ui/unused-crate-deps/warn-cmdline-json.stderr index 98dbd763927e..ef99f986eb1d 100644 --- a/tests/ui/unused-crate-deps/warn-cmdline-json.stderr +++ b/tests/ui/unused-crate-deps/warn-cmdline-json.stderr @@ -1 +1 @@ -{"lint_level":"warn","unused_extern_names":["bar"]} +{"$message_type":"unused_extern","lint_level":"warn","unused_extern_names":["bar"]} diff --git a/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs b/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs index 69ed498e7e1c..4f8b3508398f 100644 --- a/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs +++ b/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs @@ -18,6 +18,11 @@ pub struct TupleStruct(pub usize, pub &'static str); #[derive(Copy, Clone)] pub struct StructWithFields { + pub foo: isize, +} + +#[derive(Copy, Clone)] +pub struct StructWithPrivFields { foo: isize, } diff --git a/tests/ui/xcrate/xcrate-unit-struct.rs b/tests/ui/xcrate/xcrate-unit-struct.rs index c99cf77ce7a4..bc14cd8d4c01 100644 --- a/tests/ui/xcrate/xcrate-unit-struct.rs +++ b/tests/ui/xcrate/xcrate-unit-struct.rs @@ -8,5 +8,7 @@ extern crate xcrate_unit_struct; fn main() { let _ = xcrate_unit_struct::StructWithFields; //~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithFields` + let _ = xcrate_unit_struct::StructWithPrivFields; + //~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithPrivFields` let _ = xcrate_unit_struct::Struct; } diff --git a/tests/ui/xcrate/xcrate-unit-struct.stderr b/tests/ui/xcrate/xcrate-unit-struct.stderr index cee314568887..7365170b69ea 100644 --- a/tests/ui/xcrate/xcrate-unit-struct.stderr +++ b/tests/ui/xcrate/xcrate-unit-struct.stderr @@ -9,6 +9,17 @@ LL | let _ = xcrate_unit_struct::StructWithFields; LL | pub struct StructWithFields { | --------------------------- `xcrate_unit_struct::StructWithFields` defined here -error: aborting due to previous error +error[E0423]: expected value, found struct `xcrate_unit_struct::StructWithPrivFields` + --> $DIR/xcrate-unit-struct.rs:11:13 + | +LL | let _ = xcrate_unit_struct::StructWithPrivFields; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/xcrate_unit_struct.rs:25:1 + | +LL | pub struct StructWithPrivFields { + | ------------------------------- `xcrate_unit_struct::StructWithPrivFields` defined here + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0423`. diff --git a/triagebot.toml b/triagebot.toml index 4c8c1c59beba..a72338d19505 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"]