diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e83bbf643fee..93316b9cff7b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,6 +91,17 @@ jobs: # Check the `calculate_matrix` job to see how is the matrix defined. include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }} steps: + - name: Install cargo in AWS CodeBuild + if: matrix.codebuild + run: | + # Check if cargo is installed + if ! command -v cargo &> /dev/null; then + echo "Cargo not found, installing Rust..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal + # Make cargo available in PATH + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + fi + - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -125,9 +136,6 @@ jobs: # which then uses log commands to actually set them. EXTRA_VARIABLES: ${{ toJson(matrix.env) }} - - name: setup upstream remote - run: src/ci/scripts/setup-upstream-remote.sh - - name: ensure the channel matches the target branch run: src/ci/scripts/verify-channel.sh @@ -168,6 +176,8 @@ jobs: run: src/ci/scripts/install-ninja.sh - name: enable ipv6 on Docker + # Don't run on codebuild because systemctl is not available + if: ${{ !matrix.codebuild }} run: src/ci/scripts/enable-docker-ipv6.sh # Disable automatic line ending conversion (again). On Windows, when we're diff --git a/Cargo.lock b/Cargo.lock index 97a90a44a398e..4fc3cbcfd3ab3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,7 +225,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow 0.7.4", + "winnow 0.7.6", ] [[package]] @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -496,9 +496,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive", @@ -516,9 +516,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -546,12 +546,14 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.87" +version = "0.1.88" dependencies = [ "anstream", + "askama", "cargo_metadata 0.18.1", "clippy_config", "clippy_lints", + "clippy_lints_internal", "clippy_utils", "color-print", "filetime", @@ -562,7 +564,6 @@ dependencies = [ "pulldown-cmark 0.11.3", "quote", "regex", - "rinja", "rustc_tools_util 0.4.2", "serde", "serde_json", @@ -577,7 +578,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.87" +version = "0.1.88" dependencies = [ "clippy_utils", "itertools", @@ -602,7 +603,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.87" +version = "0.1.88" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -610,12 +611,9 @@ dependencies = [ "clippy_utils", "itertools", "quine-mc_cluskey", - "regex", "regex-syntax 0.8.5", "semver", "serde", - "serde_json", - "tempfile", "toml 0.7.8", "unicode-normalization", "unicode-script", @@ -623,9 +621,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "clippy_lints_internal" +version = "0.0.1" +dependencies = [ + "clippy_config", + "clippy_utils", + "regex", + "rustc-semver", +] + [[package]] name = "clippy_utils" -version = "0.1.87" +version = "0.1.88" dependencies = [ "arrayvec", "itertools", @@ -729,6 +737,7 @@ dependencies = [ "libc", "miow", "miropt-test-tools", + "rayon", "regex", "rustfix", "semver", @@ -798,9 +807,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1910,9 +1919,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488" dependencies = [ "jiff-static", "log", @@ -1923,9 +1932,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19" dependencies = [ "proc-macro2", "quote", @@ -1979,9 +1988,9 @@ dependencies = [ [[package]] name = "jsonpath-rust" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b0231bb404a6cd6c8f0ab41b907049063a089fc02aa7636cc5cd9a4d87364c9" +checksum = "6a37c2c87b8d16e788ce359660fead0ea5f4ed29ff400d55be74a4e01d1817d9" dependencies = [ "pest", "pest_derive", @@ -2113,9 +2122,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -2244,22 +2253,6 @@ dependencies = [ "libc", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minifier" version = "0.3.5" @@ -3084,45 +3077,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "rinja" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" -dependencies = [ - "itoa", - "rinja_derive", -] - -[[package]] -name = "rinja_derive" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" -dependencies = [ - "basic-toml", - "memchr", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "rinja_parser", - "rustc-hash 2.1.1", - "serde", - "syn 2.0.100", -] - -[[package]] -name = "rinja_parser" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" -dependencies = [ - "memchr", - "nom", - "serde", -] - [[package]] name = "run_make_support" version = "0.2.0" @@ -3196,6 +3150,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rustc-semver" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be1bdc7edf596692617627bbfeaba522131b18e06ca4df2b6b689e3c5d5ce84" + [[package]] name = "rustc-stable-hash" version = "0.1.2" @@ -3740,6 +3700,9 @@ dependencies = [ [[package]] name = "rustc_fs_util" version = "0.0.0" +dependencies = [ + "tempfile", +] [[package]] name = "rustc_graphviz" @@ -4052,7 +4015,6 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "tempfile", "tracing", ] @@ -4199,7 +4161,6 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", - "rustc_serialize", "rustc_type_ir", "rustc_type_ir_macros", "tracing", @@ -4562,6 +4523,7 @@ dependencies = [ "rustc_hir", "rustc_middle", "rustc_span", + "smallvec", "tracing", ] @@ -4807,14 +4769,14 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" dependencies = [ - "self_cell 1.1.0", + "self_cell 1.2.0", ] [[package]] name = "self_cell" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semver" @@ -6415,9 +6377,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] diff --git a/INSTALL.md b/INSTALL.md index 30e08201d6dfb..98eb825cd10f6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -75,8 +75,31 @@ See [the rustc-dev-guide for more info][sysllvm]. 2. Configure the build settings: + If you're unsure which build configurations to use and need a good default, you + can run the interactive `x.py setup` command. This will guide you through selecting + a config profile, setting up the LSP, configuring a Git hook, etc. + + With `configure` script, you can handle multiple configurations in a single + command which is useful to create complex/advanced config files. For example: + ```sh - ./configure + ./configure --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.lto=thin \ + --set rust.codegen-units=1 ``` If you plan to use `x.py install` to create an installation, you can either diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1532ca77f713e..8986430141be4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1927,7 +1927,7 @@ impl AttrArgs { } /// Delimited arguments, as used in `#[attr()/[]/{}]` or `mac!()/[]/{}`. -#[derive(Clone, Encodable, Decodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct DelimArgs { pub dspan: DelimSpan, pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs @@ -1942,18 +1942,6 @@ impl DelimArgs { } } -impl HashStable for DelimArgs -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - let DelimArgs { dspan, delim, tokens } = self; - dspan.hash_stable(ctx, hasher); - delim.hash_stable(ctx, hasher); - tokens.hash_stable(ctx, hasher); - } -} - /// Represents a macro definition. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MacroDef { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index f104400a572ba..f165c4ddcdd4d 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -416,10 +416,7 @@ impl MetaItem { // This path is currently unreachable in the test suite. unreachable!() } - Some(TokenTree::Token( - Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, - _, - )) => { + Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => { panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt); } _ => return None, diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 294c6c9ba7a50..1471262d2d676 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(deny(warnings))) @@ -14,7 +15,6 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 055481f5d8789..54781e8235e2f 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -32,6 +32,18 @@ pub enum InvisibleOrigin { ProcMacro, } +impl InvisibleOrigin { + // Should the parser skip these invisible delimiters? Ideally this function + // will eventually disappear and no invisible delimiters will be skipped. + #[inline] + pub fn skip(&self) -> bool { + match self { + InvisibleOrigin::MetaVar(_) => false, + InvisibleOrigin::ProcMacro => true, + } + } +} + impl PartialEq for InvisibleOrigin { #[inline] fn eq(&self, _other: &InvisibleOrigin) -> bool { @@ -125,8 +137,7 @@ impl Delimiter { pub fn skip(&self) -> bool { match self { Delimiter::Parenthesis | Delimiter::Bracket | Delimiter::Brace => false, - Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) => false, - Delimiter::Invisible(InvisibleOrigin::ProcMacro) => true, + Delimiter::Invisible(origin) => origin.skip(), } } @@ -140,6 +151,24 @@ impl Delimiter { _ => false, } } + + pub fn as_open_token_kind(&self) -> TokenKind { + match *self { + Delimiter::Parenthesis => OpenParen, + Delimiter::Brace => OpenBrace, + Delimiter::Bracket => OpenBracket, + Delimiter::Invisible(origin) => OpenInvisible(origin), + } + } + + pub fn as_close_token_kind(&self) -> TokenKind { + match *self { + Delimiter::Parenthesis => CloseParen, + Delimiter::Brace => CloseBrace, + Delimiter::Bracket => CloseBracket, + Delimiter::Invisible(origin) => CloseInvisible(origin), + } + } } // Note that the suffix is *not* considered when deciding the `LitKind` in this @@ -194,9 +223,9 @@ impl Lit { match token.uninterpolate().kind { Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)), Literal(token_lit) => Some(token_lit), - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Literal | MetaVarKind::Expr { .. }, - ))) => { + )) => { // Unreachable with the current test suite. panic!("from_token metavar"); } @@ -426,10 +455,22 @@ pub enum TokenKind { Question, /// Used by proc macros for representing lifetimes, not generated by lexer right now. SingleQuote, - /// An opening delimiter (e.g., `{`). - OpenDelim(Delimiter), - /// A closing delimiter (e.g., `}`). - CloseDelim(Delimiter), + /// `(` + OpenParen, + /// `)` + CloseParen, + /// `{` + OpenBrace, + /// `}` + CloseBrace, + /// `[` + OpenBracket, + /// `]` + CloseBracket, + /// Invisible opening delimiter, produced by a macro. + OpenInvisible(InvisibleOrigin), + /// Invisible closing delimiter, produced by a macro. + CloseInvisible(InvisibleOrigin), /* Literals */ Literal(Lit), @@ -530,6 +571,37 @@ impl TokenKind { pub fn should_end_const_arg(&self) -> bool { matches!(self, Gt | Ge | Shr | ShrEq) } + + pub fn is_delim(&self) -> bool { + self.open_delim().is_some() || self.close_delim().is_some() + } + + pub fn open_delim(&self) -> Option { + match *self { + OpenParen => Some(Delimiter::Parenthesis), + OpenBrace => Some(Delimiter::Brace), + OpenBracket => Some(Delimiter::Bracket), + OpenInvisible(origin) => Some(Delimiter::Invisible(origin)), + _ => None, + } + } + + pub fn close_delim(&self) -> Option { + match *self { + CloseParen => Some(Delimiter::Parenthesis), + CloseBrace => Some(Delimiter::Brace), + CloseBracket => Some(Delimiter::Bracket), + CloseInvisible(origin) => Some(Delimiter::Invisible(origin)), + _ => None, + } + } + + pub fn is_close_delim_or_eof(&self) -> bool { + match self { + CloseParen | CloseBrace | CloseBracket | CloseInvisible(_) | Eof => true, + _ => false, + } + } } impl Token { @@ -559,7 +631,8 @@ impl Token { | DotDotDot | DotDotEq | Comma | Semi | Colon | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | SingleQuote => true, - OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) + OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket + | OpenInvisible(_) | CloseInvisible(_) | Literal(..) | DocComment(..) | Ident(..) | NtIdent(..) | Lifetime(..) | NtLifetime(..) | Eof => false, } } @@ -573,11 +646,12 @@ impl Token { /// **NB**: Take care when modifying this function, since it will change /// the stable set of tokens that are allowed to match an expr nonterminal. pub fn can_begin_expr(&self) -> bool { - use Delimiter::*; match self.uninterpolate().kind { Ident(name, is_raw) => ident_can_begin_expr(name, self.span, is_raw), // value name or keyword - OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block + OpenParen | // tuple + OpenBrace | // block + OpenBracket | // array Literal(..) | // literal Bang | // operator not Minus | // unary minus @@ -591,12 +665,12 @@ impl Token { PathSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Block | MetaVarKind::Expr { .. } | MetaVarKind::Literal | MetaVarKind::Path - ))) => true, + )) => true, _ => false, } } @@ -608,8 +682,8 @@ impl Token { match &self.uninterpolate().kind { // box, ref, mut, and other identifiers (can stricten) Ident(..) | NtIdent(..) | - OpenDelim(Delimiter::Parenthesis) | // tuple pattern - OpenDelim(Delimiter::Bracket) | // slice pattern + OpenParen | // tuple pattern + OpenBracket | // slice pattern And | // reference Minus | // negative literal AndAnd | // double reference @@ -620,14 +694,14 @@ impl Token { Lt | // path (UFCS constant) Shl => true, // path (double UFCS) Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | MetaVarKind::Literal | MetaVarKind::Meta { .. } | MetaVarKind::Pat(_) | MetaVarKind::Path | MetaVarKind::Ty { .. } - ))) => true, + )) => true, _ => false, } } @@ -637,8 +711,8 @@ impl Token { match self.uninterpolate().kind { Ident(name, is_raw) => ident_can_begin_type(name, self.span, is_raw), // type name or keyword - OpenDelim(Delimiter::Parenthesis) | // tuple - OpenDelim(Delimiter::Bracket) | // array + OpenParen | // tuple + OpenBracket | // array Bang | // never Star | // raw pointer And | // reference @@ -647,10 +721,10 @@ impl Token { Lifetime(..) | // lifetime bound in trait object Lt | Shl | // associated path PathSep => true, // global path - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Ty { .. } | MetaVarKind::Path - ))) => true, + )) => true, // 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, @@ -660,11 +734,11 @@ impl Token { /// Returns `true` if the token can appear at the start of a const param. pub fn can_begin_const_arg(&self) -> bool { match self.kind { - OpenDelim(Delimiter::Brace) | Literal(..) | Minus => true, + OpenBrace | Literal(..) | Minus => true, Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal, - ))) => true, + )) => true, _ => false, } } @@ -711,7 +785,7 @@ impl Token { match self.uninterpolate().kind { Literal(..) | Minus => true, Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind { MetaVarKind::Literal => true, MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => { can_begin_literal_maybe_minus @@ -725,7 +799,7 @@ impl Token { pub fn can_begin_string_literal(&self) -> bool { match self.uninterpolate().kind { Literal(..) => true, - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind { MetaVarKind::Literal => true, MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal, _ => false, @@ -892,7 +966,7 @@ impl Token { /// from an expanded metavar? pub fn is_metavar_seq(&self) -> Option { match self.kind { - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => Some(kind), + OpenInvisible(InvisibleOrigin::MetaVar(kind)) => Some(kind), _ => None, } } @@ -970,7 +1044,8 @@ impl Token { Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | PlusEq | MinusEq | StarEq | SlashEq | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | DotDotDot | DotDotEq | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question - | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..) + | OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket + | OpenInvisible(_) | CloseInvisible(_) | Literal(..) | Ident(..) | NtIdent(..) | Lifetime(..) | NtLifetime(..) | DocComment(..) | Eof, _, ) => { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index fc32c4efce56a..c009abd729da9 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -5,7 +5,7 @@ use rustc_ast::*; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_hir::{self as hir, HirId, IsAnonInPath, PredicateOrigin}; +use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; @@ -1868,7 +1868,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } GenericParamKind::Lifetime => { let lt_id = self.next_node_id(); - let lifetime = self.new_named_lifetime(id, lt_id, ident, IsAnonInPath::No); + let lifetime = + self.new_named_lifetime(id, lt_id, ident, LifetimeSource::Other, ident.into()); hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime, bounds, @@ -1901,7 +1902,11 @@ impl<'hir> LoweringContext<'_, 'hir> { }), WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => { hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { - lifetime: self.lower_lifetime(lifetime), + lifetime: self.lower_lifetime( + lifetime, + LifetimeSource::Other, + lifetime.ident.into(), + ), bounds: self.lower_param_bounds( bounds, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c40987541f4a5..1e14b4d6723f4 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -32,12 +32,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end @@ -54,8 +54,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ - self as hir, ConstArg, GenericArg, HirId, IsAnonInPath, ItemLocalMap, LangItem, ParamName, - TraitCandidate, + self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, LifetimeSource, + LifetimeSyntax, ParamName, TraitCandidate, }; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; @@ -916,7 +916,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs { - DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.clone() } + args.clone() } /// Lower an associated item constraint. @@ -1079,7 +1079,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx: ImplTraitContext, ) -> hir::GenericArg<'hir> { match arg { - ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)), + ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime( + lt, + LifetimeSource::Path { with_angle_brackets: true }, + lt.ident.into(), + )), ast::GenericArg::Type(ty) => { // We cannot just match on `TyKind::Infer` as `(_)` is represented as // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer` @@ -1198,35 +1202,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { - let region = region.unwrap_or_else(|| { - let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = - self.resolver.get_lifetime_res(t.id) - { - debug_assert_eq!(start.plus(1), end); - start - } else { - self.next_node_id() - }; - let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); - Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } - }); - let lifetime = self.lower_lifetime(®ion); + let lifetime = self.lower_ty_direct_lifetime(t, *region); hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)) } TyKind::PinnedRef(region, mt) => { - let region = region.unwrap_or_else(|| { - let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = - self.resolver.get_lifetime_res(t.id) - { - debug_assert_eq!(start.plus(1), end); - start - } else { - self.next_node_id() - }; - let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); - Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } - }); - let lifetime = self.lower_lifetime(®ion); + let lifetime = self.lower_ty_direct_lifetime(t, *region); let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)); let span = self.lower_span(t.span); let arg = hir::Ty { kind, span, hir_id: self.next_id() }; @@ -1302,7 +1282,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { - lifetime_bound = Some(this.lower_lifetime(lifetime)); + lifetime_bound = Some(this.lower_lifetime( + lifetime, + LifetimeSource::Other, + lifetime.ident.into(), + )); } None } @@ -1393,6 +1377,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) } } + fn lower_ty_direct_lifetime( + &mut self, + t: &Ty, + region: Option, + ) -> &'hir hir::Lifetime { + let (region, syntax) = match region { + Some(region) => (region, region.ident.into()), + + None => { + let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = + self.resolver.get_lifetime_res(t.id) + { + debug_assert_eq!(start.plus(1), end); + start + } else { + self.next_node_id() + }; + let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); + let region = Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }; + (region, LifetimeSyntax::Hidden) + } + }; + self.lower_lifetime(®ion, LifetimeSource::Reference, syntax) + } + /// Lowers a `ReturnPositionOpaqueTy` (`-> impl Trait`) or a `TypeAliasesOpaqueTy` (`type F = /// impl Trait`): this creates the associated Opaque Type (TAIT) definition and then returns a /// HIR type that references the TAIT. @@ -1474,9 +1483,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { precise_capturing_args: &[PreciseCapturingArg], ) -> &'hir [hir::PreciseCapturingArg<'hir>] { self.arena.alloc_from_iter(precise_capturing_args.iter().map(|arg| match arg { - PreciseCapturingArg::Lifetime(lt) => { - hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt)) - } + PreciseCapturingArg::Lifetime(lt) => hir::PreciseCapturingArg::Lifetime( + self.lower_lifetime(lt, LifetimeSource::PreciseCapturing, lt.ident.into()), + ), PreciseCapturingArg::Arg(path, id) => { let [segment] = path.segments.as_slice() else { panic!(); @@ -1739,9 +1748,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::GenericBound<'hir> { match tpb { GenericBound::Trait(p) => hir::GenericBound::Trait(self.lower_poly_trait_ref(p, itctx)), - GenericBound::Outlives(lifetime) => { - hir::GenericBound::Outlives(self.lower_lifetime(lifetime)) - } + GenericBound::Outlives(lifetime) => hir::GenericBound::Outlives(self.lower_lifetime( + lifetime, + LifetimeSource::OutlivesBound, + lifetime.ident.into(), + )), GenericBound::Use(args, span) => hir::GenericBound::Use( self.lower_precise_capturing_args(args), self.lower_span(*span), @@ -1749,12 +1760,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_lifetime(&mut self, l: &Lifetime) -> &'hir hir::Lifetime { - self.new_named_lifetime(l.id, l.id, l.ident, IsAnonInPath::No) + fn lower_lifetime( + &mut self, + l: &Lifetime, + source: LifetimeSource, + syntax: LifetimeSyntax, + ) -> &'hir hir::Lifetime { + self.new_named_lifetime(l.id, l.id, l.ident, source, syntax) } - fn lower_lifetime_anon_in_path(&mut self, id: NodeId, span: Span) -> &'hir hir::Lifetime { - self.new_named_lifetime(id, id, Ident::new(kw::UnderscoreLifetime, span), IsAnonInPath::Yes) + fn lower_lifetime_hidden_in_path( + &mut self, + id: NodeId, + span: Span, + with_angle_brackets: bool, + ) -> &'hir hir::Lifetime { + self.new_named_lifetime( + id, + id, + Ident::new(kw::UnderscoreLifetime, span), + LifetimeSource::Path { with_angle_brackets }, + LifetimeSyntax::Hidden, + ) } #[instrument(level = "debug", skip(self))] @@ -1763,7 +1790,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { id: NodeId, new_id: NodeId, ident: Ident, - is_anon_in_path: IsAnonInPath, + source: LifetimeSource, + syntax: LifetimeSyntax, ) -> &'hir hir::Lifetime { let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error); let res = match res { @@ -1787,17 +1815,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } }; - #[cfg(debug_assertions)] - if is_anon_in_path == IsAnonInPath::Yes { - debug_assert_eq!(ident.name, kw::UnderscoreLifetime); - } - debug!(?res); self.arena.alloc(hir::Lifetime::new( self.lower_node_id(new_id), self.lower_ident(ident), res, - is_anon_in_path, + source, + syntax, )) } @@ -2389,7 +2413,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.next_id(), Ident::new(kw::UnderscoreLifetime, self.lower_span(span)), hir::LifetimeKind::ImplicitObjectLifetimeDefault, - IsAnonInPath::No, + LifetimeSource::Other, + LifetimeSyntax::Hidden, ); debug!("elided_dyn_bound: r={:?}", r); self.arena.alloc(r) diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 0bd65aec10f49..fabe40a9d0413 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,11 +1,10 @@ use std::sync::Arc; use rustc_ast::{self as ast, *}; -use rustc_hir as hir; -use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, PartialRes, Res}; use rustc_hir::def_id::DefId; -use rustc_middle::span_bug; +use rustc_hir::{self as hir, GenericArg}; +use rustc_middle::{span_bug, ty}; use rustc_session::parse::add_feature_diagnostics; use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; @@ -433,23 +432,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Note: these spans are used for diagnostics when they can't be inferred. // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label - let elided_lifetime_span = if generic_args.span.is_empty() { + let (elided_lifetime_span, with_angle_brackets) = if generic_args.span.is_empty() { // If there are no brackets, use the identifier span. // HACK: we use find_ancestor_inside to properly suggest elided spans in paths // originating from macros, since the segment's span might be from a macro arg. - segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span) + (segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span), false) } else if generic_args.is_empty() { // If there are brackets, but not generic arguments, then use the opening bracket - generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)) + (generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)), true) } else { // Else use an empty span right after the opening bracket. - generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo() + (generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(), true) }; generic_args.args.insert_many( 0, (start..end).map(|id| { - let l = self.lower_lifetime_anon_in_path(id, elided_lifetime_span); + let l = self.lower_lifetime_hidden_in_path( + id, + elided_lifetime_span, + with_angle_brackets, + ); GenericArg::Lifetime(l) }), ); @@ -590,14 +593,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// lowering of `async Fn()` bounds to desugar to another trait like `LendingFn`. fn map_trait_to_async_trait(&self, def_id: DefId) -> Option { let lang_items = self.tcx.lang_items(); - if Some(def_id) == lang_items.fn_trait() { - lang_items.async_fn_trait() - } else if Some(def_id) == lang_items.fn_mut_trait() { - lang_items.async_fn_mut_trait() - } else if Some(def_id) == lang_items.fn_once_trait() { - lang_items.async_fn_once_trait() - } else { - None + match self.tcx.fn_trait_kind_from_def_id(def_id)? { + ty::ClosureKind::Fn => lang_items.async_fn_trait(), + ty::ClosureKind::FnMut => lang_items.async_fn_mut_trait(), + ty::ClosureKind::FnOnce => lang_items.async_fn_once_trait(), } } } diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 093199cf34212..7956057f88ee7 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,11 +4,11 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 0985ebf945bbd..62a50c73855fc 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -770,12 +770,12 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.bclose(span, empty); } delim => { - let token_str = self.token_kind_to_string(&token::OpenDelim(delim)); + let token_str = self.token_kind_to_string(&delim.as_open_token_kind()); self.word(token_str); self.ibox(0); self.print_tts(tts, convert_dollar_crate); self.end(); - let token_str = self.token_kind_to_string(&token::CloseDelim(delim)); + let token_str = self.token_kind_to_string(&delim.as_close_token_kind()); self.word(token_str); } } @@ -799,7 +799,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere has_bang, Some(*ident), macro_def.body.delim, - ¯o_def.body.tokens.clone(), + ¯o_def.body.tokens, true, sp, ); @@ -932,14 +932,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere token::RArrow => "->".into(), token::LArrow => "<-".into(), token::FatArrow => "=>".into(), - token::OpenDelim(Delimiter::Parenthesis) => "(".into(), - token::CloseDelim(Delimiter::Parenthesis) => ")".into(), - token::OpenDelim(Delimiter::Bracket) => "[".into(), - token::CloseDelim(Delimiter::Bracket) => "]".into(), - token::OpenDelim(Delimiter::Brace) => "{".into(), - token::CloseDelim(Delimiter::Brace) => "}".into(), - token::OpenDelim(Delimiter::Invisible(_)) - | token::CloseDelim(Delimiter::Invisible(_)) => "".into(), + token::OpenParen => "(".into(), + token::CloseParen => ")".into(), + token::OpenBracket => "[".into(), + token::CloseBracket => "]".into(), + token::OpenBrace => "{".into(), + token::CloseBrace => "}".into(), + token::OpenInvisible(_) | token::CloseInvisible(_) => "".into(), token::Pound => "#".into(), token::Dollar => "$".into(), token::Question => "?".into(), @@ -1469,7 +1468,7 @@ impl<'a> State<'a> { true, None, m.args.delim, - &m.args.tokens.clone(), + &m.args.tokens, true, m.span(), ); diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index c61b44b273de5..679fe935484e8 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -1,7 +1,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 63597b37cb5ee..55c3df003fe16 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use std::ops::Deref; use std::sync::LazyLock; -use rustc_ast::{self as ast, DelimArgs}; +use rustc_ast as ast; use rustc_attr_data_structures::AttributeKind; use rustc_errors::{DiagCtxtHandle, Diagnostic}; use rustc_feature::Features; @@ -315,11 +315,7 @@ impl<'sess> AttributeParser<'sess> { fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { match args { ast::AttrArgs::Empty => AttrArgs::Empty, - ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs { - dspan: args.dspan, - delim: args.delim, - tokens: args.tokens.clone(), - }), + ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()), // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can extract literals to handle // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 249e71ef70dcf..b9692c01e2c42 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -77,8 +77,8 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 384fae59873f9..40aa39711d3e2 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -430,9 +430,7 @@ impl<'a> MetaItemListParserContext<'a> { let span = span.with_hi(segments.last().unwrap().span.hi()); Some(AttrPath { segments: segments.into_boxed_slice(), span }) } - TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => { - None - } + TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None, _ => { // malformed attributes can get here. We can't crash, but somewhere else should've // already warned for this. diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 134f30ed6f54c..0de4bd67f0ce7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -49,7 +49,7 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfo::RelateTys { expected, found } } - pub(crate) fn report_error( + pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -68,7 +68,7 @@ impl<'tcx> UniverseInfo<'tcx> { mbcx.buffer_error(err); } UniverseInfo::TypeOp(ref type_op_info) => { - type_op_info.report_error(mbcx, placeholder, error_element, cause); + type_op_info.report_erroneous_element(mbcx, placeholder, error_element, cause); } UniverseInfo::Other => { // FIXME: This error message isn't great, but it doesn't show @@ -145,8 +145,11 @@ pub(crate) trait TypeOpInfo<'tcx> { error_region: Option>, ) -> Option>; + /// Constraints require that `error_element` appear in the + /// values of `placeholder`, but this cannot be proven to + /// hold. Report an error. #[instrument(level = "debug", skip(self, mbcx))] - fn report_error( + fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -190,12 +193,7 @@ pub(crate) trait TypeOpInfo<'tcx> { let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region); debug!(?nice_error); - - if let Some(nice_error) = nice_error { - mbcx.buffer_error(nice_error); - } else { - mbcx.buffer_error(self.fallback_error(tcx, span)); - } + mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span))); } } @@ -450,7 +448,8 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( ty::ReVar(vid) => universe_of_region(vid), _ => ty::UniverseIndex::ROOT, }; - let matches = + // Are the two regions the same? + let regions_the_same = |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, _ => a_region == b_region, @@ -459,7 +458,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match *constraint { Constraint::RegSubReg(sub, sup) if ((exact && sup == placeholder_region) - || (!exact && matches(sup, placeholder_region))) + || (!exact && regions_the_same(sup, placeholder_region))) && sup != sub => { Some((sub, cause.clone())) @@ -468,23 +467,21 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( if (exact && sup == placeholder_region && !universe_of_region(vid).can_name(placeholder_universe)) - || (!exact && matches(sup, placeholder_region)) => + || (!exact && regions_the_same(sup, placeholder_region)) => { Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) } _ => None, }; - let mut info = region_constraints - .constraints - .iter() - .find_map(|(constraint, cause)| check(constraint, cause, true)); - if info.is_none() { - info = region_constraints + + let mut find_culprit = |exact_match: bool| { + region_constraints .constraints .iter() - .find_map(|(constraint, cause)| check(constraint, cause, false)); - } - let (sub_region, cause) = info?; + .find_map(|(constraint, cause)| check(constraint, cause, exact_match)) + }; + + let (sub_region, cause) = find_culprit(true).or_else(|| find_culprit(false))?; debug!(?sub_region, "cause = {:#?}", cause); let error = match (error_region, sub_region.kind()) { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index cf735815fd2bb..0f8acadb978cb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1265,12 +1265,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && let CallKind::FnCall { fn_trait_id, self_ty } = kind && let ty::Param(_) = self_ty.kind() && ty == self_ty - && [ - self.infcx.tcx.lang_items().fn_once_trait(), - self.infcx.tcx.lang_items().fn_mut_trait(), - self.infcx.tcx.lang_items().fn_trait(), - ] - .contains(&Some(fn_trait_id)) + && self.infcx.tcx.fn_trait_kind_from_def_id(fn_trait_id).is_some() { // Do not suggest `F: FnOnce() + Clone`. false diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 4423edb060518..3bec07afa0fe0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -405,7 +405,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let universe = placeholder.universe; let universe_info = self.regioncx.universe_info(universe); - universe_info.report_error(self, placeholder, error_element, cause); + universe_info.report_erroneous_element(self, placeholder, error_element, cause); } RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 3b66142eb2cec..51d37353520b8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,12 +2,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -702,12 +702,12 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> { +impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> { fn visit_after_early_statement_effect( &mut self, _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, state: &BorrowckDomain, - stmt: &'a Statement<'tcx>, + stmt: &Statement<'tcx>, location: Location, ) { debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, state); @@ -783,7 +783,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< &mut self, _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, state: &BorrowckDomain, - term: &'a Terminator<'tcx>, + term: &Terminator<'tcx>, loc: Location, ) { debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, state); @@ -896,7 +896,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< &mut self, _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, state: &BorrowckDomain, - term: &'a Terminator<'tcx>, + term: &Terminator<'tcx>, loc: Location, ) { let span = term.source_info.span; @@ -1363,7 +1363,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { fn consume_rvalue( &mut self, location: Location, - (rvalue, span): (&'a Rvalue<'tcx>, Span), + (rvalue, span): (&Rvalue<'tcx>, Span), state: &BorrowckDomain, ) { match rvalue { @@ -1636,7 +1636,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { fn consume_operand( &mut self, location: Location, - (operand, span): (&'a Operand<'tcx>, Span), + (operand, span): (&Operand<'tcx>, Span), state: &BorrowckDomain, ) { match *operand { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f8af9e59f6357..c256051c122fa 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1628,30 +1628,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - for error_element in self.scc_values.elements_contained_in(longer_fr_scc) { - match error_element { - RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {} - // If we have some bound universal region `'a`, then the only - // elements it can contain is itself -- we don't know anything - // else about it! - RegionElement::PlaceholderRegion(placeholder1) => { - if placeholder == placeholder1 { - continue; - } - } - } - + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + if let Some(error_element) = self + .scc_values + .elements_contained_in(longer_fr_scc) + .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) + { + // Stop after the first error, it gets too noisy otherwise, and does not provide more information. errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { longer_fr, error_element, placeholder, }); - - // Stop after the first error, it gets too noisy otherwise, and does not provide more - // information. - break; + } else { + debug!("check_bound_universal_region: all bounds satisfied"); } - debug!("check_bound_universal_region: all bounds satisfied"); } #[instrument(level = "debug", skip(self, infcx, errors_buffer))] @@ -2071,7 +2064,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint.category }; - match category { + let interest = match category { // Returns usually provide a type to blame and have specially written diagnostics, // so prioritize them. ConstraintCategory::Return(_) => 0, @@ -2123,9 +2116,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // specific, and are not used for relations that would make sense to blame. ConstraintCategory::BoringNoLocation => 6, // Do not blame internal constraints. - ConstraintCategory::Internal => 7, - ConstraintCategory::IllegalUniverse => 8, - } + ConstraintCategory::IllegalUniverse => 7, + ConstraintCategory::Internal => 8, + }; + + debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); + + interest }; let best_choice = if blame_source { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index d9ac5b5cb132a..f1427218cdb02 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -21,7 +21,7 @@ rustc_index::newtype_index! { /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum RegionElement { /// A point in the control-flow graph. Location(Location), diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index daebd516499bc..6d97dfa3a4d41 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -217,14 +217,12 @@ mod llvm_enzyme { ast::StmtKind::Item(iitem) => extract_item_info(iitem), _ => None, }, - Annotatable::AssocItem(assoc_item, Impl { of_trait: false }) => { - match &assoc_item.kind { - ast::AssocItemKind::Fn(box ast::Fn { sig, ident, .. }) => { - Some((assoc_item.vis.clone(), sig.clone(), ident.clone())) - } - _ => None, + Annotatable::AssocItem(assoc_item, Impl { .. }) => match &assoc_item.kind { + ast::AssocItemKind::Fn(box ast::Fn { sig, ident, .. }) => { + Some((assoc_item.vis.clone(), sig.clone(), ident.clone())) } - } + _ => None, + }, _ => None, }) else { dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); @@ -365,7 +363,7 @@ mod llvm_enzyme { } Annotatable::Item(iitem.clone()) } - Annotatable::AssocItem(ref mut assoc_item, i @ Impl { of_trait: false }) => { + Annotatable::AssocItem(ref mut assoc_item, i @ Impl { .. }) => { if !assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { assoc_item.attrs.push(attr); } @@ -596,15 +594,14 @@ mod llvm_enzyme { } }; let arg = ty.kind.is_simple_path().unwrap(); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); + let tmp = ecx.def_site_path(&[arg, kw::Default]); let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); body.stmts.push(ecx.stmt_expr(default_call_expr)); return body; } - let mut exprs: P = primal_call.clone(); + let mut exprs: P = primal_call; let d_ret_ty = match d_sig.decl.output { FnRetTy::Ty(ref ty) => ty.clone(), FnRetTy::Default(span) => { @@ -622,7 +619,7 @@ mod llvm_enzyme { // type due to the Const return activity. exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); } else { - let q = QSelf { ty: d_ret_ty.clone(), path_span: span, position: 0 }; + let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 }; let y = ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default"))); let default_call_expr = ecx.expr(span, y); @@ -640,8 +637,7 @@ mod llvm_enzyme { let mut exprs2 = thin_vec![exprs]; for arg in args.iter().skip(1) { let arg = arg.kind.is_simple_path().unwrap(); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); + let tmp = ecx.def_site_path(&[arg, kw::Default]); let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index bcd40f980e649..70e817db2a630 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,6 +5,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -12,7 +13,6 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_codegen_cranelift/example/example.rs b/compiler/rustc_codegen_cranelift/example/example.rs index 1ef2aa5dd8ea4..aeb38331edb02 100644 --- a/compiler/rustc_codegen_cranelift/example/example.rs +++ b/compiler/rustc_codegen_cranelift/example/example.rs @@ -1,6 +1,6 @@ #![feature(no_core, unboxed_closures)] #![no_core] -#![allow(dead_code)] +#![allow(dead_code, unnecessary_transmutes)] extern crate mini_core; diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 0b3a7281d5a06..93ca2e0e42188 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,13 +1,4 @@ -#![feature( - no_core, - lang_items, - never_type, - linkage, - extern_types, - naked_functions, - thread_local, - repr_simd -)] +#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index ffdc6a7d48491..2d9de2a5b8d6b 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -8,9 +8,6 @@ unboxed_closures )] #![allow(internal_features)] -// FIXME once abi_unsupported_vector_types is a hard error disable the foo test when the respective -// target feature is not enabled. -#![allow(abi_unsupported_vector_types)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; diff --git a/compiler/rustc_codegen_gcc/example/example.rs b/compiler/rustc_codegen_gcc/example/example.rs index 03470b74d0a13..888fa89201e13 100644 --- a/compiler/rustc_codegen_gcc/example/example.rs +++ b/compiler/rustc_codegen_gcc/example/example.rs @@ -1,6 +1,6 @@ #![feature(no_core, unboxed_closures)] #![no_core] -#![allow(dead_code)] +#![allow(dead_code, unnecessary_transmutes)] extern crate mini_core; @@ -11,11 +11,7 @@ fn abc(a: u8) -> u8 { } fn bcd(b: bool, a: u8) -> u8 { - if b { - a * 2 - } else { - a * 3 - } + if b { a * 2 } else { a * 3 } } fn call() { diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index fd898c59707b0..452d3f22dc518 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-04-17" +channel = "nightly-2025-04-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index dbdf37ee6c9ef..396c6d5795015 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -165,10 +165,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let mut input_registers = vec![]; for op in rust_operands { - if let InlineAsmOperandRef::In { reg, .. } = *op { - if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { - input_registers.push(reg_name); - } + if let InlineAsmOperandRef::In { reg, .. } = *op + && let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) + { + input_registers.push(reg_name); } } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index a63da6b6e27d8..918195364ffee 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -33,12 +33,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { - if value.get_type() == self.bool_type.make_pointer() { - if let Some(pointee) = typ.get_pointee() { - if pointee.dyncast_vector().is_some() { - panic!() - } - } + if value.get_type() == self.bool_type.make_pointer() + && let Some(pointee) = typ.get_pointee() + && pointee.dyncast_vector().is_some() + { + panic!() } // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some // SIMD builtins require a constant value. diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index acb3937462857..0a67bd7bc71af 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -242,10 +242,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let fn_attrs = self.tcx.codegen_fn_attrs(def_id); let global = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) { - if let Some(global) = self.get_declared_value(sym) { - if self.val_ty(global) != self.type_ptr_to(gcc_type) { - span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); - } + if let Some(global) = self.get_declared_value(sym) + && self.val_ty(global) != self.type_ptr_to(gcc_type) + { + span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 55e01687400aa..f3ced86439527 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -126,14 +126,15 @@ fn make_mir_scope<'gcc, 'tcx>( return; }; - if let Some(ref vars) = *variables { - if !vars.contains(scope) && scope_data.inlined.is_none() { - // Do not create a DIScope if there are no variables defined in this - // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = parent_scope; - instantiated.insert(scope); - return; - } + if let Some(ref vars) = *variables + && !vars.contains(scope) + && scope_data.inlined.is_none() + { + // Do not create a DIScope if there are no variables defined in this + // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. + debug_context.scopes[scope] = parent_scope; + instantiated.insert(scope); + return; } let loc = cx.lookup_debug_loc(scope_data.span.lo()); diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 202764d564916..955f902023573 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -136,14 +136,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec( let mut name = with_no_trimmed_paths!(layout.ty.to_string()); if let (&ty::Adt(def, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) + && def.is_enum() + && !def.variants().is_empty() { - if def.is_enum() && !def.variants().is_empty() { - write!(&mut name, "::{}", def.variant(index).name).unwrap(); - } + write!(&mut name, "::{}", def.variant(index).name).unwrap(); } if let (&ty::Coroutine(_, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) @@ -264,10 +264,10 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { } fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { - if let BackendRepr::Scalar(ref scalar) = self.backend_repr { - if scalar.is_bool() { - return cx.type_i1(); - } + if let BackendRepr::Scalar(ref scalar) = self.backend_repr + && scalar.is_bool() + { + return cx.type_i1(); } self.gcc_type(cx) } diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs index 64c932a265819..d5a0d71c4b298 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs @@ -1,5 +1,7 @@ //! The common code for `tests/lang_tests_*.rs` +#![allow(clippy::uninlined_format_args)] + use std::env::{self, current_dir}; use std::path::{Path, PathBuf}; use std::process::Command; diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs index c1254c51ce91d..e627886a9d575 100644 --- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs @@ -5,6 +5,7 @@ // stdout: 10 // 10 // 42 +// 1 #![feature(no_core)] #![no_std] @@ -21,6 +22,8 @@ fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u3 ) } +static mut ONE: usize = 1; + #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); @@ -28,6 +31,10 @@ extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { libc::printf(b"%d\n\0" as *const u8 as *const i8, c); libc::printf(b"%ld\n\0" as *const u8 as *const i8, d); libc::printf(b"%ld\n\0" as *const u8 as *const i8, j); + + let ptr = ONE as *mut usize; + let value = ptr as usize; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, value); } 0 } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index a8b49e9552c30..925898d817371 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -584,12 +584,10 @@ fn thin_lto( } } -fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen) { +fn enable_autodiff_settings(ad: &[config::AutoDiff]) { for &val in ad { + // We intentionally don't use a wildcard, to not forget handling anything new. match val { - config::AutoDiff::PrintModBefore => { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; - } config::AutoDiff::PrintPerf => { llvm::set_print_perf(true); } @@ -603,17 +601,23 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen< llvm::set_inline(true); } config::AutoDiff::LooseTypes => { - llvm::set_loose_types(false); + llvm::set_loose_types(true); } config::AutoDiff::PrintSteps => { llvm::set_print(true); } - // We handle this below + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintPasses => {} + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintModBefore => {} + // We handle this in the PassWrapper.cpp config::AutoDiff::PrintModAfter => {} - // We handle this below + // We handle this in the PassWrapper.cpp config::AutoDiff::PrintModFinal => {} // This is required and already checked config::AutoDiff::Enable => {} + // We handle this below + config::AutoDiff::NoPostopt => {} } } // This helps with handling enums for now. @@ -647,27 +651,27 @@ pub(crate) fn run_pass_manager( // We then run the llvm_optimize function a second time, to optimize the code which we generated // in the enzyme differentiation pass. let enable_ad = config.autodiff.contains(&config::AutoDiff::Enable); - let stage = - if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD }; + let stage = if thin { + write::AutodiffStage::PreAD + } else { + if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD } + }; if enable_ad { - enable_autodiff_settings(&config.autodiff, module); + enable_autodiff_settings(&config.autodiff); } unsafe { write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; } - if cfg!(llvm_enzyme) && enable_ad { - // This is the post-autodiff IR, mainly used for testing and educational purposes. - if config.autodiff.contains(&config::AutoDiff::PrintModAfter) { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; - } - + if cfg!(llvm_enzyme) && enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; - unsafe { - write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; + if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { + unsafe { + write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; + } } // This is the final IR, so people should be able to inspect the optimized autodiff output, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 18d221d232e82..4ac77c8f7f165 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -572,6 +572,10 @@ pub(crate) unsafe fn llvm_optimize( let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); let run_enzyme = autodiff_stage == AutodiffStage::DuringAD; + let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore); + let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter); + let print_passes = config.autodiff.contains(&config::AutoDiff::PrintPasses); + let merge_functions; let unroll_loops; let vectorize_slp; let vectorize_loop; @@ -579,13 +583,20 @@ pub(crate) unsafe fn llvm_optimize( // When we build rustc with enzyme/autodiff support, we want to postpone size-increasing // optimizations until after differentiation. Our pipeline is thus: (opt + enzyme), (full opt). // We therefore have two calls to llvm_optimize, if autodiff is used. + // + // We also must disable merge_functions, since autodiff placeholder/dummy bodies tend to be + // identical. We run opts before AD, so there is a chance that LLVM will merge our dummies. + // In that case, we lack some dummy bodies and can't replace them with the real AD code anymore. + // We then would need to abort compilation. This was especially common in test cases. if consider_ad && autodiff_stage != AutodiffStage::PostAD { + merge_functions = false; unroll_loops = false; vectorize_slp = false; vectorize_loop = false; } else { unroll_loops = opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin; + merge_functions = config.merge_functions; vectorize_slp = config.vectorize_slp; vectorize_loop = config.vectorize_loop; } @@ -663,13 +674,16 @@ pub(crate) unsafe fn llvm_optimize( thin_lto_buffer, config.emit_thin_lto, config.emit_thin_lto_summary, - config.merge_functions, + merge_functions, unroll_loops, vectorize_slp, vectorize_loop, config.no_builtins, config.emit_lifetime_markers, run_enzyme, + print_before_enzyme, + print_after_enzyme, + print_passes, sanitizer_options.as_ref(), pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 27f7f95f100e2..04c8118b6160c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -594,6 +594,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); llvm::LLVMSetAlignment(load, align.bytes() as c_uint); load } @@ -807,6 +808,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); let align = if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() as c_uint }; llvm::LLVMSetAlignment(store, align); diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index e7c071d05aa8f..0147bd5a66581 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -473,7 +473,7 @@ pub(crate) fn differentiate<'ll>( return Err(diag_handler.handle().emit_almost_fatal(AutoDiffWithoutEnable)); } - // Before dumping the module, we want all the TypeTrees to become part of the module. + // Here we replace the placeholder code with the actual autodiff code, which calls Enzyme. for item in diff_items.iter() { let name = item.source.clone(); let fn_def: Option<&llvm::Value> = cx.get_function(&name); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 425381b0ffab7..b2feeacdb4664 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -15,7 +16,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![feature(slice_as_array)] #![feature(try_blocks)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9ff04f729030c..ffb490dcdc22b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2454,6 +2454,9 @@ unsafe extern "C" { DisableSimplifyLibCalls: bool, EmitLifetimeMarkers: bool, RunEnzyme: bool, + PrintBeforeEnzyme: bool, + PrintAfterEnzyme: bool, + PrintPasses: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, PGOUsePath: *const c_char, diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 34c84c64070d2..1e1bdfb5977af 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; +use rustc_fs_util::TempDirBuilder; use rustc_session::Session; use rustc_span::Symbol; -use tempfile::Builder as TempFileBuilder; use tracing::trace; use super::metadata::search_for_section; @@ -501,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> { // it creates. We need it to be the default mode for back compat reasons however. (See // #107495) To handle this we are telling tempfile to create a temporary directory instead // and then inside this directory create a file using File::create. - let archive_tmpdir = TempFileBuilder::new() + let archive_tmpdir = TempDirBuilder::new() .suffix(".temp-archive") .tempdir_in(output.parent().unwrap_or_else(|| Path::new(""))) .map_err(|err| { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8de68925cabbc..323538969d73b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; -use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; +use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; @@ -48,7 +48,6 @@ use rustc_target::spec::{ LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, }; -use tempfile::Builder as TempFileBuilder; use tracing::{debug, info, warn}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; @@ -100,7 +99,7 @@ pub fn link_binary( }); if outputs.outputs.should_link() { - let tmpdir = TempFileBuilder::new() + let tmpdir = TempDirBuilder::new() .prefix("rustc") .tempdir() .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); @@ -2015,6 +2014,12 @@ fn add_linked_symbol_object( file.set_mangling(object::write::Mangling::None); } + if file.format() == object::BinaryFormat::MachO { + // Divide up the sections into sub-sections via symbols for dead code stripping. + // Without this flag, unused `#[no_mangle]` or `#[used]` cannot be discard on MachO targets. + file.set_subsections_via_symbols(); + } + // ld64 requires a relocation to load undefined symbols, see below. // Not strictly needed if linking with lld, but might as well do it there too. let ld64_section_helper = if file.format() == object::BinaryFormat::MachO { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 5b2ed69535b9f..c927aae2c4c2b 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -2,13 +2,13 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(rustdoc_internals)] #![feature(string_from_utf8_lossy_owned)] diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 4ca317e3a1e53..40c63f2b250f5 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -158,6 +158,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&val, dest)?; } + sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => { + let a = self.read_immediate(&args[0])?; + let b = self.read_immediate(&args[1])?; + + let op = match intrinsic_name { + sym::fadd_algebraic => BinOp::Add, + sym::fsub_algebraic => BinOp::Sub, + sym::fmul_algebraic => BinOp::Mul, + sym::fdiv_algebraic => BinOp::Div, + sym::frem_algebraic => BinOp::Rem, + + _ => bug!(), + }; + + let res = self.binary_op(op, &a, &b)?; + // `binary_op` already called `generate_nan` if needed. + + // FIXME: Miri should add some non-determinism to the result here to catch any dependences on exact computations. This has previously been done, but the behaviour was removed as part of constification. + self.write_immediate(*res, dest)?; + } + sym::ctpop | sym::cttz | sym::cttz_nonzero diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index d077900587e9c..9d8130661b04d 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -872,8 +872,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # Function pointers // (both global from `alloc_map` and local from `extra_fn_ptr_map`) - if self.get_fn_alloc(id).is_some() { - return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::Function, Mutability::Not); + if let Some(fn_val) = self.get_fn_alloc(id) { + let align = match fn_val { + FnVal::Instance(instance) => { + // Function alignment can be set globally with the `-Zmin-function-alignment=` flag; + // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. + let fn_align = self.tcx.codegen_fn_attrs(instance.def_id()).alignment; + let global_align = self.tcx.sess.opts.unstable_opts.min_function_alignment; + + Ord::max(global_align, fn_align).unwrap_or(Align::ONE) + } + // Machine-specific extra functions currently do not support alignment restrictions. + FnVal::Other(_) => Align::ONE, + }; + + return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not); } // # Global allocations diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index da52d60ae59fd..7a0c2543c3002 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,12 +1,12 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 5f07cfef1335e..dfd9bd3207616 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -39,8 +39,15 @@ impls_dyn_send_neg!( [std::io::StderrLock<'_>] ); -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] -// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +#[cfg(any( + unix, + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "wasi", + target_os = "xous" +))] +// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms impl !DynSend for std::env::VarsOs {} macro_rules! already_send { @@ -106,8 +113,15 @@ impls_dyn_sync_neg!( [std::sync::mpsc::Sender where T] ); -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] -// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +#[cfg(any( + unix, + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "wasi", + target_os = "xous" +))] +// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms impl !DynSync for std::env::VarsOs {} macro_rules! already_sync { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 40cc82727a556..d18fa89281404 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -7,10 +7,10 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(decl_macro)] -#![feature(let_chains)] #![feature(panic_backtrace_config)] #![feature(panic_update_hook)] #![feature(result_flattening)] diff --git a/compiler/rustc_error_codes/src/error_codes/E0787.md b/compiler/rustc_error_codes/src/error_codes/E0787.md index 47b56ac17f4f5..b7f92c8feb587 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0787.md +++ b/compiler/rustc_error_codes/src/error_codes/E0787.md @@ -3,8 +3,6 @@ An unsupported naked function definition. Erroneous code example: ```compile_fail,E0787 -#![feature(naked_functions)] - #[unsafe(naked)] pub extern "C" fn f() -> u32 { 42 diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index c0c5dba46772a..6f37bad9bb462 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,6 +7,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -17,7 +18,6 @@ #![feature(default_field_values)] #![feature(error_reporter)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c70e259b2cd82..d2e45d717d901 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -237,10 +237,7 @@ impl<'a> StripUnconfigured<'a> { inner = self.configure_tokens(&inner); Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)) } - AttrTokenTree::Token( - Token { kind: TokenKind::OpenDelim(_) | TokenKind::CloseDelim(_), .. }, - _, - ) => { + AttrTokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => { panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tree); } AttrTokenTree::Token(token, spacing) => Some(AttrTokenTree::Token(token, spacing)), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 1e26d66819430..1f430b0018f50 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -7,13 +7,12 @@ use std::{iter, mem}; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, - NodeId, PatKind, StmtKind, TyKind, + NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; @@ -1004,7 +1003,7 @@ pub fn parse_ast_fragment<'a>( AstFragmentKind::Stmts => { let mut stmts = SmallVec::new(); // Won't make progress on a `}`. - while this.token != token::Eof && this.token != token::CloseDelim(Delimiter::Brace) { + while this.token != token::Eof && this.token != token::CloseBrace { if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? { stmts.push(stmt); } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 4222c9fe90616..79f838e2e33f2 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,11 +1,11 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(array_windows)] #![feature(associated_type_defaults)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] #![feature(proc_macro_diagnostic)] diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index b663e959744f8..698492f42e28f 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; @@ -66,8 +66,8 @@ pub(super) fn failed_to_match_macro( } if let MatcherLoc::Token { token: expected_token } = &remaining_matcher - && (matches!(expected_token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_))) - || matches!(token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_)))) + && (matches!(expected_token.kind, token::OpenInvisible(_)) + || matches!(token.kind, token::OpenInvisible(_))) { 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 0065f83eb4ee5..c78beb40688fe 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -182,8 +182,8 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec { locs.push(MatcherLoc::Token { token: *token }); } TokenTree::Delimited(span, _, delimited) => { - let open_token = Token::new(token::OpenDelim(delimited.delim), span.open); - let close_token = Token::new(token::CloseDelim(delimited.delim), span.close); + let open_token = Token::new(delimited.delim.as_open_token_kind(), span.open); + let close_token = Token::new(delimited.delim.as_close_token_kind(), span.close); locs.push(MatcherLoc::Delimited); locs.push(MatcherLoc::Token { token: open_token }); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index c138b0908772f..93604a149f1d9 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -6,7 +6,7 @@ use std::{mem, slice}; use ast::token::IdentIsRaw; use rustc_ast::token::NtPatKind::*; use rustc_ast::token::TokenKind::*; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; +use rustc_ast::token::{self, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; @@ -784,7 +784,7 @@ impl<'tt> FirstSets<'tt> { TokenTree::Delimited(span, _, delimited) => { build_recur(sets, &delimited.tts); first.replace_with(TtHandle::from_token_kind( - token::OpenDelim(delimited.delim), + delimited.delim.as_open_token_kind(), span.open, )); } @@ -852,7 +852,7 @@ impl<'tt> FirstSets<'tt> { } TokenTree::Delimited(span, _, delimited) => { first.add_one(TtHandle::from_token_kind( - token::OpenDelim(delimited.delim), + delimited.delim.as_open_token_kind(), span.open, )); return first; @@ -1099,7 +1099,7 @@ fn check_matcher_core<'tt>( } TokenTree::Delimited(span, _, d) => { let my_suffix = TokenSet::singleton(TtHandle::from_token_kind( - token::CloseDelim(d.delim), + d.delim.as_close_token_kind(), span.close, )); check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?; @@ -1299,7 +1299,9 @@ enum IsInFollow { fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { use mbe::TokenTree; - if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok { + if let TokenTree::Token(Token { kind, .. }) = tok + && kind.close_delim().is_some() + { // closing a token tree can never be matched by any fragment; // iow, we always require that `(` and `)` match, etc. IsInFollow::Yes @@ -1358,16 +1360,8 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { ]; match tok { TokenTree::Token(token) => match token.kind { - OpenDelim(Delimiter::Brace) - | OpenDelim(Delimiter::Bracket) - | Comma - | FatArrow - | Colon - | Eq - | Gt - | Shr - | Semi - | Or => IsInFollow::Yes, + OpenBrace | OpenBracket | Comma | FatArrow | Colon | Eq | Gt | Shr + | Semi | Or => IsInFollow::Yes, Ident(name, IdentIsRaw::No) if name == kw::As || name == kw::Where => { IsInFollow::Yes } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 3f03725995659..0c2362f23bcfc 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -181,7 +181,10 @@ fn parse_tree<'a>( if delim != Delimiter::Parenthesis { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, - &Token { kind: token::OpenDelim(delim), span: delim_span.entire() }, + &Token { + kind: delim.as_open_token_kind(), + span: delim_span.entire(), + }, ); } } else { @@ -217,7 +220,8 @@ fn parse_tree<'a>( } Delimiter::Parenthesis => {} _ => { - let token = pprust::token_kind_to_string(&token::OpenDelim(delim)); + let token = + pprust::token_kind_to_string(&delim.as_open_token_kind()); sess.dcx().emit_err(errors::ExpectedParenOrBrace { span: delim_span.entire(), token, diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4edaf68c89ae2..f00201ad202af 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -308,8 +308,8 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec unreachable!(), - Eof => unreachable!(), + OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket + | OpenInvisible(_) | CloseInvisible(_) | Eof => unreachable!(), } } trees diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index fcc11dd3c1fd7..e3e4eefe5e10a 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -300,6 +300,8 @@ declare_features! ( /// Allows patterns with concurrent by-move and by-ref bindings. /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. (accepted, move_ref_pattern, "1.49.0", Some(68354)), + /// Allows using `#[naked]` on functions. + (accepted, naked_functions, "CURRENT_RUSTC_VERSION", Some(90957)), /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]` (accepted, native_link_modifiers, "1.61.0", Some(81490)), /// Allows specifying the bundle link modifier diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ccbb8e83fb658..76270cad48f39 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -443,6 +443,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No), ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes), + ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Limits: ungated!( @@ -515,12 +516,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Unstable attributes: // ========================================================================== - // Linking: - gated!( - unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, - naked_functions, experimental!(naked) - ), - // Testing: gated!( test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e09ae3c12394d..cbc121e3632a6 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -563,8 +563,6 @@ declare_features! ( (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "1.79.0", Some(123076)), - /// Allows using `#[naked]` on functions. - (unstable, naked_functions, "1.9.0", Some(90957)), /// Allows using `#[naked]` on `extern "Rust"` functions. (unstable, naked_functions_rustic_abi, "CURRENT_RUSTC_VERSION", Some(138997)), /// Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions. diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml index baca3bc7d49eb..90a6acade8b03 100644 --- a/compiler/rustc_fs_util/Cargo.toml +++ b/compiler/rustc_fs_util/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +tempfile = "3.7.1" # tidy-alphabetical-end diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 0df1b243d697e..7a883a13b72da 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,6 +1,8 @@ -use std::ffi::CString; +use std::ffi::{CString, OsStr}; use std::path::{Path, PathBuf, absolute}; -use std::{fs, io}; +use std::{env, fs, io}; + +use tempfile::TempDir; // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as @@ -102,3 +104,43 @@ pub fn path_to_c_string(p: &Path) -> CString { pub fn try_canonicalize>(path: P) -> io::Result { fs::canonicalize(&path).or_else(|_| absolute(&path)) } + +pub struct TempDirBuilder<'a, 'b> { + builder: tempfile::Builder<'a, 'b>, +} + +impl<'a, 'b> TempDirBuilder<'a, 'b> { + pub fn new() -> Self { + Self { builder: tempfile::Builder::new() } + } + + pub fn prefix + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { + self.builder.prefix(prefix); + self + } + + pub fn suffix + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { + self.builder.suffix(suffix); + self + } + + pub fn tempdir_in>(&self, dir: P) -> io::Result { + let dir = dir.as_ref(); + // On Windows in CI, we had been getting fairly frequent "Access is denied" + // errors when creating temporary directories. + // So this implements a simple retry with backoff loop. + #[cfg(windows)] + for wait in 1..11 { + match self.builder.tempdir_in(dir) { + Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {} + t => return t, + } + std::thread::sleep(std::time::Duration::from_millis(1 << wait)); + } + self.builder.tempdir_in(dir) + } + + pub fn tempdir(&self) -> io::Result { + self.tempdir_in(env::temp_dir()) + } +} diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d02c767ea677a..2f8a853424789 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -36,43 +36,110 @@ pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] -pub enum IsAnonInPath { - No, - Yes, +pub enum LifetimeSource { + /// E.g. `&Type`, `&'_ Type`, `&'a Type`, `&mut Type`, `&'_ mut Type`, `&'a mut Type` + Reference, + + /// E.g. `ContainsLifetime`, `ContainsLifetime<'_>`, `ContainsLifetime<'a>` + Path { + /// - true for `ContainsLifetime<'_>`, `ContainsLifetime<'a>`, + /// `ContainsLifetime<'_, T>`, `ContainsLifetime<'a, T>` + /// - false for `ContainsLifetime` + with_angle_brackets: bool, + }, + + /// E.g. `impl Trait + '_`, `impl Trait + 'a` + OutlivesBound, + + /// E.g. `impl Trait + use<'_>`, `impl Trait + use<'a>` + PreciseCapturing, + + /// Other usages which have not yet been categorized. Feel free to + /// add new sources that you find useful. + /// + /// Some non-exhaustive examples: + /// - `where T: 'a` + /// - `fn(_: dyn Trait + 'a)` + Other, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] +pub enum LifetimeSyntax { + /// E.g. `&Type`, `ContainsLifetime` + Hidden, + + /// E.g. `&'_ Type`, `ContainsLifetime<'_>`, `impl Trait + '_`, `impl Trait + use<'_>` + Anonymous, + + /// E.g. `&'a Type`, `ContainsLifetime<'a>`, `impl Trait + 'a`, `impl Trait + use<'a>` + Named, } -/// A lifetime. The valid field combinations are non-obvious. The following -/// example shows some of them. See also the comments on `LifetimeKind`. +impl From for LifetimeSyntax { + fn from(ident: Ident) -> Self { + let name = ident.name; + + if name == kw::Empty { + unreachable!("A lifetime name should never be empty"); + } else if name == kw::UnderscoreLifetime { + LifetimeSyntax::Anonymous + } else { + debug_assert!(name.as_str().starts_with('\'')); + LifetimeSyntax::Named + } + } +} + +/// A lifetime. The valid field combinations are non-obvious and not all +/// combinations are possible. The following example shows some of +/// them. See also the comments on `LifetimeKind` and `LifetimeSource`. +/// /// ``` /// #[repr(C)] -/// struct S<'a>(&'a u32); // res=Param, name='a, IsAnonInPath::No +/// struct S<'a>(&'a u32); // res=Param, name='a, source=Reference, syntax=Named /// unsafe extern "C" { -/// fn f1(s: S); // res=Param, name='_, IsAnonInPath::Yes -/// fn f2(s: S<'_>); // res=Param, name='_, IsAnonInPath::No -/// fn f3<'a>(s: S<'a>); // res=Param, name='a, IsAnonInPath::No +/// fn f1(s: S); // res=Param, name='_, source=Path, syntax=Hidden +/// fn f2(s: S<'_>); // res=Param, name='_, source=Path, syntax=Anonymous +/// fn f3<'a>(s: S<'a>); // res=Param, name='a, source=Path, syntax=Named /// } /// -/// struct St<'a> { x: &'a u32 } // res=Param, name='a, IsAnonInPath::No +/// struct St<'a> { x: &'a u32 } // res=Param, name='a, source=Reference, syntax=Named /// fn f() { -/// _ = St { x: &0 }; // res=Infer, name='_, IsAnonInPath::Yes -/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, IsAnonInPath::No +/// _ = St { x: &0 }; // res=Infer, name='_, source=Path, syntax=Hidden +/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, source=Path, syntax=Anonymous /// } /// -/// struct Name<'a>(&'a str); // res=Param, name='a, IsAnonInPath::No -/// const A: Name = Name("a"); // res=Static, name='_, IsAnonInPath::Yes -/// const B: &str = ""; // res=Static, name='_, IsAnonInPath::No -/// static C: &'_ str = ""; // res=Static, name='_, IsAnonInPath::No -/// static D: &'static str = ""; // res=Static, name='static, IsAnonInPath::No +/// struct Name<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named +/// const A: Name = Name("a"); // res=Static, name='_, source=Path, syntax=Hidden +/// const B: &str = ""; // res=Static, name='_, source=Reference, syntax=Hidden +/// static C: &'_ str = ""; // res=Static, name='_, source=Reference, syntax=Anonymous +/// static D: &'static str = ""; // res=Static, name='static, source=Reference, syntax=Named /// /// trait Tr {} -/// fn tr(_: Box) {} // res=ImplicitObjectLifetimeDefault, name='_, IsAnonInPath::No +/// fn tr(_: Box) {} // res=ImplicitObjectLifetimeDefault, name='_, source=Other, syntax=Hidden +/// +/// fn capture_outlives<'a>() -> +/// impl FnOnce() + 'a // res=Param, ident='a, source=OutlivesBound, syntax=Named +/// { +/// || {} +/// } +/// +/// fn capture_precise<'a>() -> +/// impl FnOnce() + use<'a> // res=Param, ident='a, source=PreciseCapturing, syntax=Named +/// { +/// || {} +/// } /// /// // (commented out because these cases trigger errors) -/// // struct S1<'a>(&'a str); // res=Param, name='a, IsAnonInPath::No -/// // struct S2(S1); // res=Error, name='_, IsAnonInPath::Yes -/// // struct S3(S1<'_>); // res=Error, name='_, IsAnonInPath::No -/// // struct S4(S1<'a>); // res=Error, name='a, IsAnonInPath::No +/// // struct S1<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named +/// // struct S2(S1); // res=Error, name='_, source=Path, syntax=Hidden +/// // struct S3(S1<'_>); // res=Error, name='_, source=Path, syntax=Anonymous +/// // struct S4(S1<'a>); // res=Error, name='a, source=Path, syntax=Named /// ``` +/// +/// Some combinations that cannot occur are `LifetimeSyntax::Hidden` with +/// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing` +/// — there's no way to "elide" these lifetimes. #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Lifetime { #[stable_hasher(ignore)] @@ -86,9 +153,13 @@ pub struct Lifetime { /// Semantics of this lifetime. pub kind: LifetimeKind, - /// Is the lifetime anonymous and in a path? Used only for error - /// suggestions. See `Lifetime::suggestion` for example use. - pub is_anon_in_path: IsAnonInPath, + /// The context in which the lifetime occurred. See `Lifetime::suggestion` + /// for example use. + pub source: LifetimeSource, + + /// The syntax that the user used to declare this lifetime. See + /// `Lifetime::suggestion` for example use. + pub syntax: LifetimeSyntax, } #[derive(Debug, Copy, Clone, HashStable_Generic)] @@ -185,9 +256,10 @@ impl Lifetime { hir_id: HirId, ident: Ident, kind: LifetimeKind, - is_anon_in_path: IsAnonInPath, + source: LifetimeSource, + syntax: LifetimeSyntax, ) -> Lifetime { - let lifetime = Lifetime { hir_id, ident, kind, is_anon_in_path }; + let lifetime = Lifetime { hir_id, ident, kind, source, syntax }; // Sanity check: elided lifetimes form a strict subset of anonymous lifetimes. #[cfg(debug_assertions)] @@ -209,23 +281,44 @@ impl Lifetime { self.ident.name == kw::UnderscoreLifetime } + pub fn is_syntactically_hidden(&self) -> bool { + matches!(self.syntax, LifetimeSyntax::Hidden) + } + + pub fn is_syntactically_anonymous(&self) -> bool { + matches!(self.syntax, LifetimeSyntax::Anonymous) + } + + pub fn is_static(&self) -> bool { + self.kind == LifetimeKind::Static + } + pub fn suggestion(&self, new_lifetime: &str) -> (Span, String) { + use LifetimeSource::*; + use LifetimeSyntax::*; + debug_assert!(new_lifetime.starts_with('\'')); - match (self.is_anon_in_path, self.ident.span.is_empty()) { + match (self.syntax, self.source) { + // The user wrote `'a` or `'_`. + (Named | Anonymous, _) => (self.ident.span, format!("{new_lifetime}")), + // The user wrote `Path`, and omitted the `'_,`. - (IsAnonInPath::Yes, true) => (self.ident.span, format!("{new_lifetime}, ")), + (Hidden, Path { with_angle_brackets: true }) => { + (self.ident.span, format!("{new_lifetime}, ")) + } // The user wrote `Path` and omitted the `<'_>`. - (IsAnonInPath::Yes, false) => { + (Hidden, Path { with_angle_brackets: false }) => { (self.ident.span.shrink_to_hi(), format!("<{new_lifetime}>")) } // The user wrote `&type` or `&mut type`. - (IsAnonInPath::No, true) => (self.ident.span, format!("{new_lifetime} ")), + (Hidden, Reference) => (self.ident.span, format!("{new_lifetime} ")), - // The user wrote `'a` or `'_`. - (IsAnonInPath::No, false) => (self.ident.span, format!("{new_lifetime}")), + (Hidden, source) => { + unreachable!("can't suggest for a hidden lifetime of {source:?}") + } } } } diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs index fcd0eafa46139..18f8c523f9d39 100644 --- a/compiler/rustc_hir/src/hir/tests.rs +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -58,7 +58,8 @@ fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) { hir_id: HirId::INVALID, ident: Ident::new(sym::name, DUMMY_SP), kind: LifetimeKind::Static, - is_anon_in_path: IsAnonInPath::No, + source: LifetimeSource::Other, + syntax: LifetimeSyntax::Hidden, } }, syntax, diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index a84857e3597b0..32064f96dd6c2 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -4,12 +4,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(closure_track_caller)] #![feature(debug_closure_helpers)] #![feature(exhaustive_patterns)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 89fb35154b75e..692784bf1714d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -2,8 +2,7 @@ use rustc_abi::ExternAbi; use rustc_errors::DiagMessage; -use rustc_hir::{self as hir}; -use rustc_middle::bug; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; @@ -173,23 +172,22 @@ pub(crate) fn check_intrinsic_type( ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), ]); let mk_va_list_ty = |mutbl| { - tcx.lang_items().va_list().map(|did| { - let region = ty::Region::new_bound( - tcx, - ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, - ); - let env_region = ty::Region::new_bound( - tcx, - ty::INNERMOST, - ty::BoundRegion { - var: ty::BoundVar::from_u32(2), - kind: ty::BoundRegionKind::ClosureEnv, - }, - ); - let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); - (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) - }) + let did = tcx.require_lang_item(LangItem::VaList, Some(span)); + let region = ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, + ); + let env_region = ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_u32(2), + kind: ty::BoundRegionKind::ClosureEnv, + }, + ); + let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); + (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }; let (n_tps, n_lts, n_cts, inputs, output, safety) = if name_str.starts_with("atomic_") { @@ -548,23 +546,17 @@ pub(crate) fn check_intrinsic_type( ) } - sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (0, 0, vec![va_list_ref_ty], tcx.types.unit), - None => bug!("`va_list` lang item needed for C-variadic intrinsics"), - }, + sym::va_start | sym::va_end => { + (0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit) + } - sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) { - Some((va_list_ref_ty, va_list_ty)) => { - let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); - (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit) - } - None => bug!("`va_list` lang item needed for C-variadic intrinsics"), - }, + sym::va_copy => { + let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not); + let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); + (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit) + } - sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (1, 0, vec![va_list_ref_ty], param(0)), - None => bug!("`va_list` lang item needed for C-variadic intrinsics"), - }, + sym::va_arg => (1, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], param(0)), sym::nontemporal_store => { (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8ad9d80c6b5af..52656fc2d9001 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -750,7 +750,7 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err ObligationCause::misc(impl_span, checker.impl_def_id), param_env, nontrivial_field_ty, - tcx.lang_items().pointer_like().unwrap(), + tcx.require_lang_item(LangItem::PointerLike, Some(impl_span)), ); // FIXME(dyn-star): We should regionck this implementation. if ocx.select_all_or_error().is_empty() { diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 526ee30209c7d..a3c8ce620b366 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -486,15 +486,15 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let items: &AssocItems = self.tcx.associated_items(self.def_id); items .in_definition_order() - .filter(|item| item.is_type()) .filter(|item| { - !self - .gen_args - .constraints - .iter() - .any(|constraint| constraint.ident.name == item.name()) + item.is_type() + && !item.is_impl_trait_in_trait() + && !self + .gen_args + .constraints + .iter() + .any(|constraint| constraint.ident.name == item.name()) }) - .filter(|item| !item.is_impl_trait_in_trait()) .map(|item| self.tcx.item_ident(item.def_id).to_string()) .collect() } else { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 7605c6c6a420a..22162b8b3649a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -29,7 +29,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, }; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -1731,9 +1731,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.associated_items(*trait_def_id) .in_definition_order() .any(|i| { - i.namespace() == Namespace::TypeNS + i.is_type() + && !i.is_impl_trait_in_trait() && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - && i.is_type() }) // Consider only accessible traits && tcx.visibility(*trait_def_id) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index e1ad8124aea76..309b8f2c76138 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -59,6 +59,7 @@ This API is completely unstable and subject to change. #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -67,7 +68,6 @@ This API is completely unstable and subject to change. #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_partition_dedup)] diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index ff4385c3bccec..779fae80f19cd 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2,7 +2,7 @@ //! the definitions in this file have equivalents in `rustc_ast_pretty`. // tidy-alphabetical-start -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 02fd7367e2f29..d770937bd6616 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -37,7 +37,7 @@ pub(crate) fn check_legal_trait_for_method_call( body_id: DefId, ) -> Result<(), ErrorGuaranteed> { if tcx.is_lang_item(trait_id, LangItem::Drop) - && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id) + && !tcx.is_lang_item(body_id, LangItem::FallbackSurfaceDrop) { let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { errors::ExplicitDestructorCallSugg::Snippet { diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index b19d9efe2c6f5..caf36ba47bd75 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -501,12 +501,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { .must_apply_modulo_regions() { label = false; - err.span_suggestion( - self.span, - "consider using the `From` trait instead", - format!("{}::from({})", self.cast_ty, snippet), - Applicability::MaybeIncorrect, - ); + if let ty::Adt(def, args) = self.cast_ty.kind() { + err.span_suggestion_verbose( + self.span, + "consider using the `From` trait instead", + format!( + "{}::from({})", + fcx.tcx.value_path_str_with_args(def.did(), args), + snippet + ), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + }; } } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b8968b58769a2..8fd59999fce5e 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -163,7 +163,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resume type defaults to `()` if the coroutine has no argument. let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit); - let interior = self.next_ty_var(expr_span); + // In the new solver, we can just instantiate this eagerly + // with the witness. This will ensure that goals that don't need + // to stall on interior types will get processed eagerly. + let interior = if self.next_trait_solver() { + Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args) + } else { + self.next_ty_var(expr_span) + }; + self.deferred_coroutine_interiors.borrow_mut().push((expr_def_id, interior)); // Coroutines that come from coroutine closures have not yet determined diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d6a7bfc446a41..9c6d4ee096f1a 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3289,8 +3289,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.multipart_suggestion( format!("{val} is a raw pointer; try dereferencing it"), vec![ - (base.span.shrink_to_lo(), "(*".to_string()), - (base.span.shrink_to_hi(), ")".to_string()), + (base.span.shrink_to_lo(), "(*".into()), + (base.span.between(field.span), format!(").")), ], Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 87d92b3fbde8d..cac8237f8f61a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -635,34 +635,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut obligations = vec![]; - for &(coroutine_def_id, interior) in coroutines.iter() { - debug!(?coroutine_def_id); + if !self.next_trait_solver() { + for &(coroutine_def_id, interior) in coroutines.iter() { + debug!(?coroutine_def_id); + + // Create the `CoroutineWitness` type that we will unify with `interior`. + let args = ty::GenericArgs::identity_for_item( + self.tcx, + self.tcx.typeck_root_def_id(coroutine_def_id.to_def_id()), + ); + let witness = + Ty::new_coroutine_witness(self.tcx, coroutine_def_id.to_def_id(), args); - // Create the `CoroutineWitness` type that we will unify with `interior`. - let args = ty::GenericArgs::identity_for_item( - self.tcx, - self.tcx.typeck_root_def_id(coroutine_def_id.to_def_id()), - ); - let witness = Ty::new_coroutine_witness(self.tcx, coroutine_def_id.to_def_id(), args); - - // Unify `interior` with `witness` and collect all the resulting obligations. - let span = self.tcx.hir_body_owned_by(coroutine_def_id).value.span; - let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else { - span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind()) - }; - let ok = self - .at(&self.misc(span), self.param_env) - // Will never define opaque types, as all we do is instantiate a type variable. - .eq(DefineOpaqueTypes::Yes, interior, witness) - .expect("Failed to unify coroutine interior type"); - - obligations.extend(ok.obligations); + // Unify `interior` with `witness` and collect all the resulting obligations. + let span = self.tcx.hir_body_owned_by(coroutine_def_id).value.span; + let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else { + span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind()) + }; + let ok = self + .at(&self.misc(span), self.param_env) + // Will never define opaque types, as all we do is instantiate a type variable. + .eq(DefineOpaqueTypes::Yes, interior, witness) + .expect("Failed to unify coroutine interior type"); + + obligations.extend(ok.obligations); + } } - // FIXME: Use a real visitor for unstalled obligations in the new solver. if !coroutines.is_empty() { - obligations - .extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx)); + obligations.extend( + self.fulfillment_cx + .borrow_mut() + .drain_stalled_obligations_for_coroutines(&self.infcx), + ); } self.typeck_results diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index da0e8e362d663..8b2d9ab297905 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -925,7 +925,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { if let ty::Adt(adt, _) = ty.kind() - && self.tcx().lang_items().get(hir::LangItem::RangeFull) == Some(adt.did()) + && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) && let hir::ExprKind::Struct( hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index af8ec37393472..c3717b4efa47f 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,11 +1,11 @@ // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 0e42a84ca32ae..b86991f81ad83 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -234,7 +234,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // us do better coercions than we would be able to do otherwise, // particularly for things like `String + &String`. let rhs_ty_var = self.next_ty_var(rhs_expr.span); - let result = self.lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty_var)), @@ -698,6 +697,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let lhs_name_str = match lhs_expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => { + path.segments.last().map_or("_".to_string(), |s| s.ident.to_string()) + } + _ => self + .tcx + .sess + .source_map() + .span_to_snippet(lhs_expr.span) + .unwrap_or("_".to_string()), + }; + + if op.span().can_be_used_for_suggestions() { + match op { + Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. }) + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => + { + err.multipart_suggestion( + "consider using `add` or `wrapping_add` to do pointer arithmetic", + vec![ + (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)), + ( + lhs_expr.span.between(rhs_expr.span), + ".wrapping_add(".to_owned(), + ), + (rhs_expr.span.shrink_to_hi(), ")".to_owned()), + ], + Applicability::MaybeIncorrect, + ); + } + Op::AssignOp(Spanned { node: hir::AssignOpKind::SubAssign, .. }) => { + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() { + err.multipart_suggestion( + "consider using `sub` or `wrapping_sub` to do pointer arithmetic", + vec![ + (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)), + ( + lhs_expr.span.between(rhs_expr.span), + ".wrapping_sub(".to_owned(), + + ), + (rhs_expr.span.shrink_to_hi(), ")".to_owned()), + ], + Applicability::MaybeIncorrect, + ); + } + } + _ => {} + } + } + let reported = err.emit(); Ty::new_error(self.tcx, reported) } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index e5e4fc7f8b77f..7520782930a22 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -759,22 +759,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically-sized byte arrays. + // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have + // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec`. let mut pat_ty = ty; if let hir::PatExprKind::Lit { lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, .. } = lt.kind { + let tcx = self.tcx; let expected = self.structurally_resolve_type(span, expected); - if let ty::Ref(_, inner_ty, _) = *expected.kind() - && self.try_structurally_resolve_type(span, inner_ty).is_slice() - { - let tcx = self.tcx; - trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); - pat_ty = - Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8)); + match *expected.kind() { + // Allow `b"...": &[u8]` + ty::Ref(_, inner_ty, _) + if self.try_structurally_resolve_type(span, inner_ty).is_slice() => + { + trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); + pat_ty = Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_static, + Ty::new_slice(tcx, tcx.types.u8), + ); + } + // Allow `b"...": [u8; 3]` for `deref_patterns` + ty::Array(..) if tcx.features().deref_patterns() => { + pat_ty = match *ty.kind() { + ty::Ref(_, inner_ty, _) => inner_ty, + _ => span_bug!(span, "found byte string literal with non-ref type {ty:?}"), + } + } + // Allow `b"...": [u8]` for `deref_patterns` + ty::Slice(..) if tcx.features().deref_patterns() => { + pat_ty = Ty::new_slice(tcx, tcx.types.u8); + } + // Otherwise, `b"...": &[u8; 3]` + _ => {} } } + // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow + // string literal patterns to have type `str`. This is accounted for when lowering to MIR. + if self.tcx.features().deref_patterns() + && let hir::PatExprKind::Lit { + lit: Spanned { node: ast::LitKind::Str(..), .. }, .. + } = lt.kind + && self.try_structurally_resolve_type(span, expected).is_str() + { + pat_ty = self.tcx.types.str_; + } + if self.tcx.features().string_deref_patterns() && let hir::PatExprKind::Lit { lit: Spanned { node: ast::LitKind::Str(..), .. }, .. @@ -1457,15 +1489,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (None, None), }; - let ranges = &[ - self.tcx.lang_items().range_struct(), - self.tcx.lang_items().range_from_struct(), - self.tcx.lang_items().range_to_struct(), - self.tcx.lang_items().range_full_struct(), - self.tcx.lang_items().range_inclusive_struct(), - self.tcx.lang_items().range_to_inclusive_struct(), - ]; - if type_def_id != None && ranges.contains(&type_def_id) { + let is_range = match type_def_id.and_then(|id| self.tcx.as_lang_item(id)) { + Some( + LangItem::Range + | LangItem::RangeFrom + | LangItem::RangeTo + | LangItem::RangeFull + | LangItem::RangeInclusiveStruct + | LangItem::RangeToInclusive, + ) => true, + _ => false, + }; + if is_range { if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { let msg = "constants only support matching by type, \ if you meant to match against a range of values, \ diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 5b4fc51cec885..56859eef45f72 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -2,9 +2,8 @@ use std::cell::RefCell; use std::ops::Deref; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, HirIdMap}; +use rustc_hir::{self as hir, HirId, HirIdMap, LangItem}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode}; @@ -84,7 +83,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; let infcx = - tcx.infer_ctxt().ignoring_regions().build(TypingMode::analysis_in_body(tcx, def_id)); + tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id)); let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); TypeckRootCtxt { @@ -137,7 +136,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { obligation.predicate.kind().skip_binder() && let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) - && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id) + && !self.tcx.is_lang_item(tpred.trait_ref.def_id, LangItem::Sized) { let new_self_ty = self.tcx.types.unit; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6ba7435cb790d..3caaa7f9a9d0b 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -8,6 +8,7 @@ use rustc_data_structures::unord::ExtendUnord; use rustc_errors::ErrorGuaranteed; use rustc_hir::intravisit::{self, InferKind, Visitor}; use rustc_hir::{self as hir, AmbigArg, HirId}; +use rustc_infer::traits::solve::Goal; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -763,7 +764,32 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { T: TypeFoldable>, { let value = self.fcx.resolve_vars_if_possible(value); - let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true)); + + let mut goals = vec![]; + let value = + value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true, &mut goals)); + + // Ensure that we resolve goals we get from normalizing coroutine interiors, + // but we shouldn't expect those goals to need normalizing (or else we'd get + // into a somewhat awkward fixpoint situation, and we don't need it anyways). + let mut unexpected_goals = vec![]; + self.typeck_results.coroutine_stalled_predicates.extend( + goals + .into_iter() + .map(|pred| { + self.fcx.resolve_vars_if_possible(pred).fold_with(&mut Resolver::new( + self.fcx, + span, + self.body, + false, + &mut unexpected_goals, + )) + }) + // FIXME: throwing away the param-env :( + .map(|goal| (goal.predicate, self.fcx.misc(span.to_span(self.fcx.tcx)))), + ); + assert_eq!(unexpected_goals, vec![]); + assert!(!value.has_infer()); // We may have introduced e.g. `ty::Error`, if inference failed, make sure @@ -781,7 +807,12 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { T: TypeFoldable>, { let value = self.fcx.resolve_vars_if_possible(value); - let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false)); + + let mut goals = vec![]; + let value = + value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false, &mut goals)); + assert_eq!(goals, vec![]); + assert!(!value.has_infer()); // We may have introduced e.g. `ty::Error`, if inference failed, make sure @@ -818,6 +849,7 @@ struct Resolver<'cx, 'tcx> { /// Whether we should normalize using the new solver, disabled /// both when using the old solver and when resolving predicates. should_normalize: bool, + nested_goals: &'cx mut Vec>>, } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { @@ -826,8 +858,9 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, should_normalize: bool, + nested_goals: &'cx mut Vec>>, ) -> Resolver<'cx, 'tcx> { - Resolver { fcx, span, body, should_normalize } + Resolver { fcx, span, body, nested_goals, should_normalize } } fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { @@ -864,12 +897,18 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); let at = self.fcx.at(&cause, self.fcx.param_env); let universes = vec![None; outer_exclusive_binder(value).as_usize()]; - solve::deeply_normalize_with_skipped_universes(at, value, universes).unwrap_or_else( - |errors| { + match solve::deeply_normalize_with_skipped_universes_and_ambiguous_goals( + at, value, universes, + ) { + Ok((value, goals)) => { + self.nested_goals.extend(goals); + value + } + Err(errors) => { let guar = self.fcx.err_ctxt().report_fulfillment_errors(errors); new_err(tcx, guar) - }, - ) + } + } } else { value }; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 529e996ad1e7d..c4698e5cbb456 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -967,7 +967,9 @@ impl<'tcx> InferCtxt<'tcx> { pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); match self.typing_mode() { - TypingMode::Analysis { defining_opaque_types } + TypingMode::Analysis { + defining_opaque_types_and_generators: defining_opaque_types, + } | TypingMode::Borrowck { defining_opaque_types } => { id.into().as_local().is_some_and(|def_id| defining_opaque_types.contains(&def_id)) } @@ -1262,7 +1264,7 @@ impl<'tcx> InferCtxt<'tcx> { // to handle them without proper canonicalization. This means we may cause cycle // errors and fail to reveal opaques while inside of bodies. We should rename this // function and require explicit comments on all use-sites in the future. - ty::TypingMode::Analysis { defining_opaque_types: _ } + ty::TypingMode::Analysis { defining_opaque_types_and_generators: _ } | ty::TypingMode::Borrowck { defining_opaque_types: _ } => { TypingMode::non_body_analysis() } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index ece18f4ea64ee..8b2aab4204228 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -16,12 +16,12 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(extend_one)] #![feature(iterator_try_collect)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 1eae10673b62b..9e51a53ae95fa 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -94,7 +94,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { /// Among all pending obligations, collect those are stalled on a inference variable which has /// changed since the last call to `select_where_possible`. Those obligations are marked as /// successful and returned. - fn drain_unstalled_obligations( + fn drain_stalled_obligations_for_coroutines( &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx>; diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 67e0be93523d9..4128070718308 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,8 +1,8 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(decl_macro)] #![feature(file_buffered)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 212368bea8266..96705e79e0a41 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -21,6 +21,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -28,7 +29,6 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_order_by)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(try_blocks)] @@ -608,6 +608,11 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see PR #139001 \ for more information", ); + store.register_removed( + "abi_unsupported_vector_types", + "converted into hard error, \ + see for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index 571cab934fd6a..00fa0499556cb 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -1,4 +1,4 @@ -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, impl_lint_pass}; @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { return; }; - if Some(method_def_id) != cx.tcx.lang_items().into_iter_fn() { + if !cx.tcx.is_lang_item(method_def_id, LangItem::IntoIterIntoIter) { return; } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a084dacfb5be8..f9dce5a5198d0 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,7 +1,9 @@ use std::iter; use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, TagEncoding, VariantIdx, Variants, WrappingRange}; +use rustc_abi::{ + BackendRepr, Integer, IntegerType, TagEncoding, VariantIdx, Variants, WrappingRange, +}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::intravisit::VisitorExt; @@ -1243,6 +1245,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } + if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_128bit, + help: None, + }; + } + use improper_ctypes::check_non_exhaustive_variant; let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b25d2a30681c0..17d501c5730b8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -16,7 +16,6 @@ declare_lint_pass! { /// that are used by other parts of the compiler. HardwiredLints => [ // tidy-alphabetical-start - ABI_UNSUPPORTED_VECTOR_TYPES, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, AMBIGUOUS_GLOB_IMPORTS, @@ -118,6 +117,7 @@ declare_lint_pass! { UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNNECESSARY_TRANSMUTES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -4910,6 +4910,30 @@ declare_lint! { "detects pointer to integer transmutes in const functions and associated constants", } +declare_lint! { + /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives. + /// + /// ### Example + /// + /// ```rust + /// fn bytes_at_home(x: [u8; 4]) -> u32 { + /// unsafe { std::mem::transmute(x) } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Using an explicit method is preferable over calls to + /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as + /// they more clearly communicate the intent, are easier to review, and + /// are less likely to accidentally result in unsoundness. + pub UNNECESSARY_TRANSMUTES, + Warn, + "detects transmutes that are shadowed by std methods" +} + declare_lint! { /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, /// that runs a custom `Drop` destructor. @@ -5027,74 +5051,6 @@ declare_lint! { crate_level_only } -declare_lint! { - /// The `abi_unsupported_vector_types` lint detects function definitions and calls - /// whose ABI depends on enabling certain target features, but those features are not enabled. - /// - /// ### Example - /// - /// ```rust,ignore (fails on non-x86_64) - /// extern "C" fn missing_target_feature(_: std::arch::x86_64::__m256) { - /// todo!() - /// } - /// - /// #[target_feature(enable = "avx")] - /// unsafe extern "C" fn with_target_feature(_: std::arch::x86_64::__m256) { - /// todo!() - /// } - /// - /// fn main() { - /// let v = unsafe { std::mem::zeroed() }; - /// unsafe { with_target_feature(v); } - /// } - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller - /// --> lint_example.rs:18:12 - /// | - /// | unsafe { with_target_feature(v); } - /// | ^^^^^^^^^^^^^^^^^^^^^^ function called here - /// | - /// = 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 #116558 - /// = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) - /// = note: `#[warn(abi_unsupported_vector_types)]` on by default - /// - /// - /// warning: ABI error: this function definition uses a avx vector type, which is not enabled - /// --> lint_example.rs:3:1 - /// | - /// | pub extern "C" fn with_target_feature(_: std::arch::x86_64::__m256) { - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - /// | - /// = 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 #116558 - /// = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) - /// ``` - /// - /// - /// - /// ### Explanation - /// - /// The C ABI for `__m256` requires the value to be passed in an AVX register, - /// which is only possible when the `avx` target feature is enabled. - /// Therefore, `missing_target_feature` cannot be compiled without that target feature. - /// A similar (but complementary) message is triggered when `with_target_feature` is called - /// by a function that does not enable the `avx` target feature. - /// - /// Note that this lint is very similar to the `-Wpsabi` warning in `gcc`/`clang`. - pub ABI_UNSUPPORTED_VECTOR_TYPES, - Warn, - "this function call or definition uses a vector type which is not enabled", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #116558 ", - }; -} - declare_lint! { /// The `wasm_c_abi` lint detects usage of the `extern "C"` ABI of wasm that is affected /// by a planned ABI change that has the goal of aligning Rust with the standard C ABI diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index e02c80c50b14a..8bee051dd4c3c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -14,6 +14,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" +#include "llvm/IRPrinter/IRPrintingPasses.h" #include "llvm/LTO/LTO.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/TargetRegistry.h" @@ -703,7 +704,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( bool LintIR, LLVMRustThinLTOBuffer **ThinLTOBufferRef, bool EmitThinLTO, bool EmitThinLTOSummary, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, bool RunEnzyme, + bool EmitLifetimeMarkers, bool RunEnzyme, bool PrintBeforeEnzyme, + bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, @@ -1048,14 +1050,38 @@ extern "C" LLVMRustResult LLVMRustOptimize( // now load "-enzyme" pass: #ifdef ENZYME if (RunEnzyme) { - registerEnzymeAndPassPipeline(PB, true); + + if (PrintBeforeEnzyme) { + // Handle the Rust flag `-Zautodiff=PrintModBefore`. + std::string Banner = "Module before EnzymeNewPM"; + MPM.addPass(PrintModulePass(outs(), Banner, true, false)); + } + + registerEnzymeAndPassPipeline(PB, false); if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) { std::string ErrMsg = toString(std::move(Err)); LLVMRustSetLastError(ErrMsg.c_str()); return LLVMRustResult::Failure; } + + if (PrintAfterEnzyme) { + // Handle the Rust flag `-Zautodiff=PrintModAfter`. + std::string Banner = "Module after EnzymeNewPM"; + MPM.addPass(PrintModulePass(outs(), Banner, true, false)); + } } #endif + if (PrintPasses) { + // Print all passes from the PM: + std::string Pipeline; + raw_string_ostream SOS(Pipeline); + MPM.printPipeline(SOS, [&PIC](StringRef ClassName) { + auto PassName = PIC.getPassNameForClassName(ClassName); + return PassName.empty() ? ClassName : PassName; + }); + outs() << Pipeline; + outs() << "\n"; + } // Upgrade all calls to old intrinsics first. for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 68058250a2671..ed5edeef1617d 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -229,6 +229,7 @@ pub fn initialize_available_targets() { LLVMInitializeXtensaTargetInfo, LLVMInitializeXtensaTarget, LLVMInitializeXtensaTargetMC, + LLVMInitializeXtensaAsmPrinter, LLVMInitializeXtensaAsmParser ); init_target!( diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 91398f1a9da9d..55228248188e5 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -55,8 +55,7 @@ use synstructure::Structure; /// /// See rustc dev guide for more examples on using the `#[derive(Diagnostic)]`: /// -pub(super) fn diagnostic_derive(mut s: Structure<'_>) -> TokenStream { - s.underscore_const(true); +pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream { DiagnosticDerive::new(s).into_tokens() } @@ -102,8 +101,7 @@ pub(super) fn diagnostic_derive(mut s: Structure<'_>) -> TokenStream { /// /// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`: /// -pub(super) fn lint_diagnostic_derive(mut s: Structure<'_>) -> TokenStream { - s.underscore_const(true); +pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { LintDiagnosticDerive::new(s).into_tokens() } @@ -153,7 +151,6 @@ pub(super) fn lint_diagnostic_derive(mut s: Structure<'_>) -> TokenStream { /// /// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident }); /// ``` -pub(super) fn subdiagnostic_derive(mut s: Structure<'_>) -> TokenStream { - s.underscore_const(true); +pub(super) fn subdiagnostic_derive(s: Structure<'_>) -> TokenStream { SubdiagnosticDerive::new().into_tokens(s) } diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index 6b3210cad7be6..a6396ba687d11 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -74,8 +74,6 @@ fn hash_stable_derive_with_mode( HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX), }; - s.underscore_const(true); - // 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, diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index edb25e799045a..62ca7ce3ca995 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,7 +1,7 @@ // tidy-alphabetical-start #![allow(rustc::default_hash_types)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] diff --git a/compiler/rustc_macros/src/lift.rs b/compiler/rustc_macros/src/lift.rs index 341447f7e6f26..03ea396a42c75 100644 --- a/compiler/rustc_macros/src/lift.rs +++ b/compiler/rustc_macros/src/lift.rs @@ -4,7 +4,6 @@ use syn::parse_quote; pub(super) fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { s.add_bounds(synstructure::AddBounds::Generics); s.bind_with(|_| synstructure::BindStyle::Move); - s.underscore_const(true); let tcx: syn::Lifetime = parse_quote!('tcx); let newtcx: syn::GenericParam = parse_quote!('__lifted); diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 673e6cd618ff8..c7aaaf0da4679 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -12,7 +12,6 @@ pub(super) fn type_decodable_derive( let decoder_ty = quote! { __D }; s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_middle::ty::codec::TyDecoder<'tcx> }); s.add_bounds(synstructure::AddBounds::Fields); - s.underscore_const(true); decodable_body(s, decoder_ty) } @@ -26,7 +25,6 @@ pub(super) fn meta_decodable_derive( s.add_impl_generic(parse_quote! { '__a }); let decoder_ty = quote! { DecodeContext<'__a, 'tcx> }; s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); decodable_body(s, decoder_ty) } @@ -35,7 +33,6 @@ pub(super) fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro let decoder_ty = quote! { __D }; s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder }); s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); decodable_body(s, decoder_ty) } @@ -46,13 +43,12 @@ pub(super) fn decodable_nocontext_derive( let decoder_ty = quote! { __D }; s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_serialize::Decoder }); s.add_bounds(synstructure::AddBounds::Fields); - s.underscore_const(true); decodable_body(s, decoder_ty) } fn decodable_body( - mut s: synstructure::Structure<'_>, + s: synstructure::Structure<'_>, decoder_ty: TokenStream, ) -> proc_macro2::TokenStream { if let syn::Data::Union(_) = s.ast().data { @@ -98,7 +94,6 @@ fn decodable_body( } } }; - s.underscore_const(true); s.bound_impl( quote!(::rustc_serialize::Decodable<#decoder_ty>), @@ -133,7 +128,6 @@ pub(super) fn type_encodable_derive( } s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_middle::ty::codec::TyEncoder<'tcx> }); s.add_bounds(synstructure::AddBounds::Fields); - s.underscore_const(true); encodable_body(s, encoder_ty, false) } @@ -147,7 +141,6 @@ pub(super) fn meta_encodable_derive( s.add_impl_generic(parse_quote! { '__a }); let encoder_ty = quote! { EncodeContext<'__a, 'tcx> }; s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); encodable_body(s, encoder_ty, true) } @@ -156,7 +149,6 @@ pub(super) fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro let encoder_ty = quote! { __E }; s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder }); s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); encodable_body(s, encoder_ty, false) } @@ -167,7 +159,6 @@ pub(super) fn encodable_nocontext_derive( let encoder_ty = quote! { __E }; s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder }); s.add_bounds(synstructure::AddBounds::Fields); - s.underscore_const(true); encodable_body(s, encoder_ty, false) } @@ -181,7 +172,6 @@ fn encodable_body( panic!("cannot derive on union") } - s.underscore_const(true); s.bind_with(|binding| { // Handle the lack of a blanket reference impl. if let syn::Type::Reference(_) = binding.ast().ty { diff --git a/compiler/rustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs index 85051311bee9e..91b747f18569d 100644 --- a/compiler/rustc_macros/src/type_foldable.rs +++ b/compiler/rustc_macros/src/type_foldable.rs @@ -6,8 +6,6 @@ pub(super) fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_m panic!("cannot derive on union") } - s.underscore_const(true); - if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { s.add_impl_generic(parse_quote! { 'tcx }); } diff --git a/compiler/rustc_macros/src/type_visitable.rs b/compiler/rustc_macros/src/type_visitable.rs index fb37e1a39edbe..f99c5113a6053 100644 --- a/compiler/rustc_macros/src/type_visitable.rs +++ b/compiler/rustc_macros/src/type_visitable.rs @@ -8,8 +8,6 @@ pub(super) fn type_visitable_derive( panic!("cannot derive on union") } - s.underscore_const(true); - // ignore fields with #[type_visitable(ignore)] s.filter(|bi| { let mut ignored = false; diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 08dcc3d519a2b..b11f9260be7c0 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -26,7 +26,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -tempfile = "3.2" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index c4e1e0f1d1a99..e57534b847ef0 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -2,11 +2,11 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_fs_util::TempDirBuilder; use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; -use tempfile::Builder as TempFileBuilder; use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, @@ -45,7 +45,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // final destination, with an `fs::rename` call. In order for the rename to // always succeed, the temporary file needs to be on the same filesystem, // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() + let metadata_tmpdir = TempDirBuilder::new() .prefix("rmeta") .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err })); diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 3b44c44fcb94e..3931be1654a35 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(coroutines)] @@ -8,7 +9,6 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] -#![feature(let_chains)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 8fe2cc7101ba3..df025aeebf048 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -29,6 +29,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] @@ -48,7 +49,6 @@ #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] -#![feature(let_chains)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 7a91bfad4836c..0f92c1910f1bb 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -35,11 +35,10 @@ impl<'tcx> TyCtxt<'tcx> { /// returns a corresponding [`ty::ClosureKind`]. /// For any other [`DefId`] return `None`. pub fn fn_trait_kind_from_def_id(self, id: DefId) -> Option { - let items = self.lang_items(); - match Some(id) { - x if x == items.fn_trait() => Some(ty::ClosureKind::Fn), - x if x == items.fn_mut_trait() => Some(ty::ClosureKind::FnMut), - x if x == items.fn_once_trait() => Some(ty::ClosureKind::FnOnce), + match self.as_lang_item(id)? { + LangItem::Fn => Some(ty::ClosureKind::Fn), + LangItem::FnMut => Some(ty::ClosureKind::FnMut), + LangItem::FnOnce => Some(ty::ClosureKind::FnOnce), _ => None, } } @@ -48,11 +47,10 @@ impl<'tcx> TyCtxt<'tcx> { /// returns a corresponding [`ty::ClosureKind`]. /// For any other [`DefId`] return `None`. pub fn async_fn_trait_kind_from_def_id(self, id: DefId) -> Option { - let items = self.lang_items(); - match Some(id) { - x if x == items.async_fn_trait() => Some(ty::ClosureKind::Fn), - x if x == items.async_fn_mut_trait() => Some(ty::ClosureKind::FnMut), - x if x == items.async_fn_once_trait() => Some(ty::ClosureKind::FnOnce), + match self.as_lang_item(id)? { + LangItem::AsyncFn => Some(ty::ClosureKind::Fn), + LangItem::AsyncFnMut => Some(ty::ClosureKind::FnMut), + LangItem::AsyncFnOnce => Some(ty::ClosureKind::FnOnce), _ => None, } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e94f088304b19..3432648feab07 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -387,6 +387,15 @@ rustc_queries! { } } + query stalled_generators_within( + key: LocalDefId + ) -> &'tcx ty::List { + desc { + |tcx| "computing the coroutines defined within `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + /// Returns the explicitly user-written *bounds* on the associated or opaque type given by `DefId` /// that must be proven true at definition site (and which can be assumed at usage sites). /// diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index a099f77041709..69b6f88d72bfd 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -366,7 +366,7 @@ macro_rules! define_callbacks { pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; - // Ensure that keys grow no larger than 80 bytes by accident. + // Ensure that keys grow no larger than 88 bytes by accident. // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(target_pointer_width = "64")] const _: () = { diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 0c44fd2758d52..78b2e265b488c 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -246,6 +246,8 @@ impl AssocItems { } /// Returns an iterator over all associated items with the given name, ignoring hygiene. + /// + /// Panics if `name.is_empty()` returns `true`. pub fn filter_by_name_unhygienic( &self, name: Symbol, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fa2f1cf1a1c87..e8dad1e056cbd 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -106,7 +106,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Self::PredefinedOpaques { self.mk_predefined_opaques_in_body(data) } - type DefiningOpaqueTypes = &'tcx ty::List; + type LocalDefIds = &'tcx ty::List; type CanonicalVars = CanonicalVarInfos<'tcx>; fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { self.mk_canonical_var_infos(infos) @@ -590,6 +590,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.defaultness(def_id).has_value() } + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + self.specializes((impl_def_id, victim_def_id)) + } + fn impl_is_default(self, impl_def_id: DefId) -> bool { self.defaultness(impl_def_id).is_default() } @@ -674,9 +678,24 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.anonymize_bound_vars(binder) } - fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::DefiningOpaqueTypes { + fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::LocalDefIds { self.opaque_types_defined_by(defining_anchor) } + + fn opaque_types_and_generators_defined_by( + self, + defining_anchor: Self::LocalDefId, + ) -> Self::LocalDefIds { + if self.next_trait_solver_globally() { + self.mk_local_def_ids_from_iter( + self.opaque_types_defined_by(defining_anchor) + .iter() + .chain(self.stalled_generators_within(defining_anchor)), + ) + } else { + self.opaque_types_defined_by(defining_anchor) + } + } } macro_rules! bidirectional_lang_item_map { @@ -2906,11 +2925,11 @@ impl<'tcx> TyCtxt<'tcx> { self.interners.intern_clauses(clauses) } - pub fn mk_local_def_ids(self, clauses: &[LocalDefId]) -> &'tcx List { + pub fn mk_local_def_ids(self, def_ids: &[LocalDefId]) -> &'tcx List { // FIXME consider asking the input slice to be sorted to avoid // re-interning permutations, in which case that would be asserted // here. - self.intern_local_def_ids(clauses) + self.intern_local_def_ids(def_ids) } pub fn mk_local_def_ids_from_iter(self, iter: I) -> T::Output diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 02e316dfc3db7..551d816941b6e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -121,11 +121,10 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { - // Keep this in sync with the one in `rustc_type_ir::inherent`! match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) - | PredicateKind::AliasRelate(..) - | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { + false + } PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) @@ -137,6 +136,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Coerce(_) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) | PredicateKind::ConstEquate(_, _) + | PredicateKind::NormalizesTo(..) | PredicateKind::Ambiguous => true, } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 301ae60457434..affb7b31ae1d8 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1774,9 +1774,7 @@ impl<'tcx> Ty<'tcx> { match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) { Ok(metadata_ty) => metadata_ty, Err(tail_ty) => { - let Some(metadata_def_id) = tcx.lang_items().metadata_type() else { - bug!("No metadata_type lang item while looking at {self:?}") - }; + let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); Ty::new_projection(tcx, metadata_def_id, [tail_ty]) } } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 73cc3c86e22f7..210b9cce581fd 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -146,6 +146,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut place = place; let mut block = block; match ty.kind() { + ty::Str => { + // String literal patterns may have type `str` if `deref_patterns` is + // enabled, in order to allow `deref!("..."): String`. In this case, `value` + // is of type `&str`, so we compare it to `&place`. + if !tcx.features().deref_patterns() { + span_bug!( + test.span, + "matching on `str` went through without enabling deref_patterns" + ); + } + let re_erased = tcx.lifetimes.re_erased; + let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); + let ref_place = self.temp(ref_str_ty, test.span); + // `let ref_place: &str = &place;` + self.cfg.push_assign( + block, + self.source_info(test.span), + ref_place, + Rvalue::Ref(re_erased, BorrowKind::Shared, place), + ); + place = ref_place; + ty = ref_str_ty; + } ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { if !tcx.features().string_deref_patterns() { span_bug!( diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index e42336a1dbbcc..4e4b11b8fa6b2 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1530,7 +1530,7 @@ fn build_scope_drops<'tcx>( // path, then don't generate the drop. (We only take this into // account for non-unwind paths so as not to disturb the // caching mechanism.) - if scope.moved_locals.iter().any(|&o| o == local) { + if scope.moved_locals.contains(&local) { continue; } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 8e96d46dac272..a051cf570b7d1 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -3,10 +3,10 @@ // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index b3210813703ca..b4fa55e1c1fdb 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -37,13 +37,23 @@ pub(crate) fn lit_to_const<'tcx>( let str_bytes = s.as_str().as_bytes(); ty::ValTree::from_raw_bytes(tcx, str_bytes) } + (ast::LitKind::Str(s, _), ty::Str) if tcx.features().deref_patterns() => { + // String literal patterns may have type `str` if `deref_patterns` is enabled, in order + // to allow `deref!("..."): String`. + let str_bytes = s.as_str().as_bytes(); + ty::ValTree::from_raw_bytes(tcx, str_bytes) + } (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) - if matches!(inner_ty.kind(), ty::Slice(_)) => + if matches!(inner_ty.kind(), ty::Slice(_) | ty::Array(..)) => { let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } - (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + (ast::LitKind::ByteStr(data, _), ty::Slice(_) | ty::Array(..)) + if tcx.features().deref_patterns() => + { + // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is + // enabled, in order to allow, e.g., `deref!(b"..."): Vec`. let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a40001bf74556..b7d203e3cd78c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -280,6 +280,16 @@ impl<'tcx> ConstToPat<'tcx> { slice: None, suffix: Box::new([]), }, + ty::Str => { + // String literal patterns may have type `str` if `deref_patterns` is enabled, in + // order to allow `deref!("..."): String`. Since we need a `&str` for the comparison + // when lowering to MIR in `Builder::perform_test`, treat the constant as a `&str`. + // This works because `str` and `&str` have the same valtree representation. + let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty); + PatKind::Constant { + value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)), + } + } ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { // `&str` is represented as a valtree, let's keep using this // optimization for now. diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index c46ae9775cf68..d5005768b80bd 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -114,26 +114,11 @@ where self.reachable_blocks.insert_all() } - /// Returns the underlying `Results`. - pub fn results(&self) -> &Results<'tcx, A> { - &self.results - } - - /// Returns the underlying `Results`. - pub fn mut_results(&mut self) -> &mut Results<'tcx, A> { - &mut self.results - } - /// Returns the `Analysis` used to generate the underlying `Results`. pub fn analysis(&self) -> &A { &self.results.analysis } - /// Returns the `Analysis` used to generate the underlying `Results`. - pub fn mut_analysis(&mut self) -> &mut A { - &mut self.results.analysis - } - /// Resets the cursor to hold the entry set for the given basic block. /// /// For forward dataflow analyses, this is the dataflow state prior to the first statement. diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 3d7f9e2d8e71b..b8c26dad59f41 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -43,7 +43,7 @@ pub trait Direction { block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>; } @@ -212,7 +212,7 @@ impl Direction for Backward { block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { @@ -394,7 +394,7 @@ impl Direction for Forward { block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 95f488a925b2c..b5e9a0b893247 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -201,11 +201,12 @@ struct Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { + body: &'mir Body<'tcx>, // The `RefCell` is used because `::node_label` - // takes `&self`, but it needs to modify the cursor. This is also the + // takes `&self`, but it needs to modify the results. This is also the // reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has // the operations that involve the mutation, i.e. within the `borrow_mut`. - cursor: RefCell>, + results: RefCell<&'mir mut Results<'tcx, A>>, style: OutputStyle, reachable: DenseBitSet, } @@ -220,11 +221,7 @@ where style: OutputStyle, ) -> Self { let reachable = traversal::reachable_as_bitset(body); - Formatter { cursor: results.as_results_cursor(body).into(), style, reachable } - } - - fn body(&self) -> &'mir Body<'tcx> { - self.cursor.borrow().body() + Formatter { body, results: results.into(), style, reachable } } } @@ -253,7 +250,7 @@ where type Edge = CfgEdge; fn graph_id(&self) -> dot::Id<'_> { - let name = graphviz_safe_def_name(self.body().source.def_id()); + let name = graphviz_safe_def_name(self.body.source.def_id()); dot::Id::new(format!("graph_for_def_id_{name}")).unwrap() } @@ -262,10 +259,16 @@ where } fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { - let mut cursor = self.cursor.borrow_mut(); - let mut fmt = - BlockFormatter { cursor: &mut cursor, style: self.style, bg: Background::Light }; - let label = fmt.write_node_label(*block).unwrap(); + let mut results = self.results.borrow_mut(); + + let diffs = StateDiffCollector::run(self.body, *block, *results, self.style); + + let mut fmt = BlockFormatter { + cursor: results.as_results_cursor(self.body), + style: self.style, + bg: Background::Light, + }; + let label = fmt.write_node_label(*block, diffs).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -275,7 +278,7 @@ where } fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { - let label = &self.body()[e.source].terminator().kind.fmt_successor_labels()[e.index]; + let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; dot::LabelText::label(label.clone()) } } @@ -288,7 +291,7 @@ where type Edge = CfgEdge; fn nodes(&self) -> dot::Nodes<'_, Self::Node> { - self.body() + self.body .basic_blocks .indices() .filter(|&idx| self.reachable.contains(idx)) @@ -297,10 +300,10 @@ where } fn edges(&self) -> dot::Edges<'_, Self::Edge> { - let body = self.body(); - body.basic_blocks + self.body + .basic_blocks .indices() - .flat_map(|bb| dataflow_successors(body, bb)) + .flat_map(|bb| dataflow_successors(self.body, bb)) .collect::>() .into() } @@ -310,20 +313,20 @@ where } fn target(&self, edge: &Self::Edge) -> Self::Node { - self.body()[edge.source].terminator().successors().nth(edge.index).unwrap() + self.body[edge.source].terminator().successors().nth(edge.index).unwrap() } } -struct BlockFormatter<'a, 'mir, 'tcx, A> +struct BlockFormatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { - cursor: &'a mut ResultsCursor<'mir, 'tcx, A>, + cursor: ResultsCursor<'mir, 'tcx, A>, bg: Background, style: OutputStyle, } -impl<'tcx, A> BlockFormatter<'_, '_, 'tcx, A> +impl<'tcx, A> BlockFormatter<'_, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -336,7 +339,11 @@ where bg } - fn write_node_label(&mut self, block: BasicBlock) -> io::Result> { + fn write_node_label( + &mut self, + block: BasicBlock, + diffs: StateDiffCollector, + ) -> io::Result> { use std::io::Write; // Sample output: @@ -392,7 +399,7 @@ where self.write_row_with_full_state(w, "", "(on start)")?; // D + E: Statement and terminator transfer functions - self.write_statements_and_terminator(w, block)?; + self.write_statements_and_terminator(w, block, diffs)?; // F: State at end of block @@ -575,14 +582,8 @@ where &mut self, w: &mut impl io::Write, block: BasicBlock, + diffs: StateDiffCollector, ) -> io::Result<()> { - let diffs = StateDiffCollector::run( - self.cursor.body(), - block, - self.cursor.mut_results(), - self.style, - ); - let mut diffs_before = diffs.before.map(|v| v.into_iter()); let mut diffs_after = diffs.after.into_iter(); @@ -709,7 +710,7 @@ impl StateDiffCollector { } } -impl<'tcx, A> ResultsVisitor<'_, 'tcx, A> for StateDiffCollector +impl<'tcx, A> ResultsVisitor<'tcx, A> for StateDiffCollector where A: Analysis<'tcx>, A::Domain: DebugWithContext, diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs index 8e2c3afddb352..93dfc06a878aa 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -47,7 +47,7 @@ where &mut self, body: &'mir Body<'tcx>, blocks: impl IntoIterator, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + vis: &mut impl ResultsVisitor<'tcx, A>, ) { visit_results(body, blocks, self, vis) } @@ -55,7 +55,7 @@ where pub fn visit_reachable_with<'mir>( &mut self, body: &'mir Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + vis: &mut impl ResultsVisitor<'tcx, A>, ) { let blocks = traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index a03aecee7be12..c9fdf46c4f5b2 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -8,7 +8,7 @@ pub fn visit_results<'mir, 'tcx, A>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { @@ -29,7 +29,7 @@ pub fn visit_results<'mir, 'tcx, A>( /// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in /// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain /// locations. -pub trait ResultsVisitor<'mir, 'tcx, A> +pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { @@ -40,7 +40,7 @@ where &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, - _statement: &'mir mir::Statement<'tcx>, + _statement: &mir::Statement<'tcx>, _location: Location, ) { } @@ -50,7 +50,7 @@ where &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, - _statement: &'mir mir::Statement<'tcx>, + _statement: &mir::Statement<'tcx>, _location: Location, ) { } @@ -60,7 +60,7 @@ where &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, - _terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, _location: Location, ) { } @@ -72,7 +72,7 @@ where &mut self, _results: &mut Results<'tcx, A>, _state: &A::Domain, - _terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, _location: Location, ) { } diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index a0efc623b8e7e..38f82b1274658 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,10 +1,10 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(file_buffered)] -#![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 5d2a78acbf526..21590ff1bbad4 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -120,12 +120,12 @@ struct Visitor<'a, N: Idx> { values: SparseIntervalMatrix, } -impl<'mir, 'tcx, A, N> ResultsVisitor<'mir, 'tcx, A> for Visitor<'_, N> +impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N> where A: Analysis<'tcx, Domain = DenseBitSet>, N: Idx, { - fn visit_after_primary_statement_effect( + fn visit_after_primary_statement_effect<'mir>( &mut self, _results: &mut Results<'tcx, A>, state: &A::Domain, @@ -139,7 +139,7 @@ where }); } - fn visit_after_primary_terminator_effect( + fn visit_after_primary_terminator_effect<'mir>( &mut self, _results: &mut Results<'tcx, A>, state: &A::Domain, diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index 5628f4c9381b3..a1264471a2df5 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -84,3 +84,4 @@ mir_transform_undefined_transmute = pointers cannot be transmuted to integers du .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored +mir_transform_unnecessary_transmute = unnecessary transmute diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index b70cca1484070..5115583f37c0f 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -1,3 +1,4 @@ +use rustc_abi::Align; use rustc_index::IndexVec; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::PlaceContext; @@ -11,10 +12,6 @@ pub(super) struct CheckAlignment; impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { fn is_enabled(&self, sess: &Session) -> bool { - // FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows - if sess.target.llvm_target == "i686-pc-windows-msvc" { - return false; - } sess.ub_checks() } @@ -87,6 +84,33 @@ fn insert_alignment_check<'tcx>( ))), }); + // If this target does not have reliable alignment, further limit the mask by anding it with + // the mask for the highest reliable alignment. + #[allow(irrefutable_let_patterns)] + if let max_align = tcx.sess.target.max_reliable_alignment() + && max_align < Align::MAX + { + let max_mask = max_align.bytes() - 1; + let max_mask = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val( + ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)), + tcx.types.usize, + ), + })); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + alignment_mask, + Rvalue::BinaryOp( + BinOp::BitAnd, + Box::new((Operand::Copy(alignment_mask), max_mask)), + ), + ))), + }); + } + // BitAnd the alignment mask with the pointer let alignment_bits = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index e49723a6c39db..cace4cd6bba56 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; +use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_middle::mir::{self, BasicBlock, BasicBlocks, Body, Terminator, TerminatorKind}; use rustc_middle::ty::{self, GenericArg, GenericArgs, Instance, Ty, TyCtxt}; @@ -44,8 +45,7 @@ impl<'tcx> MirLint<'tcx> for CheckDropRecursion { if let DefKind::AssocFn = tcx.def_kind(def_id) && let Some(trait_ref) = tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) - && let Some(drop_trait) = tcx.lang_items().drop_trait() - && drop_trait == trait_ref.instantiate_identity().def_id + && tcx.is_lang_item(trait_ref.instantiate_identity().def_id, LangItem::Drop) // avoid erroneous `Drop` impls from causing ICEs below && let sig = tcx.fn_sig(def_id).instantiate_identity() && sig.inputs().skip_binder().len() == 1 diff --git a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs new file mode 100644 index 0000000000000..4aff127908eec --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs @@ -0,0 +1,97 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; +use rustc_middle::ty::*; +use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES; +use rustc_span::source_map::Spanned; +use rustc_span::{Span, sym}; + +use crate::errors::UnnecessaryTransmute as Error; + +/// Check for transmutes that overlap with stdlib methods. +/// For example, transmuting `[u8; 4]` to `u32`. +pub(super) struct CheckUnnecessaryTransmutes; + +impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut checker = UnnecessaryTransmuteChecker { body, tcx }; + checker.visit_body(body); + } +} + +struct UnnecessaryTransmuteChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> { + fn is_unnecessary_transmute( + &self, + function: &Operand<'tcx>, + arg: String, + span: Span, + ) -> Option { + let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); + let [input] = fn_sig.inputs() else { return None }; + + let err = |sugg| Error { span, sugg, help: None }; + + Some(match (input.kind(), fn_sig.output().kind()) { + // dont check the length; transmute does that for us. + // [u8; _] => primitive + (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()), + help: Some( + "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order", + ), + span, + }, + // primitive => [u8; _] + (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{input}::to_ne_bytes({arg})"), + help: Some( + "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order", + ), + span, + }, + // char → u32 + (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")), + // u32 → char + (Uint(UintTy::U32), Char) => Error { + sugg: format!("char::from_u32_unchecked({arg})"), + help: Some("consider `char::from_u32(…).unwrap()`"), + span, + }, + // uNN → iNN + (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())), + // iNN → uNN + (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())), + // fNN → uNN + (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())), + // uNN → fNN + (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())), + // bool → { x8 } + (Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())), + // u8 → bool + (Uint(_), Bool) => err(format!("({arg} == 1)")), + _ => return None, + }) + } +} + +impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> { + // Check each block's terminator for calls to pointer to integer transmutes + // in const functions or associated constants and emit a lint. + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + if let TerminatorKind::Call { func, args, .. } = &terminator.kind + && let [Spanned { span: arg, .. }] = **args + && let Some((func_def_id, _)) = func.const_fn_def() + && self.tcx.is_intrinsic(func_def_id, sym::transmute) + && let span = self.body.source_info(location).span + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg) + && let Some(lint) = self.is_unnecessary_transmute(func, snippet, span) + && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes) + { + self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint); + } + } +} diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 92d5e152f58e6..0eed46c72f993 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -875,14 +875,14 @@ struct StorageConflictVisitor<'a, 'tcx> { eligible_storage_live: DenseBitSet, } -impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> +impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>> for StorageConflictVisitor<'a, 'tcx> { fn visit_after_early_statement_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, state: &DenseBitSet, - _statement: &'a Statement<'tcx>, + _statement: &Statement<'tcx>, loc: Location, ) { self.apply_state(state, loc); @@ -892,7 +892,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, state: &DenseBitSet, - _terminator: &'a Terminator<'tcx>, + _terminator: &Terminator<'tcx>, loc: Location, ) { self.apply_state(state, loc); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index aa4c0ef1e1f93..51c4e95e74b85 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,8 +1,7 @@ -pub(super) mod query; - mod counters; mod graph; mod mappings; +pub(super) mod query; mod spans; #[cfg(test)] mod tests; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 90173da17f0fc..a2103a004d2d1 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -958,13 +958,13 @@ fn try_write_constant<'tcx>( interp_ok(()) } -impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> { +impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> { #[instrument(level = "trace", skip(self, results, statement))] fn visit_after_early_statement_effect( &mut self, results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &State>, - statement: &'mir Statement<'tcx>, + statement: &Statement<'tcx>, location: Location, ) { match &statement.kind { @@ -986,7 +986,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect &mut self, results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &State>, - statement: &'mir Statement<'tcx>, + statement: &Statement<'tcx>, location: Location, ) { match statement.kind { @@ -1011,7 +1011,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect &mut self, results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, state: &State>, - terminator: &'mir Terminator<'tcx>, + terminator: &Terminator<'tcx>, location: Location, ) { OperandCollector { diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 29698b0c2e445..5b03a4987ed71 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -158,6 +158,26 @@ pub(crate) struct MustNotSuspendReason { pub reason: String, } +pub(crate) struct UnnecessaryTransmute { + pub span: Span, + pub sugg: String, + pub help: Option<&'static str>, +} + +// Needed for def_path_str +impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::mir_transform_unnecessary_transmute); + diag.span_suggestion( + self.span, + "replace this with", + self.sugg, + lint::Applicability::MachineApplicable, + ); + self.help.map(|help| diag.help(help)); + } +} + #[derive(LintDiagnostic)] #[diag(mir_transform_undefined_transmute)] #[note] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 4d74ecddfb057..5db62b7e90200 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -7,7 +8,6 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] @@ -125,6 +125,7 @@ declare_passes! { mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; mod check_undefined_transmutes : CheckUndefinedTransmutes; + mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -391,6 +392,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), &Lint(check_undefined_transmutes::CheckUndefinedTransmutes), + &Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index adfe096f0cdcb..0dd20bbb35f8a 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -70,10 +70,11 @@ pub(crate) struct UnknownCguCollectionMode<'a> { pub mode: &'a str, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag(monomorphize_abi_error_disabled_vector_type)] #[help] pub(crate) struct AbiErrorDisabledVectorType<'a> { + #[primary_span] #[label] pub span: Span, pub required_feature: &'a str, @@ -82,9 +83,10 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> { pub is_call: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag(monomorphize_abi_error_unsupported_vector_type)] pub(crate) struct AbiErrorUnsupportedVectorType<'a> { + #[primary_span] #[label] pub span: Span, pub ty: Ty<'a>, diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 8f6914f3d7242..8469e0f17a69d 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] -#![feature(let_chains)] // tidy-alphabetical-end use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 94ee34c8b7bf3..cfeaee0777610 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -5,7 +5,7 @@ use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_middle::mir::{self, Location, traversal}; use rustc_middle::ty::layout::LayoutCx; use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypingEnv}; -use rustc_session::lint::builtin::{ABI_UNSUPPORTED_VECTOR_TYPES, WASM_C_ABI}; +use rustc_session::lint::builtin::WASM_C_ABI; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_target::callconv::{ArgAbi, Conv, FnAbi, PassMode}; @@ -50,34 +50,24 @@ fn do_check_simd_vector_abi<'tcx>( let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { Some((_, feature)) => feature, None => { - let (span, hir_id) = loc(); - tcx.emit_node_span_lint( - ABI_UNSUPPORTED_VECTOR_TYPES, - hir_id, + let (span, _hir_id) = loc(); + tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType { span, - errors::AbiErrorUnsupportedVectorType { - span, - ty: arg_abi.layout.ty, - is_call, - }, - ); + ty: arg_abi.layout.ty, + is_call, + }); continue; } }; if !have_feature(Symbol::intern(feature)) { // Emit error. - let (span, hir_id) = loc(); - tcx.emit_node_span_lint( - ABI_UNSUPPORTED_VECTOR_TYPES, - hir_id, + let (span, _hir_id) = loc(); + tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { span, - errors::AbiErrorDisabledVectorType { - span, - required_feature: feature, - ty: arg_abi.layout.ty, - is_call, - }, - ); + required_feature: feature, + ty: arg_abi.layout.ty, + is_call, + }); } } } @@ -99,6 +89,12 @@ fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool return true; } + // Both the old and the new ABIs treat vector types like `v128` the same + // way. + if uses_vector_registers(&arg.mode, &arg.layout.backend_repr) { + return true; + } + // This matches `unwrap_trivial_aggregate` in the wasm ABI logic. if arg.layout.is_aggregate() { let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized()); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index b1b6f10e0fe2c..6bc8a0fc88ce7 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -223,7 +223,7 @@ where match mono_item.instantiation_mode(cx.tcx) { InstantiationMode::GloballyShared { .. } => {} InstantiationMode::LocalCopy => { - if Some(mono_item.def_id()) != cx.tcx.lang_items().start_fn() { + if !cx.tcx.is_lang_item(mono_item.def_id(), LangItem::Start) { continue; } } @@ -254,8 +254,9 @@ where always_export_generics, ); - // We can't differentiate something that got inlined. + // We can't differentiate a function that got inlined. let autodiff_active = cfg!(llvm_enzyme) + && matches!(mono_item, MonoItem::Fn(_)) && cx .tcx .codegen_fn_attrs(mono_item.def_id()) diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 63aa60f2f26b9..36d53901d9e82 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,7 +9,6 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } tracing = "0.1" @@ -20,7 +19,6 @@ default = ["nightly"] nightly = [ "dep:rustc_data_structures", "dep:rustc_macros", - "dep:rustc_serialize", "rustc_index/nightly", "rustc_type_ir/nightly", ] diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index 0fc313e33b323..f7bd460094328 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -16,6 +16,7 @@ //! relate them structurally. use rustc_type_ir::inherent::*; +use rustc_type_ir::solve::GoalSource; use rustc_type_ir::{self as ty, Interner}; use tracing::{instrument, trace}; @@ -49,7 +50,10 @@ where // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { let term = self.next_term_infer_of_kind(lhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { lhs @@ -58,7 +62,10 @@ where // Structurally normalize the rhs. let rhs = if let Some(alias) = rhs.to_alias_term() { let term = self.next_term_infer_of_kind(rhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { rhs diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index ecb57cc0ad71b..381a732b8dee9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -10,6 +10,7 @@ use rustc_type_ir::{ }; use tracing::{debug, instrument}; +use super::has_only_region_constraints; use super::trait_goals::TraitGoalProvenVia; use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; @@ -771,6 +772,69 @@ where } }) } +} + +pub(super) enum AllowInferenceConstraints { + Yes, + No, +} + +impl EvalCtxt<'_, D> +where + D: SolverDelegate, + I: Interner, +{ + /// Check whether we can ignore impl candidates due to specialization. + /// + /// This is only necessary for `feature(specialization)` and seems quite ugly. + pub(super) fn filter_specialized_impls( + &mut self, + allow_inference_constraints: AllowInferenceConstraints, + candidates: &mut Vec>, + ) { + match self.typing_mode() { + TypingMode::Coherence => return, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} + } + + let mut i = 0; + 'outer: while i < candidates.len() { + let CandidateSource::Impl(victim_def_id) = candidates[i].source else { + i += 1; + continue; + }; + + for (j, c) in candidates.iter().enumerate() { + if i == j { + continue; + } + + let CandidateSource::Impl(other_def_id) = c.source else { + continue; + }; + + // See if we can toss out `victim` based on specialization. + // + // While this requires us to know *for sure* that the `lhs` impl applies + // we still use modulo regions here. This is fine as specialization currently + // assumes that specializing impls have to be always applicable, meaning that + // the only allowed region constraints may be constraints also present on the default impl. + if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) + || has_only_region_constraints(c.result) + { + if self.cx().impl_specializes(other_def_id, victim_def_id) { + candidates.remove(i); + continue 'outer; + } + } + } + + i += 1; + } + } /// Assemble and merge candidates for goals which are related to an underlying trait /// goal. Right now, this is normalizes-to and host effect goals. @@ -857,7 +921,7 @@ where } } TraitGoalProvenVia::Misc => { - let candidates = + let mut candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); // Prefer "orphaned" param-env normalization predicates, which are used @@ -871,6 +935,13 @@ where return Ok(response); } + // We drop specialized impls to allow normalization via a final impl here. In case + // the specializing impl has different inference constraints from the specialized + // impl, proving the trait goal is already ambiguous, so we never get here. This + // means we can just ignore inference constraints and don't have to special-case + // constraining the normalized-to `term`. + self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); + let responses: Vec<_> = candidates.iter().map(|c| c.result).collect(); if let Some(response) = self.try_merge_responses(&responses) { Ok(response) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index d56b0e5847e57..04f80a056f94d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -22,7 +22,7 @@ use tracing::{debug, instrument, trace}; use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::EagerResolver; -use crate::solve::eval_ctxt::{CurrentGoalKind, NestedGoals}; +use crate::solve::eval_ctxt::CurrentGoalKind; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, @@ -112,13 +112,9 @@ where // by `try_evaluate_added_goals()`. let (certainty, normalization_nested_goals) = match self.current_goal_kind { CurrentGoalKind::NormalizesTo => { - let NestedGoals { normalizes_to_goals, goals } = - std::mem::take(&mut self.nested_goals); - if cfg!(debug_assertions) { - assert!(normalizes_to_goals.is_empty()); - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } + let goals = std::mem::take(&mut self.nested_goals); + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); } (certainty, NestedNormalizationGoals(goals)) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 9994c85d0d0d4..19fb2b00fcb3a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1,8 +1,8 @@ +use std::mem; use std::ops::ControlFlow; -use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_macros::HashStable_NoContext; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; @@ -14,9 +14,9 @@ use rustc_type_ir::{ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::{instrument, trace}; +use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; @@ -114,7 +114,7 @@ where pub(super) search_graph: &'a mut SearchGraph, - nested_goals: NestedGoals, + nested_goals: Vec<(GoalSource, Goal)>, pub(super) origin_span: I::Span, @@ -129,38 +129,6 @@ where pub(super) inspect: ProofTreeBuilder, } -#[derive_where(Clone, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -struct NestedGoals { - /// These normalizes-to goals are treated specially during the evaluation - /// loop. In each iteration we take the RHS of the projection, replace it with - /// a fresh inference variable, and only after evaluating that goal do we - /// equate the fresh inference variable with the actual RHS of the predicate. - /// - /// This is both to improve caching, and to avoid using the RHS of the - /// projection predicate to influence the normalizes-to candidate we select. - /// - /// Forgetting to replace the RHS with a fresh inference variable when we evaluate - /// this goal results in an ICE.. - pub normalizes_to_goals: Vec>>, - /// The rest of the goals which have not yet processed or remain ambiguous. - pub goals: Vec<(GoalSource, Goal)>, -} - -impl NestedGoals { - fn new() -> Self { - Self { normalizes_to_goals: Vec::new(), goals: Vec::new() } - } - - fn is_empty(&self) -> bool { - self.normalizes_to_goals.is_empty() && self.goals.is_empty() - } -} - #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GenerateProofTree { @@ -332,7 +300,7 @@ where let mut ecx = EvalCtxt { delegate, search_graph: &mut search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree), // Only relevant when canonicalizing the response, @@ -385,7 +353,7 @@ where predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.canonical.max_universe, search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), origin_span: I::Span::dummy(), tainted: Ok(()), inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), @@ -509,13 +477,8 @@ where Ok(response) => response, }; - let has_changed = if !response.value.var_values.is_identity_modulo_regions() - || !response.value.external_constraints.opaque_types.is_empty() - { - HasChanged::Yes - } else { - HasChanged::No - }; + let has_changed = + if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; let (normalization_nested_goals, certainty) = self.instantiate_and_apply_query_response(goal.param_env, orig_values, response); @@ -629,78 +592,83 @@ where /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { let cx = self.cx(); - let mut goals = core::mem::take(&mut self.nested_goals); - // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - for goal in goals.normalizes_to_goals { - // Replace the goal with an unconstrained infer var, so the - // RHS does not affect projection candidate assembly. - let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with( - cx, - ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, - ); - - let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( - GoalEvaluationKind::Nested, - GoalSource::TypeRelating, - unconstrained_goal, - )?; - // Add the nested goals from normalization to our own nested goals. - trace!(?nested_goals); - goals.goals.extend(nested_goals); - - // Finally, equate the goal's RHS with the unconstrained var. - // - // SUBTLE: - // We structurally relate aliases here. This is necessary - // as we otherwise emit a nested `AliasRelate` goal in case the - // returned term is a rigid alias, resulting in overflow. + for (source, goal) in mem::take(&mut self.nested_goals) { + // We treat normalizes-to goals specially here. In each iteration we take the + // RHS of the projection, replace it with a fresh inference variable, and only + // after evaluating that goal do we equate the fresh inference variable with the + // actual RHS of the predicate. // - // It is correct as both `goal.predicate.term` and `unconstrained_rhs` - // start out as an unconstrained inference variable so any aliases get - // fully normalized when instantiating it. + // This is both to improve caching, and to avoid using the RHS of the + // projection predicate to influence the normalizes-to candidate we select. // - // FIXME: Strictly speaking this may be incomplete if the normalized-to - // type contains an ambiguous alias referencing bound regions. We should - // consider changing this to only use "shallow structural equality". - self.eq_structurally_relating_aliases( - goal.param_env, - goal.predicate.term, - unconstrained_rhs, - )?; - - // We only look at the `projection_ty` part here rather than - // looking at the "has changed" return from evaluate_goal, - // because we expect the `unconstrained_rhs` part of the predicate - // to have changed -- that means we actually normalized successfully! - let with_resolved_vars = self.resolve_vars_if_possible(goal); - if goal.predicate.alias != with_resolved_vars.predicate.alias { - unchanged_certainty = None; - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.normalizes_to_goals.push(with_resolved_vars); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + // Forgetting to replace the RHS with a fresh inference variable when we evaluate + // this goal results in an ICE. + if let Some(pred) = goal.predicate.as_normalizes_to() { + // We should never encounter higher-ranked normalizes-to goals. + let pred = pred.no_bound_vars().unwrap(); + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(pred.term); + let unconstrained_goal = + goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs }); + + let (NestedNormalizationGoals(nested_goals), _, certainty) = + self.evaluate_goal_raw(GoalEvaluationKind::Nested, source, unconstrained_goal)?; + // Add the nested goals from normalization to our own nested goals. + trace!(?nested_goals); + self.nested_goals.extend(nested_goals); + + // Finally, equate the goal's RHS with the unconstrained var. + // + // SUBTLE: + // We structurally relate aliases here. This is necessary + // as we otherwise emit a nested `AliasRelate` goal in case the + // returned term is a rigid alias, resulting in overflow. + // + // It is correct as both `goal.predicate.term` and `unconstrained_rhs` + // start out as an unconstrained inference variable so any aliases get + // fully normalized when instantiating it. + // + // FIXME: Strictly speaking this may be incomplete if the normalized-to + // type contains an ambiguous alias referencing bound regions. We should + // consider changing this to only use "shallow structural equality". + self.eq_structurally_relating_aliases( + goal.param_env, + pred.term, + unconstrained_rhs, + )?; + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + let with_resolved_vars = self.resolve_vars_if_possible(goal); + if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + unchanged_certainty = None; } - } - } - for (source, goal) in goals.goals { - let (has_changed, certainty) = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; - if has_changed == HasChanged::Yes { - unchanged_certainty = None; - } + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, with_resolved_vars)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } else { + let (has_changed, certainty) = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; + if has_changed == HasChanged::Yes { + unchanged_certainty = None; + } - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.goals.push((source, goal)); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } } } } @@ -717,23 +685,12 @@ where self.delegate.cx() } - #[instrument(level = "trace", skip(self))] - pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new( - self, - GoalSource::TypeRelating, - goal.param_env, - )); - self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); - self.nested_goals.normalizes_to_goals.push(goal); - } - #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); - self.nested_goals.goals.push((source, goal)); + self.nested_goals.push((source, goal)); } #[instrument(level = "trace", skip(self, goals))] diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 6a8e0790f7cb4..f22b275bc44a2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -412,20 +412,6 @@ impl, I: Interner> ProofTreeBuilder { } } - pub(crate) fn add_normalizes_to_goal( - &mut self, - delegate: &D, - max_input_universe: ty::UniverseIndex, - goal: Goal>, - ) { - self.add_goal( - delegate, - max_input_universe, - GoalSource::TypeRelating, - goal.with(delegate.cx(), goal.predicate), - ); - } - pub(crate) fn add_goal( &mut self, delegate: &D, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 7641e9a16ee62..d9cf84787a036 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints( && normalization_nested_goals.is_empty() } +fn has_only_region_constraints(response: ty::Canonical>) -> bool { + let ExternalConstraintsData { + region_constraints: _, + ref opaque_types, + ref normalization_nested_goals, + } = *response.value.external_constraints; + response.value.var_values.is_identity_modulo_regions() + && opaque_types.is_empty() + && normalization_nested_goals.is_empty() +} + impl<'a, D, I> EvalCtxt<'a, D> where D: SolverDelegate, @@ -329,7 +340,7 @@ where TypingMode::Coherence | TypingMode::PostAnalysis => false, // During analysis, opaques are rigid unless they may be defined by // the current body. - TypingMode::Analysis { defining_opaque_types: non_rigid_opaques } + TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques } | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 9466901683e21..119d197de134f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -213,9 +213,6 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, goal.predicate.def_id(), @@ -223,8 +220,28 @@ where ) { Ok(Some(target_item_def_id)) => target_item_def_id, Ok(None) => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + match ecx.typing_mode() { + // In case the associated item is hidden due to specialization, we have to + // return ambiguity this would otherwise be incomplete, resulting in + // unsoundness during coherence (#105782). + ty::TypingMode::Coherence => { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + // Outside of coherence, we treat the associated item as rigid instead. + ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + ecx.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }; } Err(guar) => return error_response(ecx, guar), }; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index aa89e77bb6fb9..ee439f1b3d0d5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -33,11 +33,11 @@ where ); self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - TypingMode::Analysis { defining_opaque_types } => { + TypingMode::Analysis { defining_opaque_types_and_generators } => { let Some(def_id) = opaque_ty .def_id .as_local() - .filter(|&def_id| defining_opaque_types.contains(&def_id)) + .filter(|&def_id| defining_opaque_types_and_generators.contains(&def_id)) else { self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 7bd1300f34ed7..24657496df6a5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -13,7 +13,7 @@ use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; -use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate}; +use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate}; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, @@ -208,6 +208,11 @@ where } } + // We need to make sure to stall any coroutines we are inferring to avoid query cycles. + if let Some(cand) = ecx.try_stall_coroutine_witness(goal.predicate.self_ty()) { + return cand; + } + ecx.probe_and_evaluate_goal_for_constituent_tys( CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, @@ -259,6 +264,11 @@ where return Err(NoSolution); } + // We need to make sure to stall any coroutines we are inferring to avoid query cycles. + if let Some(cand) = ecx.try_stall_coroutine_witness(goal.predicate.self_ty()) { + return cand; + } + ecx.probe_and_evaluate_goal_for_constituent_tys( CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, @@ -1298,18 +1308,6 @@ where return true; } - // We don't consider a trait-bound global if it has a projection bound. - // - // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs - // for an example where this is necessary. - for p in goal.param_env.caller_bounds().iter() { - if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() { - if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref { - return true; - } - } - } - false } _ => false, @@ -1340,6 +1338,8 @@ where }; } + self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); + // If there are *only* global where bounds, then make sure to return that this // is still reported as being proven-via the param-env so that rigid projections // operate correctly. Otherwise, drop all global where-bounds before merging the @@ -1368,4 +1368,28 @@ where let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); self.merge_trait_candidates(goal, candidates) } + + fn try_stall_coroutine_witness( + &mut self, + self_ty: I::Ty, + ) -> Option, NoSolution>> { + if let ty::CoroutineWitness(def_id, _) = self_ty.kind() { + match self.typing_mode() { + TypingMode::Analysis { + defining_opaque_types_and_generators: stalled_generators, + } => { + if def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id)) + { + return Some(self.forced_ambiguity(MaybeCause::Ambiguity)); + } + } + TypingMode::Coherence + | TypingMode::PostAnalysis + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} + } + } + + None + } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e7f17bb6f996d..ac4f7ed64e22f 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -246,9 +246,9 @@ parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$tok parse_expected_trait_in_trait_impl_found_type = expected a trait, found type -parse_expr_rarrow_call = `->` used for field access or method call +parse_expr_rarrow_call = `->` is not valid syntax for field accesses and method calls .suggestion = try using `.` instead - .help = the `.` operator will dereference the value if needed + .help = the `.` operator will automatically dereference the value, except if the value is a raw pointer parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements .label = dash-separated idents are not valid @@ -675,6 +675,8 @@ parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns ar parse_nul_in_c_str = null characters in C string literals are not supported +parse_or_in_let_chain = `||` operators are not supported in let chain conditions + parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings parse_out_of_range_hex_escape = out of range hex escape @@ -893,6 +895,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_cr = use `cr` for a raw C-string .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 44b4e1a3e47a0..6a6fb0eb9b5ba 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -478,6 +478,13 @@ pub(crate) struct ExpectedExpressionFoundLet { pub comparison: Option, } +#[derive(Diagnostic)] +#[diag(parse_or_in_let_chain)] +pub(crate) struct OrInLetChain { + #[primary_span] + pub span: Span, +} + #[derive(Subdiagnostic, Clone, Copy)] #[multipart_suggestion( parse_maybe_missing_let, @@ -2140,6 +2147,13 @@ pub(crate) enum UnknownPrefixSugg { style = "verbose" )] UseBr(#[primary_span] Span), + #[suggestion( + parse_suggestion_cr, + code = "cr", + applicability = "maybe-incorrect", + style = "verbose" + )] + UseCr(#[primary_span] Span), #[suggestion( parse_suggestion_whitespace, code = " ", diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index e1f19beb53aee..0b97d4e6993bb 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -1,14 +1,17 @@ use rustc_ast::token::Delimiter; use rustc_errors::Diag; +use rustc_session::parse::ParseSess; use rustc_span::Span; use rustc_span::source_map::SourceMap; use super::UnmatchedDelim; +use crate::errors::MismatchedClosingDelimiter; +use crate::pprust; #[derive(Default)] pub(super) struct TokenTreeDiagInfo { /// Stack of open delimiters and their spans. Used for error message. - pub open_braces: Vec<(Delimiter, Span)>, + pub open_delimiters: Vec<(Delimiter, Span)>, pub unmatched_delims: Vec, /// Used only for error recovery when arriving to EOF with mismatched braces. @@ -108,7 +111,7 @@ pub(super) fn report_suspicious_mismatch_block( } else { // If there is no suspicious span, give the last properly closed block may help if let Some(parent) = diag_info.matching_block_spans.last() - && diag_info.open_braces.last().is_none() + && diag_info.open_delimiters.last().is_none() && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) { err.span_label(parent.0, "this opening brace..."); @@ -116,3 +119,24 @@ pub(super) fn report_suspicious_mismatch_block( } } } + +pub(crate) fn make_unclosed_delims_error( + unmatched: UnmatchedDelim, + psess: &ParseSess, +) -> Option> { + // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to + // `unmatched_delims` only for error recovery in the `Parser`. + let found_delim = unmatched.found_delim?; + let mut spans = vec![unmatched.found_span]; + if let Some(sp) = unmatched.unclosed_span { + spans.push(sp); + }; + let err = psess.dcx().create_err(MismatchedClosingDelimiter { + spans, + delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(), + unmatched: unmatched.found_span, + opening_candidate: unmatched.candidate_span, + unclosed: unmatched.unclosed_span, + }); + Some(err) +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4935fc03256f4..e8a5cae54cf8b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use diagnostics::make_unclosed_delims_error; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; @@ -17,9 +18,9 @@ use rustc_session::parse::ParseSess; use rustc_span::{BytePos, Pos, Span, Symbol}; use tracing::debug; +use crate::errors; use crate::lexer::diagnostics::TokenTreeDiagInfo; use crate::lexer::unicode_chars::UNICODE_ARRAY; -use crate::{errors, make_unclosed_delims_error}; mod diagnostics; mod tokentrees; @@ -256,7 +257,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let lit_start = start + BytePos(prefix_len); self.pos = lit_start; self.cursor = Cursor::new(&str_before[prefix_len as usize..]); - self.report_unknown_prefix(start); let prefix_span = self.mk_sp(start, lit_start); return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace); @@ -371,12 +371,12 @@ impl<'psess, 'src> Lexer<'psess, 'src> { rustc_lexer::TokenKind::Semi => token::Semi, rustc_lexer::TokenKind::Comma => token::Comma, rustc_lexer::TokenKind::Dot => token::Dot, - rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis), - rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis), - rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace), - rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace), - rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket), - rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket), + rustc_lexer::TokenKind::OpenParen => token::OpenParen, + rustc_lexer::TokenKind::CloseParen => token::CloseParen, + rustc_lexer::TokenKind::OpenBrace => token::OpenBrace, + rustc_lexer::TokenKind::CloseBrace => token::CloseBrace, + rustc_lexer::TokenKind::OpenBracket => token::OpenBracket, + rustc_lexer::TokenKind::CloseBracket => token::CloseBracket, rustc_lexer::TokenKind::At => token::At, rustc_lexer::TokenKind::Pound => token::Pound, rustc_lexer::TokenKind::Tilde => token::Tilde, @@ -789,13 +789,14 @@ impl<'psess, 'src> Lexer<'psess, 'src> { fn report_unknown_prefix(&self, start: BytePos) { let prefix_span = self.mk_sp(start, self.pos); let prefix = self.str_from_to(start, self.pos); - let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition.at_least_rust_2021() { // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) + } else if prefix == "rc" { + Some(errors::UnknownPrefixSugg::UseCr(prefix_span)) } else if expn_data.is_root() { if self.cursor.first() == '\'' && let Some(start) = self.last_lifetime diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b3f83a320241e..fbea958dcc598 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -18,38 +18,33 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let mut buf = Vec::new(); loop { - match self.token.kind { - token::OpenDelim(delim) => { - // Invisible delimiters cannot occur here because `TokenTreesReader` parses - // code directly from strings, with no macro expansion involved. - debug_assert!(!matches!(delim, Delimiter::Invisible(_))); - buf.push(match self.lex_token_tree_open_delim(delim) { - Ok(val) => val, - Err(errs) => return Err(errs), - }) - } - token::CloseDelim(delim) => { - // Invisible delimiters cannot occur here because `TokenTreesReader` parses - // code directly from strings, with no macro expansion involved. - debug_assert!(!matches!(delim, Delimiter::Invisible(_))); - return if is_delimited { - Ok((open_spacing, TokenStream::new(buf))) - } else { - Err(vec![self.close_delim_err(delim)]) - }; - } - token::Eof => { - return if is_delimited { - Err(vec![self.eof_err()]) - } else { - Ok((open_spacing, TokenStream::new(buf))) - }; - } - _ => { - // Get the next normal token. - let (this_tok, this_spacing) = self.bump(); - buf.push(TokenTree::Token(this_tok, this_spacing)); - } + if let Some(delim) = self.token.kind.open_delim() { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); + buf.push(match self.lex_token_tree_open_delim(delim) { + Ok(val) => val, + Err(errs) => return Err(errs), + }) + } else if let Some(delim) = self.token.kind.close_delim() { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); + return if is_delimited { + Ok((open_spacing, TokenStream::new(buf))) + } else { + Err(vec![self.close_delim_err(delim)]) + }; + } else if self.token.kind == token::Eof { + return if is_delimited { + Err(vec![self.eof_err()]) + } else { + Ok((open_spacing, TokenStream::new(buf))) + }; + } else { + // Get the next normal token. + let (this_tok, this_spacing) = self.bump(); + buf.push(TokenTree::Token(this_tok, this_spacing)); } } } @@ -59,8 +54,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let mut err = self.dcx().struct_span_err(self.token.span, msg); let unclosed_delimiter_show_limit = 5; - let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_braces.len()); - for &(_, span) in &self.diag_info.open_braces[..len] { + let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len()); + for &(_, span) in &self.diag_info.open_delimiters[..len] { err.span_label(span, "unclosed delimiter"); self.diag_info.unmatched_delims.push(UnmatchedDelim { found_delim: None, @@ -70,19 +65,19 @@ impl<'psess, 'src> Lexer<'psess, 'src> { }); } - if let Some((_, span)) = self.diag_info.open_braces.get(unclosed_delimiter_show_limit) - && self.diag_info.open_braces.len() >= unclosed_delimiter_show_limit + 2 + if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit) + && self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2 { err.span_label( *span, format!( "another {} unclosed delimiters begin from here", - self.diag_info.open_braces.len() - unclosed_delimiter_show_limit + self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit ), ); } - if let Some((delim, _)) = self.diag_info.open_braces.last() { + if let Some((delim, _)) = self.diag_info.open_delimiters.last() { report_suspicious_mismatch_block( &mut err, &self.diag_info, @@ -100,7 +95,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // The span for beginning of the delimited section. let pre_span = self.token.span; - self.diag_info.open_braces.push((open_delim, self.token.span)); + self.diag_info.open_delimiters.push((open_delim, self.token.span)); // Lex the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -111,14 +106,15 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let delim_span = DelimSpan::from_pair(pre_span, self.token.span); let sm = self.psess.source_map(); - let close_spacing = match self.token.kind { - // Correct delimiter. - token::CloseDelim(close_delim) if close_delim == open_delim => { - let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap(); - let close_brace_span = self.token.span; + let close_spacing = if let Some(close_delim) = self.token.kind.close_delim() { + if close_delim == open_delim { + // Correct delimiter. + let (open_delimiter, open_delimiter_span) = + self.diag_info.open_delimiters.pop().unwrap(); + let close_delimiter_span = self.token.span; if tts.is_empty() && close_delim == Delimiter::Brace { - let empty_block_span = open_brace_span.to(close_brace_span); + let empty_block_span = open_delimiter_span.to(close_delimiter_span); if !sm.is_multiline(empty_block_span) { // Only track if the block is in the form of `{}`, otherwise it is // likely that it was written on purpose. @@ -127,16 +123,17 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } // only add braces - if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { + if let (Delimiter::Brace, Delimiter::Brace) = (open_delimiter, open_delim) { // Add all the matching spans, we will sort by span later - self.diag_info.matching_block_spans.push((open_brace_span, close_brace_span)); + self.diag_info + .matching_block_spans + .push((open_delimiter_span, close_delimiter_span)); } // Move past the closing delimiter. self.bump_minimal() - } - // Incorrect delimiter. - token::CloseDelim(close_delim) => { + } else { + // Incorrect delimiter. let mut unclosed_delimiter = None; let mut candidate = None; @@ -146,18 +143,18 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // This is a conservative error: only report the last unclosed // delimiter. The previous unclosed delimiters could actually be // closed! The lexer just hasn't gotten to them yet. - if let Some(&(_, sp)) = self.diag_info.open_braces.last() { + if let Some(&(_, sp)) = self.diag_info.open_delimiters.last() { unclosed_delimiter = Some(sp); }; - for (brace, brace_span) in &self.diag_info.open_braces { - if same_indentation_level(sm, self.token.span, *brace_span) - && brace == &close_delim + for (delimiter, delimiter_span) in &self.diag_info.open_delimiters { + if same_indentation_level(sm, self.token.span, *delimiter_span) + && delimiter == &close_delim { // high likelihood of these two corresponding - candidate = Some(*brace_span); + candidate = Some(*delimiter_span); } } - let (_, _) = self.diag_info.open_braces.pop().unwrap(); + let (_, _) = self.diag_info.open_delimiters.pop().unwrap(); self.diag_info.unmatched_delims.push(UnmatchedDelim { found_delim: Some(close_delim), found_span: self.token.span, @@ -165,7 +162,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { candidate_span: candidate, }); } else { - self.diag_info.open_braces.pop(); + self.diag_info.open_delimiters.pop(); } // If the incorrect delimiter matches an earlier opening @@ -175,21 +172,20 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // fn foo() { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` - if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { + if !self.diag_info.open_delimiters.iter().any(|&(d, _)| d == close_delim) { self.bump_minimal() } else { // The choice of value here doesn't matter. Spacing::Alone } } - token::Eof => { - // Silently recover, the EOF token will be seen again - // and an error emitted then. Thus we don't pop from - // self.open_braces here. The choice of spacing value here - // doesn't matter. - Spacing::Alone - } - _ => unreachable!(), + } else { + assert_eq!(self.token.kind, token::Eof); + // Silently recover, the EOF token will be seen again + // and an error emitted then. Thus we don't pop from + // self.open_delimiters here. The choice of spacing value here + // doesn't matter. + Spacing::Alone }; let spacing = DelimSpacing::new(open_spacing, close_spacing); diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 2bfa1ea4e0581..751d13af4331b 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -5,7 +5,7 @@ use rustc_span::{BytePos, Pos, Span, kw}; use super::Lexer; use crate::errors::TokenSubstitution; -use crate::token::{self, Delimiter}; +use crate::token; #[rustfmt::skip] // for line breaks pub(super) static UNICODE_ARRAY: &[(char, &str, &str)] = &[ @@ -315,12 +315,12 @@ const ASCII_ARRAY: &[(&str, &str, Option)] = &[ ("!", "Exclamation Mark", Some(token::Bang)), ("?", "Question Mark", Some(token::Question)), (".", "Period", Some(token::Dot)), - ("(", "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))), - (")", "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))), - ("[", "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))), - ("]", "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))), - ("{", "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))), - ("}", "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))), + ("(", "Left Parenthesis", Some(token::OpenParen)), + (")", "Right Parenthesis", Some(token::CloseParen)), + ("[", "Left Square Bracket", Some(token::OpenBracket)), + ("]", "Right Square Bracket", Some(token::CloseBracket)), + ("{", "Left Curly Brace", Some(token::OpenBrace)), + ("}", "Right Curly Brace", Some(token::CloseBrace)), ("*", "Asterisk", Some(token::Star)), ("/", "Slash", Some(token::Slash)), ("\\", "Backslash", None), diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2edc8c83017d8..e73d68e2037ac 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -4,13 +4,13 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(string_from_utf8_lossy_owned)] // tidy-alphabetical-end @@ -32,7 +32,7 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] pub mod parser; -use parser::{Parser, make_unclosed_delims_error}; +use parser::Parser; pub mod lexer; pub mod validate_attr; diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index f1bd6a2273099..6061c9cb48563 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::{iter, mem}; -use rustc_ast::token::{Delimiter, Token, TokenKind}; +use rustc_ast::token::{Delimiter, Token}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing, DelimSpan, LazyAttrTokenStream, Spacing, ToAttrTokenStream, @@ -501,27 +501,27 @@ fn make_attr_token_stream( let mut stack_rest = vec![]; for flat_token in iter { match flat_token { - FlatToken::Token((Token { kind: TokenKind::OpenDelim(delim), span }, spacing)) => { - stack_rest.push(mem::replace( - &mut stack_top, - FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, - )); - } - FlatToken::Token((Token { kind: TokenKind::CloseDelim(delim), span }, spacing)) => { - let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); - let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); - assert!( - open_delim.eq_ignoring_invisible_origin(&delim), - "Mismatched open/close delims: open={open_delim:?} close={span:?}" - ); - let dspan = DelimSpan::from_pair(open_sp, span); - let dspacing = DelimSpacing::new(open_spacing, spacing); - let stream = AttrTokenStream::new(frame_data.inner); - let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream); - stack_top.inner.push(delimited); - } - FlatToken::Token((token, spacing)) => { - stack_top.inner.push(AttrTokenTree::Token(token, spacing)) + FlatToken::Token((token @ Token { kind, span }, spacing)) => { + if let Some(delim) = kind.open_delim() { + stack_rest.push(mem::replace( + &mut stack_top, + FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, + )); + } else if let Some(delim) = kind.close_delim() { + let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); + let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); + assert!( + open_delim.eq_ignoring_invisible_origin(&delim), + "Mismatched open/close delims: open={open_delim:?} close={span:?}" + ); + let dspan = DelimSpan::from_pair(open_sp, span); + let dspacing = DelimSpacing::new(open_spacing, spacing); + let stream = AttrTokenStream::new(frame_data.inner); + let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream); + stack_top.inner.push(delimited); + } else { + stack_top.inner.push(AttrTokenTree::Token(token, spacing)) + } } FlatToken::AttrsTarget(target) => { stack_top.inner.push(AttrTokenTree::AttrsTarget(target)) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7c8e0146c3d22..23c8db7bca784 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut}; use ast::token::IdentIsRaw; use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind}; +use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, @@ -304,10 +304,10 @@ impl<'a> Parser<'a> { TokenKind::Comma, TokenKind::Semi, TokenKind::PathSep, - TokenKind::OpenDelim(Delimiter::Brace), - TokenKind::OpenDelim(Delimiter::Parenthesis), - TokenKind::CloseDelim(Delimiter::Brace), - TokenKind::CloseDelim(Delimiter::Parenthesis), + TokenKind::OpenBrace, + TokenKind::OpenParen, + TokenKind::CloseBrace, + TokenKind::CloseParen, ]; if let TokenKind::DocComment(..) = self.prev_token.kind && valid_follow.contains(&self.token.kind) @@ -507,7 +507,7 @@ impl<'a> Parser<'a> { } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { // The current token is in the same line as the prior token, not recoverable. } else if [token::Comma, token::Colon].contains(&self.token.kind) - && self.prev_token == token::CloseDelim(Delimiter::Parenthesis) + && self.prev_token == token::CloseParen { // Likely typo: The current token is on a new line and is expected to be // `.`, `;`, `?`, or an operator after a close delimiter token. @@ -518,8 +518,7 @@ impl<'a> Parser<'a> { // ^ // https://github.com/rust-lang/rust/issues/72253 } else if self.look_ahead(1, |t| { - t == &token::CloseDelim(Delimiter::Brace) - || t.can_begin_expr() && *t != token::Colon + t == &token::CloseBrace || t.can_begin_expr() && *t != token::Colon }) && [token::Comma, token::Colon].contains(&self.token.kind) { // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is @@ -537,7 +536,7 @@ impl<'a> Parser<'a> { self.bump(); return Ok(guar); } else if self.look_ahead(0, |t| { - t == &token::CloseDelim(Delimiter::Brace) + t == &token::CloseBrace || ((t.can_begin_expr() || t.can_begin_item()) && t != &token::Semi && t != &token::Pound) @@ -675,8 +674,7 @@ impl<'a> Parser<'a> { // `pub` may be used for an item or `pub(crate)` if self.prev_token.is_ident_named(sym::public) - && (self.token.can_begin_item() - || self.token == TokenKind::OpenDelim(Delimiter::Parenthesis)) + && (self.token.can_begin_item() || self.token == TokenKind::OpenParen) { err.span_suggestion_short( self.prev_token.span, @@ -843,9 +841,7 @@ impl<'a> Parser<'a> { if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" }, ), ); - if self.token == token::Pound - && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket)) - { + if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) { // We have // #[attr] // expr @@ -1037,9 +1033,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P> { err.span_label(lo.to(decl_hi), "while parsing the body of this closure"); let guar = match before.kind { - token::OpenDelim(Delimiter::Brace) - if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => - { + token::OpenBrace if token.kind != token::OpenBrace => { // `{ || () }` should have been `|| { () }` err.multipart_suggestion( "you might have meant to open the body of the closure, instead of enclosing \ @@ -1054,9 +1048,7 @@ impl<'a> Parser<'a> { self.eat_to_tokens(&[exp!(CloseBrace)]); guar } - token::OpenDelim(Delimiter::Parenthesis) - if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => - { + token::OpenParen if token.kind != token::OpenBrace => { // We are within a function call or tuple, we can emit the error // and recover. self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]); @@ -1071,7 +1063,7 @@ impl<'a> Parser<'a> { ); err.emit() } - _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => { + _ if token.kind != token::OpenBrace => { // We don't have a heuristic to correctly identify where the block // should be closed. err.multipart_suggestion_verbose( @@ -1225,7 +1217,7 @@ impl<'a> Parser<'a> { trailing_span = trailing_span.to(self.token.span); self.bump(); } - if self.token == token::OpenDelim(Delimiter::Parenthesis) { + if self.token == token::OpenParen { // Recover from bad turbofish: `foo.collect::Vec<_>()`. segment.args = Some(AngleBracketedArgs { args, span }.into()); @@ -1470,9 +1462,7 @@ impl<'a> Parser<'a> { let modifiers = [(token::Lt, 1), (token::Gt, -1), (token::Shr, -2)]; self.consume_tts(1, &modifiers); - if !&[token::OpenDelim(Delimiter::Parenthesis), token::PathSep] - .contains(&self.token.kind) - { + if !matches!(self.token.kind, token::OpenParen | token::PathSep) { // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the // parser and bail out. self.restore_snapshot(snapshot); @@ -1510,7 +1500,7 @@ impl<'a> Parser<'a> { Err(self.dcx().create_err(err)) } } - } else if self.token == token::OpenDelim(Delimiter::Parenthesis) { + } else if self.token == token::OpenParen { // We have high certainty that this was a bad turbofish at this point. // `foo< bar >(` if let ExprKind::Binary(o, ..) = inner_op.kind @@ -1570,10 +1560,7 @@ impl<'a> Parser<'a> { self.bump(); // `(` // Consume the fn call arguments. - let modifiers = [ - (token::OpenDelim(Delimiter::Parenthesis), 1), - (token::CloseDelim(Delimiter::Parenthesis), -1), - ]; + let modifiers = [(token::OpenParen, 1), (token::CloseParen, -1)]; self.consume_tts(1, &modifiers); if self.token == token::Eof { @@ -1978,7 +1965,7 @@ impl<'a> Parser<'a> { fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P, bool)> { let is_question = self.eat(exp!(Question)); // Handle `await? `. - let expr = if self.token == token::OpenDelim(Delimiter::Brace) { + let expr = if self.token == token::OpenBrace { // Handle `await { }`. // This needs to be handled separately from the next arm to avoid // interpreting `await { }?` as `?.await`. @@ -2014,9 +2001,7 @@ impl<'a> Parser<'a> { /// If encountering `future.await()`, consumes and emits an error. pub(super) fn recover_from_await_method_call(&mut self) { - if self.token == token::OpenDelim(Delimiter::Parenthesis) - && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis)) - { + if self.token == token::OpenParen && self.look_ahead(1, |t| t == &token::CloseParen) { // future.await() let lo = self.token.span; self.bump(); // ( @@ -2029,9 +2014,7 @@ impl<'a> Parser<'a> { /// /// If encountering `x.use()`, consumes and emits an error. pub(super) fn recover_from_use(&mut self) { - if self.token == token::OpenDelim(Delimiter::Parenthesis) - && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis)) - { + if self.token == token::OpenParen && self.look_ahead(1, |t| t == &token::CloseParen) { // var.use() let lo = self.token.span; self.bump(); // ( @@ -2045,7 +2028,7 @@ impl<'a> Parser<'a> { pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P> { let is_try = self.token.is_keyword(kw::Try); let is_questionmark = self.look_ahead(1, |t| t == &token::Bang); //check for ! - let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); //check for ( + let is_open = self.look_ahead(2, |t| t == &token::OpenParen); //check for ( if is_try && is_questionmark && is_open { let lo = self.token.span; @@ -2053,7 +2036,7 @@ impl<'a> Parser<'a> { self.bump(); //remove ! let try_span = lo.to(self.token.span); //we take the try!( span self.bump(); //remove ( - let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty + let is_empty = self.token == token::CloseParen; //check if the block is empty self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::No); //eat the block let hi = self.token.span; self.bump(); //remove ) @@ -2148,7 +2131,7 @@ impl<'a> Parser<'a> { loop { debug!("recover_stmt_ loop {:?}", self.token); match self.token.kind { - token::OpenDelim(Delimiter::Brace) => { + token::OpenBrace => { brace_depth += 1; self.bump(); if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0 @@ -2156,11 +2139,11 @@ impl<'a> Parser<'a> { in_block = true; } } - token::OpenDelim(Delimiter::Bracket) => { + token::OpenBracket => { bracket_depth += 1; self.bump(); } - token::CloseDelim(Delimiter::Brace) => { + token::CloseBrace => { if brace_depth == 0 { debug!("recover_stmt_ return - close delim {:?}", self.token); break; @@ -2172,7 +2155,7 @@ impl<'a> Parser<'a> { break; } } - token::CloseDelim(Delimiter::Bracket) => { + token::CloseBracket => { bracket_depth -= 1; if bracket_depth < 0 { bracket_depth = 0; @@ -2219,12 +2202,10 @@ impl<'a> Parser<'a> { if let token::DocComment(..) = self.token.kind { self.dcx().emit_err(DocCommentOnParamType { span: self.token.span }); self.bump(); - } else if self.token == token::Pound - && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket)) - { + } else if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) { let lo = self.token.span; // Skip every token until next possible arg. - while self.token != token::CloseDelim(Delimiter::Bracket) { + while self.token != token::CloseBracket { self.bump(); } let sp = lo.to(self.token.span); @@ -2243,9 +2224,7 @@ impl<'a> Parser<'a> { // If we find a pattern followed by an identifier, it could be an (incorrect) // C-style parameter declaration. if self.check_ident() - && self.look_ahead(1, |t| { - *t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis) - }) + && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseParen) { // `fn foo(String s) {}` let ident = self.parse_ident().unwrap(); @@ -2261,7 +2240,7 @@ impl<'a> Parser<'a> { } else if require_name && (self.token == token::Comma || self.token == token::Lt - || self.token == token::CloseDelim(Delimiter::Parenthesis)) + || self.token == token::CloseParen) { let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; @@ -2872,7 +2851,7 @@ impl<'a> Parser<'a> { // Check for `'a : {` if !(self.check_lifetime() && self.look_ahead(1, |t| *t == token::Colon) - && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))) + && self.look_ahead(2, |t| *t == token::OpenBrace)) { return false; } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 71cc814cb500a..f3b53971b2957 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -26,6 +26,7 @@ use rustc_macros::Subdiagnostic; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; +use rustc_span::edition::Edition; use rustc_span::source_map::{self, Spanned}; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -436,7 +437,7 @@ impl<'a> Parser<'a> { fn is_at_start_of_range_notation_rhs(&self) -> bool { if self.token.can_begin_expr() { // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. - if self.token == token::OpenDelim(Delimiter::Brace) { + if self.token == token::OpenBrace { return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); } true @@ -542,8 +543,8 @@ impl<'a> Parser<'a> { } // Recover from `++x`: token::Plus if this.look_ahead(1, |t| *t == token::Plus) => { - let starts_stmt = this.prev_token == token::Semi - || this.prev_token == token::CloseDelim(Delimiter::Brace); + let starts_stmt = + this.prev_token == token::Semi || this.prev_token == token::CloseBrace; let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span)); // Eat both `+`s. this.bump(); @@ -637,8 +638,8 @@ impl<'a> Parser<'a> { /// Returns the span of expr if it was not interpolated, or the span of the interpolated token. fn interpolated_or_expr_span(&self, expr: &Expr) -> Span { match self.prev_token.kind { - TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) => self.prev_token.span, - TokenKind::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => { + token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span, + token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => { // `expr.span` is the interpolated span, because invisible open // and close delims both get marked with the same span, one // that covers the entire thing between them. (See @@ -912,8 +913,8 @@ impl<'a> Parser<'a> { return Ok(e); } e = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e), - token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?, + token::OpenParen => self.parse_expr_fn_call(lo, e), + token::OpenBracket => self.parse_expr_index(lo, e)?, _ => return Ok(e), } } @@ -1002,7 +1003,7 @@ impl<'a> Parser<'a> { (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => { (span.shrink_to_hi(), format!("`{}`", snippet)) } - (token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))), _) => { + (token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => { // No need to report an error. This case will only occur when parsing a pasted // metavariable, and we should have emitted an error when parsing the macro call in // the first place. E.g. in this code: @@ -1202,7 +1203,7 @@ impl<'a> Parser<'a> { } } - if matches!(self.token.kind, token::CloseDelim(..) | token::Comma) { + if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma { break; } else if trailing_dot.is_none() { // This loop should only repeat if there is a trailing dot. @@ -1232,7 +1233,7 @@ impl<'a> Parser<'a> { /// Parse a function call expression, `expr(...)`. fn parse_expr_fn_call(&mut self, lo: Span, fun: P) -> P { - let snapshot = if self.token == token::OpenDelim(Delimiter::Parenthesis) { + let snapshot = if self.token == token::OpenParen { Some((self.create_snapshot_for_diagnostic(), fun.kind.clone())) } else { None @@ -1676,14 +1677,11 @@ impl<'a> Parser<'a> { self.parse_expr_for(label, lo) } else if self.eat_keyword(exp!(Loop)) { self.parse_expr_loop(label, lo) - } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) - || self.token.is_metavar_block() - { + } else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() { self.parse_expr_block(label, lo, BlockCheckMode::Default) } else if !ate_colon && self.may_recover() - && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) - || self.token.is_punct()) + && (self.token.kind.close_delim().is_some() || self.token.is_punct()) && could_be_unclosed_char_literal(label_.ident) { let (lit, _) = @@ -1878,7 +1876,7 @@ impl<'a> Parser<'a> { }, }); Some(lexpr) - } else if self.token != token::OpenDelim(Delimiter::Brace) + } else if self.token != token::OpenBrace || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) { let mut expr = self.parse_expr_opt()?; @@ -2016,7 +2014,7 @@ impl<'a> Parser<'a> { // Eat tokens until the macro call ends. if self.may_recover() { - while !matches!(self.token.kind, token::CloseDelim(..) | token::Eof) { + while !self.token.kind.is_close_delim_or_eof() { self.bump(); } } @@ -2148,6 +2146,17 @@ impl<'a> Parser<'a> { /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and /// `Lit::from_token` (excluding unary negation). fn eat_token_lit(&mut self) -> Option { + let check_expr = |expr: P| { + if let ast::ExprKind::Lit(token_lit) = expr.kind { + Some(token_lit) + } else if let ast::ExprKind::Unary(UnOp::Neg, inner) = &expr.kind + && let ast::Expr { kind: ast::ExprKind::Lit(_), .. } = **inner + { + None + } else { + panic!("unexpected reparsed expr/literal: {:?}", expr.kind); + } + }; match self.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) if name.is_bool_lit() => { self.bump(); @@ -2157,32 +2166,19 @@ impl<'a> Parser<'a> { self.bump(); Some(token_lit) } - token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( - MetaVarKind::Literal, - ))) => { + token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Literal)) => { let lit = self .eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus()) .expect("metavar seq literal"); - let ast::ExprKind::Lit(token_lit) = lit.kind else { - panic!("didn't reparse a literal"); - }; - Some(token_lit) + check_expr(lit) } - token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + token::OpenInvisible(InvisibleOrigin::MetaVar( mv_kind @ MetaVarKind::Expr { can_begin_literal_maybe_minus: true, .. }, - ))) => { + )) => { let expr = self .eat_metavar_seq(mv_kind, |this| this.parse_expr()) .expect("metavar seq expr"); - if let ast::ExprKind::Lit(token_lit) = expr.kind { - Some(token_lit) - } else if let ast::ExprKind::Unary(UnOp::Neg, inner) = &expr.kind - && let ast::Expr { kind: ast::ExprKind::Lit(_), .. } = **inner - { - None - } else { - panic!("unexpected reparsed expr: {:?}", expr.kind); - } + check_expr(expr) } _ => None, } @@ -2275,7 +2271,7 @@ impl<'a> Parser<'a> { } fn is_array_like_block(&mut self) -> bool { - matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace)) + self.token.kind == TokenKind::OpenBrace && self .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) && self.look_ahead(2, |t| t == &token::Comma) @@ -2328,8 +2324,8 @@ impl<'a> Parser<'a> { |p| p.parse_expr(), ) { Ok(_) - // When the close delim is `)`, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`, - // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`. + // When the close delim is `)`, `token.kind` is expected to be `token::CloseParen`, + // but the actual `token.kind` is `token::CloseBracket`. // This is because the `token.kind` of the close delim is treated as the same as // that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different. // Therefore, `token.kind` should not be compared here. @@ -2484,7 +2480,7 @@ impl<'a> Parser<'a> { fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P> { if self.may_recover() && self.token.can_begin_expr() - && !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace)) + && self.token.kind != TokenKind::OpenBrace && !self.token.is_metavar_block() { let snapshot = self.create_snapshot_for_diagnostic(); @@ -2607,7 +2603,10 @@ impl<'a> Parser<'a> { /// Parses an `if` expression (`if` token already eaten). fn parse_expr_if(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; - let cond = self.parse_expr_cond()?; + // Scoping code checks the top level edition of the `if`; let's match it here. + // The `CondChecker` also checks the edition of the `let` itself, just to make sure. + let let_chains_policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() }; + let cond = self.parse_expr_cond(let_chains_policy)?; self.parse_if_after_cond(lo, cond) } @@ -2716,18 +2715,17 @@ impl<'a> Parser<'a> { } /// Parses the condition of a `if` or `while` expression. + /// + /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct, + /// i.e. the same span we use to later decide whether the drop behaviour should be that of + /// edition `..=2021` or that of `2024..`. // Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions. - pub fn parse_expr_cond(&mut self) -> PResult<'a, P> { + pub fn parse_expr_cond(&mut self, let_chains_policy: LetChainsPolicy) -> PResult<'a, P> { let attrs = self.parse_outer_attributes()?; let (mut cond, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; - CondChecker::new(self).visit_expr(&mut cond); - - if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); - } + CondChecker::new(self, let_chains_policy).visit_expr(&mut cond); Ok(cond) } @@ -2887,7 +2885,7 @@ impl<'a> Parser<'a> { } fn parse_for_head(&mut self) -> PResult<'a, (P, P)> { - let begin_paren = if self.token == token::OpenDelim(Delimiter::Parenthesis) { + let begin_paren = if self.token == token::OpenParen { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` // in which case we will suggest `for $stuff $block`. @@ -2921,7 +2919,7 @@ impl<'a> Parser<'a> { return Err(err); } }; - return if self.token == token::CloseDelim(Delimiter::Parenthesis) { + return if self.token == token::CloseParen { // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the // parser state and emit a targeted suggestion. let span = vec![start_span, self.token.span]; @@ -2965,7 +2963,7 @@ impl<'a> Parser<'a> { let (pat, expr) = self.parse_for_head()?; // Recover from missing expression in `for` loop if matches!(expr.kind, ExprKind::Block(..)) - && !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace)) + && self.token.kind != token::OpenBrace && self.may_recover() { let guar = self @@ -3022,7 +3020,8 @@ impl<'a> Parser<'a> { /// Parses a `while` or `while let` expression (`while` token already eaten). fn parse_expr_while(&mut self, opt_label: Option", "DIR"), + opt(Stable, Opt, "o", "", "Write output to FILENAME", ""), + opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", ""), opt( Stable, Opt, "", "explain", "Provide a detailed explanation of an error message", - "OPT", + "", ), opt(Stable, Flag, "", "test", "Build a test harness", ""), - opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "TARGET"), - opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"), - opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"), - opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"), - opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"), - opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"), + opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", ""), + opt(Stable, Multi, "A", "allow", "Set lint allowed", ""), + opt(Stable, Multi, "W", "warn", "Set lint warnings", ""), + opt(Stable, Multi, "", "force-warn", "Set lint force-warn", ""), + opt(Stable, Multi, "D", "deny", "Set lint denied", ""), + opt(Stable, Multi, "F", "forbid", "Set lint forbidden", ""), opt( Stable, Multi, "", "cap-lints", "Set the most restrictive lint level. More restrictive lints are capped at this level", - "LEVEL", + "", ), - opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt(Stable, Multi, "C", "codegen", "Set a codegen option", "[=]"), opt(Stable, Flag, "V", "version", "Print version info and exit", ""), opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), ]; @@ -1666,29 +1763,29 @@ pub fn rustc_optgroups() -> Vec { "", "extern", "Specify where an external rust library is located", - "NAME[=PATH]", + "[=]", ), - opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"), - opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "FLAG"), + opt(Stable, Opt, "", "sysroot", "Override the system root", ""), + opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", ""), opt( Stable, Opt, "", "error-format", "How errors and other messages are produced", - "human|json|short", + "", ), - opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "CONFIG"), + opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", ""), opt( Stable, Opt, "", "color", "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", - "auto|always|never", + * auto = colorize, if output goes to a tty (default); + * always = always colorize output; + * never = never colorize output", + "", ), opt( Stable, @@ -1696,7 +1793,7 @@ pub fn rustc_optgroups() -> Vec { "", "diagnostic-width", "Inform rustc of the width of the output so that diagnostics can be truncated to fit", - "WIDTH", + "", ), opt( Stable, @@ -1704,9 +1801,9 @@ pub fn rustc_optgroups() -> Vec { "", "remap-path-prefix", "Remap source names in all output (compiler messages and output files)", - "FROM=TO", + "=", ), - opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"), + opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "="), ]; options.extend(verbose_only.into_iter().map(|mut opt| { opt.is_verbose_help_only = true; @@ -2706,7 +2803,7 @@ pub fn make_crate_type_option() -> RustcOptGroup { "crate-type", "Comma separated list of types of crates for the compiler to emit", - "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + "", ) } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 0e19b982a133e..ec8e9898dc71e 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,8 +1,8 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(default_field_values)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 36eee5f308656..5f4695fb1841b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -711,7 +711,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `LooseTypes`, `Inline`"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -1360,6 +1360,8 @@ pub mod parse { "PrintModBefore" => AutoDiff::PrintModBefore, "PrintModAfter" => AutoDiff::PrintModAfter, "PrintModFinal" => AutoDiff::PrintModFinal, + "NoPostopt" => AutoDiff::NoPostopt, + "PrintPasses" => AutoDiff::PrintPasses, "LooseTypes" => AutoDiff::LooseTypes, "Inline" => AutoDiff::Inline, _ => { @@ -2098,6 +2100,8 @@ options! { `=PrintModBefore` `=PrintModAfter` `=PrintModFinal` + `=PrintPasses`, + `=NoPostopt` `=LooseTypes` `=Inline` Multiple options can be combined with commas."), diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index 77e5f3423d1e7..771ff98d58d5a 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -19,7 +19,6 @@ pub mod rustc_internal; -// Make this module private for now since external users should not call these directly. -mod rustc_smir; +pub mod rustc_smir; pub mod stable_mir; diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 146ba17d14fc8..2982a920b03d1 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -18,9 +18,10 @@ use rustc_span::def_id::{CrateNum, DefId}; use scoped_tls::scoped_thread_local; use stable_mir::Error; use stable_mir::abi::Layout; +use stable_mir::compiler_interface::SmirInterface; use stable_mir::ty::IndexedVal; -use crate::rustc_smir::context::TablesWrapper; +use crate::rustc_smir::context::SmirCtxt; use crate::rustc_smir::{Stable, Tables}; use crate::stable_mir; @@ -196,12 +197,12 @@ 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, F, T>(tables: &TablesWrapper<'tcx>, f: F) -> T +pub(crate) fn init<'tcx, F, T>(cx: &SmirCtxt<'tcx>, f: F) -> T where F: FnOnce() -> T, { assert!(!TLV.is_set()); - let ptr = tables as *const _ as *const (); + let ptr = cx as *const _ as *const (); TLV.set(&Cell::new(ptr), || f()) } @@ -212,8 +213,8 @@ pub(crate) fn with_tables(f: impl for<'tcx> FnOnce(&mut Tables<'tcx>) -> R) - TLV.with(|tlv| { let ptr = tlv.get(); assert!(!ptr.is_null()); - let wrapper = ptr as *const TablesWrapper<'_>; - let mut tables = unsafe { (*wrapper).0.borrow_mut() }; + let context = ptr as *const SmirCtxt<'_>; + let mut tables = unsafe { (*context).0.borrow_mut() }; f(&mut *tables) }) } @@ -222,7 +223,7 @@ pub fn run(tcx: TyCtxt<'_>, f: F) -> Result where F: FnOnce() -> T, { - let tables = TablesWrapper(RefCell::new(Tables { + let tables = SmirCtxt(RefCell::new(Tables { tcx, def_ids: IndexMap::default(), alloc_ids: IndexMap::default(), @@ -233,7 +234,12 @@ where mir_consts: IndexMap::default(), layouts: IndexMap::default(), })); - stable_mir::compiler_interface::run(&tables, || init(&tables, f)) + + let interface = SmirInterface { cx: tables }; + + // Pass the `SmirInterface` to compiler_interface::run + // and initialize the rustc-specific TLS with tables. + stable_mir::compiler_interface::run(&interface, || init(&interface.cx, f)) } /// Instantiate and run the compiler with the provided arguments and callback. @@ -256,7 +262,7 @@ where /// // Your code goes in here. /// # ControlFlow::Continue(()) /// } -/// # let args = vec!["--verbose".to_string()]; +/// # let args = &["--verbose".to_string()]; /// let result = run!(args, analyze_code); /// # assert_eq!(result, Err(CompilerError::Skipped)) /// # } @@ -278,7 +284,7 @@ where /// // Your code goes in here. /// # ControlFlow::Continue(()) /// } -/// # let args = vec!["--verbose".to_string()]; +/// # let args = &["--verbose".to_string()]; /// # let extra_args = vec![]; /// let result = run!(args, || analyze_code(extra_args)); /// # assert_eq!(result, Err(CompilerError::Skipped)) @@ -340,7 +346,6 @@ macro_rules! run_driver { C: Send, F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, { - args: Vec, callback: Option, result: Option>, } @@ -352,14 +357,14 @@ macro_rules! run_driver { F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, { /// Creates a new `StableMir` instance, with given test_function and arguments. - pub fn new(args: Vec, callback: F) -> Self { - StableMir { args, callback: Some(callback), result: None } + pub fn new(callback: F) -> Self { + StableMir { callback: Some(callback), result: None } } /// Runs the compiler against given target and tests it with `test_function` - pub fn run(&mut self) -> Result> { + pub fn run(&mut self, args: &[String]) -> Result> { let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { - run_compiler(&self.args.clone(), self); + run_compiler(&args, self); Ok(()) }); match (compiler_result, self.result.take()) { @@ -409,7 +414,7 @@ macro_rules! run_driver { } } - StableMir::new($args, $callback).run() + StableMir::new($callback).run($args) }}; } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 240f6f7fe45a1..9a4cac243bd39 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -1,7 +1,4 @@ -//! Implementation of `[stable_mir::compiler_interface::Context]` trait. -//! -//! This trait is currently the main interface between the Rust compiler, -//! and the `stable_mir` crate. +//! Implementation of StableMIR Context. #![allow(rustc::usage_of_qualified_ty)] @@ -20,7 +17,6 @@ use rustc_middle::ty::{ use rustc_middle::{mir, ty}; use rustc_span::def_id::LOCAL_CRATE; use stable_mir::abi::{FnAbi, Layout, LayoutShape}; -use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; use stable_mir::mir::{BinOp, Body, Place, UnOp}; @@ -37,8 +33,14 @@ use crate::rustc_smir::builder::BodyBuilder; use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate}; use crate::stable_mir; -impl<'tcx> Context for TablesWrapper<'tcx> { - fn target_info(&self) -> MachineInfo { +/// Provides direct access to rustc's internal queries. +/// +/// The [`crate::stable_mir::compiler_interface::SmirInterface`] must go through +/// this context to obtain rustc-level information. +pub struct SmirCtxt<'tcx>(pub RefCell>); + +impl<'tcx> SmirCtxt<'tcx> { + pub fn target_info(&self) -> MachineInfo { let mut tables = self.0.borrow_mut(); MachineInfo { endian: tables.tcx.data_layout.endian.stable(&mut *tables), @@ -48,31 +50,35 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn entry_fn(&self) -> Option { + pub fn entry_fn(&self) -> Option { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; Some(tables.crate_item(tcx.entry_fn(())?.0)) } - fn all_local_items(&self) -> stable_mir::CrateItems { + /// Retrieve all items of the local crate that have a MIR associated with them. + pub fn all_local_items(&self) -> stable_mir::CrateItems { let mut tables = self.0.borrow_mut(); tables.tcx.mir_keys(()).iter().map(|item| tables.crate_item(item.to_def_id())).collect() } - fn mir_body(&self, item: stable_mir::DefId) -> stable_mir::mir::Body { + /// Retrieve the body of a function. + /// This function will panic if the body is not available. + pub fn mir_body(&self, item: stable_mir::DefId) -> stable_mir::mir::Body { let mut tables = self.0.borrow_mut(); let def_id = tables[item]; tables.tcx.instance_mir(rustc_middle::ty::InstanceKind::Item(def_id)).stable(&mut tables) } - fn has_body(&self, def: DefId) -> bool { + /// Check whether the body of a function is available. + pub fn has_body(&self, def: DefId) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.internal(&mut *tables, tcx); tables.item_has_body(def_id) } - fn foreign_modules(&self, crate_num: CrateNum) -> Vec { + pub fn foreign_modules(&self, crate_num: CrateNum) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.foreign_modules(crate_num.internal(&mut *tables, tcx)) @@ -81,21 +87,23 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn crate_functions(&self, crate_num: CrateNum) -> Vec { + /// Retrieve all functions defined in this crate. + pub fn crate_functions(&self, crate_num: CrateNum) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let krate = crate_num.internal(&mut *tables, tcx); filter_def_ids(tcx, krate, |def_id| tables.to_fn_def(def_id)) } - fn crate_statics(&self, crate_num: CrateNum) -> Vec { + /// Retrieve all static items defined in this crate. + pub fn crate_statics(&self, crate_num: CrateNum) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let krate = crate_num.internal(&mut *tables, tcx); filter_def_ids(tcx, krate, |def_id| tables.to_static(def_id)) } - fn foreign_module( + pub fn foreign_module( &self, mod_def: stable_mir::ty::ForeignModuleDef, ) -> stable_mir::ty::ForeignModule { @@ -105,7 +113,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { mod_def.stable(&mut *tables) } - fn foreign_items(&self, mod_def: stable_mir::ty::ForeignModuleDef) -> Vec { + pub fn foreign_items(&self, mod_def: stable_mir::ty::ForeignModuleDef) -> Vec { let mut tables = self.0.borrow_mut(); let def_id = tables[mod_def.def_id()]; tables @@ -119,12 +127,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn all_trait_decls(&self) -> stable_mir::TraitDecls { + pub fn all_trait_decls(&self) -> stable_mir::TraitDecls { let mut tables = self.0.borrow_mut(); tables.tcx.all_traits().map(|trait_def_id| tables.trait_def(trait_def_id)).collect() } - fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls { + pub fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.traits(crate_num.internal(&mut *tables, tcx)) @@ -133,14 +141,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn trait_decl(&self, trait_def: &stable_mir::ty::TraitDef) -> stable_mir::ty::TraitDecl { + pub fn trait_decl(&self, trait_def: &stable_mir::ty::TraitDef) -> stable_mir::ty::TraitDecl { let mut tables = self.0.borrow_mut(); let def_id = tables[trait_def.0]; let trait_def = tables.tcx.trait_def(def_id); trait_def.stable(&mut *tables) } - fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls { + pub fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; iter::once(LOCAL_CRATE) @@ -150,7 +158,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls { + pub fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.trait_impls_in_crate(crate_num.internal(&mut *tables, tcx)) @@ -159,21 +167,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn trait_impl(&self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait { + pub fn trait_impl(&self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait { let mut tables = self.0.borrow_mut(); let def_id = tables[impl_def.0]; let impl_trait = tables.tcx.impl_trait_ref(def_id).unwrap(); impl_trait.stable(&mut *tables) } - fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics { + pub fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; let generics = tables.tcx.generics_of(def_id); generics.stable(&mut *tables) } - fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates { + pub fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id); @@ -191,7 +199,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn explicit_predicates_of( + pub fn explicit_predicates_of( &self, def_id: stable_mir::DefId, ) -> stable_mir::ty::GenericPredicates { @@ -212,17 +220,20 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn local_crate(&self) -> stable_mir::Crate { + /// Get information about the local crate. + pub fn local_crate(&self) -> stable_mir::Crate { let tables = self.0.borrow(); smir_crate(tables.tcx, LOCAL_CRATE) } - fn external_crates(&self) -> Vec { + /// Retrieve a list of all external crates. + pub fn external_crates(&self) -> Vec { let tables = self.0.borrow(); tables.tcx.crates(()).iter().map(|crate_num| smir_crate(tables.tcx, *crate_num)).collect() } - fn find_crates(&self, name: &str) -> Vec { + /// Find a crate with the given name. + pub fn find_crates(&self, name: &str) -> Vec { let tables = self.0.borrow(); let crates: Vec = [LOCAL_CRATE] .iter() @@ -235,7 +246,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { crates } - fn def_name(&self, def_id: stable_mir::DefId, trimmed: bool) -> Symbol { + /// Returns the name of given `DefId`. + pub fn def_name(&self, def_id: stable_mir::DefId, trimmed: bool) -> Symbol { let tables = self.0.borrow(); if trimmed { with_forced_trimmed_paths!(tables.tcx.def_path_str(tables[def_id])) @@ -244,7 +256,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn tool_attrs( + /// Return registered tool attributes with the given attribute name. + /// + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + pub fn tool_attrs( &self, def_id: stable_mir::DefId, attr: &[stable_mir::Symbol], @@ -268,7 +287,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn all_tool_attrs(&self, def_id: stable_mir::DefId) -> Vec { + /// Get all tool attributes of a definition. + pub fn all_tool_attrs( + &self, + def_id: stable_mir::DefId, + ) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; @@ -292,12 +315,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn span_to_string(&self, span: stable_mir::ty::Span) -> String { + /// Returns printable, human readable form of `Span`. + pub fn span_to_string(&self, span: stable_mir::ty::Span) -> String { let tables = self.0.borrow(); tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span]) } - fn get_filename(&self, span: &Span) -> Filename { + /// Return filename from given `Span`, for diagnostic purposes. + pub fn get_filename(&self, span: &Span) -> Filename { let tables = self.0.borrow(); tables .tcx @@ -308,23 +333,27 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .to_string() } - fn get_lines(&self, span: &Span) -> LineInfo { + /// Return lines corresponding to this `Span`. + pub fn get_lines(&self, span: &Span) -> LineInfo { let tables = self.0.borrow(); let lines = &tables.tcx.sess.source_map().span_to_location_info(tables[*span]); LineInfo { start_line: lines.1, start_col: lines.2, end_line: lines.3, end_col: lines.4 } } - fn item_kind(&self, item: CrateItem) -> ItemKind { + /// Returns the `kind` of given `DefId`. + pub 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: DefId) -> bool { + /// Returns whether this is a foreign item. + pub fn is_foreign_item(&self, item: DefId) -> bool { let tables = self.0.borrow(); tables.tcx.is_foreign_item(tables[item]) } - fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind { + /// Returns the kind of a given foreign item. + pub fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind { let mut tables = self.0.borrow_mut(); let def_id = tables[def.def_id()]; let tcx = tables.tcx; @@ -339,32 +368,37 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn adt_kind(&self, def: AdtDef) -> AdtKind { + /// Returns the kind of a given algebraic data type. + pub fn adt_kind(&self, def: AdtDef) -> AdtKind { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).adt_kind().stable(&mut *tables) } - fn adt_is_box(&self, def: AdtDef) -> bool { + /// Returns if the ADT is a box. + pub fn adt_is_box(&self, def: AdtDef) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).is_box() } - fn adt_is_simd(&self, def: AdtDef) -> bool { + /// Returns whether this ADT is simd. + pub fn adt_is_simd(&self, def: AdtDef) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).repr().simd() } - fn adt_is_cstr(&self, def: AdtDef) -> bool { + /// Returns whether this definition is a C string. + pub fn adt_is_cstr(&self, def: AdtDef) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); tables.tcx.is_lang_item(def_id, LangItem::CStr) } - fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { + /// Retrieve the function signature for the given generic arguments. + pub fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); @@ -373,7 +407,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { sig.stable(&mut *tables) } - fn intrinsic(&self, def: DefId) -> Option { + /// Retrieve the intrinsic definition if the item corresponds one. + pub fn intrinsic(&self, def: DefId) -> Option { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.internal(&mut *tables, tcx); @@ -381,14 +416,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> { intrinsic.map(|_| IntrinsicDef(def)) } - fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { + /// Retrieve the plain function name of an intrinsic. + pub fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); tcx.intrinsic(def_id).unwrap().name.to_string() } - fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { + /// Retrieve the closure signature for the given generic arguments. + pub fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let args_ref = args.internal(&mut *tables, tcx); @@ -396,25 +433,28 @@ impl<'tcx> Context for TablesWrapper<'tcx> { sig.stable(&mut *tables) } - fn adt_variants_len(&self, def: AdtDef) -> usize { + /// The number of variants in this ADT. + pub fn adt_variants_len(&self, def: AdtDef) -> usize { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).variants().len() } - fn variant_name(&self, def: VariantDef) -> Symbol { + /// The name of a variant. + pub fn variant_name(&self, def: VariantDef) -> Symbol { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).name.to_string() } - fn variant_fields(&self, def: VariantDef) -> Vec { + pub fn variant_fields(&self, def: VariantDef) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).fields.iter().map(|f| f.stable(&mut *tables)).collect() } - fn eval_target_usize(&self, cnst: &MirConst) -> Result { + /// Evaluate constant as a target usize. + pub fn eval_target_usize(&self, cnst: &MirConst) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); @@ -422,7 +462,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .try_eval_target_usize(tables.tcx, ty::TypingEnv::fully_monomorphized()) .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { + pub fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); @@ -431,7 +471,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn try_new_const_zst(&self, ty: Ty) -> Result { + /// Create a new zero-sized constant. + pub fn try_new_const_zst(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty_internal = ty.internal(&mut *tables, tcx); @@ -456,7 +497,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables)) } - fn new_const_str(&self, value: &str) -> MirConst { + /// Create a new constant that represents the given string value. + pub fn new_const_str(&self, value: &str) -> MirConst { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty::Ty::new_static_str(tcx); @@ -467,12 +509,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { mir::Const::from_value(val, ty).stable(&mut tables) } - fn new_const_bool(&self, value: bool) -> MirConst { + /// Create a new constant that represents the given boolean value. + pub fn new_const_bool(&self, value: bool) -> MirConst { let mut tables = self.0.borrow_mut(); mir::Const::from_bool(tables.tcx, value).stable(&mut tables) } - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { + /// Create a new constant that represents the given value. + pub fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx)); @@ -487,7 +531,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Ok(mir::Const::from_scalar(tcx, mir::interpret::Scalar::Int(scalar), ty) .stable(&mut tables)) } - fn try_new_ty_const_uint( + pub fn try_new_ty_const_uint( &self, value: u128, uint_ty: UintTy, @@ -509,27 +553,35 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables)) } - fn new_rigid_ty(&self, kind: RigidTy) -> stable_mir::ty::Ty { + /// Create a new type from the given kind. + pub fn new_rigid_ty(&self, kind: RigidTy) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let internal_kind = kind.internal(&mut *tables, tcx); tables.tcx.mk_ty_from_kind(internal_kind).stable(&mut *tables) } - fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty { + /// Create a new box type, `Box`, for the given inner type `T`. + pub fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let inner = ty.internal(&mut *tables, tcx); ty::Ty::new_box(tables.tcx, inner).stable(&mut *tables) } - fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty { + /// Returns the type of given crate item. + pub fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.type_of(item.internal(&mut *tables, tcx)).instantiate_identity().stable(&mut *tables) } - fn def_ty_with_args(&self, item: stable_mir::DefId, args: &GenericArgs) -> stable_mir::ty::Ty { + /// Returns the type of given definition instantiated with the given arguments. + pub fn def_ty_with_args( + &self, + item: stable_mir::DefId, + args: &GenericArgs, + ) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let args = args.internal(&mut *tables, tcx); @@ -544,33 +596,38 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables) } - fn mir_const_pretty(&self, cnst: &stable_mir::ty::MirConst) -> String { + /// Returns literal value of a const as a string. + pub fn mir_const_pretty(&self, cnst: &stable_mir::ty::MirConst) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; cnst.internal(&mut *tables, tcx).to_string() } - fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span { + /// `Span` of an item. + pub fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span { let mut tables = self.0.borrow_mut(); tables.tcx.def_span(tables[def_id]).stable(&mut *tables) } - fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String { + /// Obtain the representation of a type. + pub fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String { let tables = self.0.borrow_mut(); tables.types[ty].to_string() } - fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { + /// Obtain the representation of a type. + pub fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { let mut tables = self.0.borrow_mut(); tables.types[ty].kind().stable(&mut *tables) } - fn ty_const_pretty(&self, ct: stable_mir::ty::TyConstId) -> String { + pub fn ty_const_pretty(&self, ct: stable_mir::ty::TyConstId) -> String { let tables = self.0.borrow_mut(); tables.ty_consts[ct].to_string() } - fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> stable_mir::ty::Ty { + /// Get the discriminant Ty for this Ty if there's one. + pub fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let internal_kind = ty.internal(&mut *tables, tcx); @@ -578,7 +635,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { internal_ty.discriminant_ty(tables.tcx).stable(&mut *tables) } - fn instance_body(&self, def: InstanceDef) -> Option { + /// Get the body of an Instance which is already monomorphized. + pub fn instance_body(&self, def: InstanceDef) -> Option { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; tables @@ -586,63 +644,74 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .then(|| BodyBuilder::new(tables.tcx, instance).build(&mut *tables)) } - fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { + /// Get the instance type with generic instantiations applied and lifetimes erased. + pub fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; assert!(!instance.has_non_region_param(), "{instance:?} needs further instantiation"); instance.ty(tables.tcx, ty::TypingEnv::fully_monomorphized()).stable(&mut *tables) } - fn instance_args(&self, def: InstanceDef) -> GenericArgs { + /// Get the instantiation types. + pub fn instance_args(&self, def: InstanceDef) -> GenericArgs { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; instance.args.stable(&mut *tables) } - fn instance_abi(&self, def: InstanceDef) -> Result { + /// Get an instance ABI. + pub fn instance_abi(&self, def: InstanceDef) -> Result { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; Ok(tables.fn_abi_of_instance(instance, List::empty())?.stable(&mut *tables)) } - fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result { + /// Get the ABI of a function pointer. + pub fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let sig = fn_ptr.internal(&mut *tables, tcx); Ok(tables.fn_abi_of_fn_ptr(sig, List::empty())?.stable(&mut *tables)) } - fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId { + /// Get the instance. + pub fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId { let mut tables = self.0.borrow_mut(); let def_id = tables.instances[def].def_id(); tables.create_def_id(def_id) } - fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol { + /// Get the instance mangled name. + pub fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol { let tables = self.0.borrow_mut(); let instance = tables.instances[instance]; tables.tcx.symbol_name(instance).name.to_string() } - fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { + /// Check if this is an empty DropGlue shim. + pub fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; matches!(instance.def, ty::InstanceKind::DropGlue(_, None)) } - fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { + /// Check if this is an empty AsyncDropGlueCtor shim. + pub fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; matches!(instance.def, ty::InstanceKind::AsyncDropGlueCtorShim(_, None)) } - fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { + /// Convert a non-generic crate item into an instance. + /// This function will panic if the item is generic. + pub fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; Instance::mono(tables.tcx, def_id).stable(&mut *tables) } - fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool { + /// Item requires monomorphization. + pub fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool { let tables = self.0.borrow(); let def_id = tables[def_id]; let generics = tables.tcx.generics_of(def_id); @@ -650,7 +719,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { result } - fn resolve_instance( + /// Resolve an instance from the given function definition and generic arguments. + pub fn resolve_instance( &self, def: stable_mir::ty::FnDef, args: &stable_mir::ty::GenericArgs, @@ -670,7 +740,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance { + /// Resolve an instance for drop_in_place for the given type. + pub fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let internal_ty = ty.internal(&mut *tables, tcx); @@ -678,7 +749,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { instance.stable(&mut *tables) } - fn resolve_for_fn_ptr( + /// Resolve instance for a function pointer. + pub fn resolve_for_fn_ptr( &self, def: FnDef, args: &GenericArgs, @@ -696,7 +768,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables) } - fn resolve_closure( + /// Resolve instance for a closure with the requested type. + pub fn resolve_closure( &self, def: ClosureDef, args: &GenericArgs, @@ -713,7 +786,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ) } - fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result { + /// Try to evaluate an instance into a constant. + pub fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; let tcx = tables.tcx; @@ -733,21 +807,24 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .map_err(|e| e.stable(&mut *tables))? } - fn eval_static_initializer(&self, def: StaticDef) -> Result { + /// Evaluate a static's initializer. + pub fn eval_static_initializer(&self, def: StaticDef) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); tables.tcx.eval_static_initializer(def_id).stable(&mut *tables) } - fn global_alloc(&self, alloc: stable_mir::mir::alloc::AllocId) -> GlobalAlloc { + /// Retrieve global allocation for the given allocation ID. + pub fn global_alloc(&self, alloc: stable_mir::mir::alloc::AllocId) -> GlobalAlloc { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let alloc_id = alloc.internal(&mut *tables, tcx); tables.tcx.global_alloc(alloc_id).stable(&mut *tables) } - fn vtable_allocation( + /// Retrieve the id for the virtual table. + pub fn vtable_allocation( &self, global_alloc: &GlobalAlloc, ) -> Option { @@ -765,7 +842,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Some(alloc_id.stable(&mut *tables)) } - fn krate(&self, def_id: stable_mir::DefId) -> Crate { + pub fn krate(&self, def_id: stable_mir::DefId) -> Crate { let tables = self.0.borrow(); smir_crate(tables.tcx, tables[def_id].krate) } @@ -773,7 +850,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { /// Retrieve the instance name for diagnostic messages. /// /// This will return the specialized name, e.g., `Vec::new`. - fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol { + pub fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; if trimmed { @@ -787,7 +864,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn ty_layout(&self, ty: Ty) -> Result { + /// Get the layout of a type. + pub fn ty_layout(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty.internal(&mut *tables, tcx); @@ -795,19 +873,22 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Ok(layout.stable(&mut *tables)) } - fn layout_shape(&self, id: Layout) -> LayoutShape { + /// Get the layout shape. + pub fn layout_shape(&self, id: Layout) -> LayoutShape { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; id.internal(&mut *tables, tcx).0.stable(&mut *tables) } - fn place_pretty(&self, place: &Place) -> String { + /// Get a debug string representation of a place. + pub fn place_pretty(&self, place: &Place) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; format!("{:?}", place.internal(&mut *tables, tcx)) } - fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + /// Get the resulting type of binary operation. + pub fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let rhs_internal = rhs.internal(&mut *tables, tcx); @@ -816,7 +897,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ty.stable(&mut *tables) } - fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { + /// Get the resulting type of unary operation. + pub fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let arg_internal = arg.internal(&mut *tables, tcx); @@ -824,7 +906,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ty.stable(&mut *tables) } - fn associated_items(&self, def_id: stable_mir::DefId) -> stable_mir::AssocItems { + /// Get all associated items of a definition. + pub fn associated_items(&self, def_id: stable_mir::DefId) -> stable_mir::AssocItems { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = tables[def_id]; @@ -840,8 +923,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } -pub(crate) struct TablesWrapper<'tcx>(pub RefCell>); - /// Implement error handling for extracting function ABI information. impl<'tcx> FnAbiOfHelpers<'tcx> for Tables<'tcx> { type FnAbiOfResult = Result<&'tcx rustc_target::callconv::FnAbi<'tcx, ty::Ty<'tcx>>, Error>; diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index dea8f54a67097..b5003baaf633c 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -25,7 +25,7 @@ use crate::stable_mir; mod alloc; mod builder; -pub(crate) mod context; +pub mod context; mod convert; pub struct Tables<'tcx> { diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs index 33d950bb951cd..cd61907dc6baa 100644 --- a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs +++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs @@ -5,6 +5,7 @@ use std::cell::Cell; +use rustc_smir::context::SmirCtxt; use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::crate_def::Attribute; use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; @@ -22,47 +23,116 @@ use stable_mir::{ ItemKind, Symbol, TraitDecls, mir, }; -use crate::stable_mir; +use crate::{rustc_smir, stable_mir}; + +/// Stable public API for querying compiler information. +/// +/// All queries are delegated to an internal [`SmirCtxt`] that provides +/// similar APIs but based on internal rustc constructs. +/// +/// Do not use this directly. This is currently used in the macro expansion. +pub(crate) struct SmirInterface<'tcx> { + pub(crate) cx: SmirCtxt<'tcx>, +} + +impl<'tcx> SmirInterface<'tcx> { + pub(crate) fn entry_fn(&self) -> Option { + self.cx.entry_fn() + } -/// This trait defines the interface between stable_mir and the Rust compiler. -/// Do not use this directly. -pub trait Context { - fn entry_fn(&self) -> Option; /// Retrieve all items of the local crate that have a MIR associated with them. - fn all_local_items(&self) -> CrateItems; + pub(crate) fn all_local_items(&self) -> CrateItems { + self.cx.all_local_items() + } + /// Retrieve the body of a function. /// This function will panic if the body is not available. - fn mir_body(&self, item: DefId) -> mir::Body; + pub(crate) fn mir_body(&self, item: DefId) -> mir::Body { + self.cx.mir_body(item) + } + /// Check whether the body of a function is available. - fn has_body(&self, item: DefId) -> bool; - fn foreign_modules(&self, crate_num: CrateNum) -> Vec; + pub(crate) fn has_body(&self, item: DefId) -> bool { + self.cx.has_body(item) + } + + pub(crate) fn foreign_modules(&self, crate_num: CrateNum) -> Vec { + self.cx.foreign_modules(crate_num) + } /// Retrieve all functions defined in this crate. - fn crate_functions(&self, crate_num: CrateNum) -> Vec; + pub(crate) fn crate_functions(&self, crate_num: CrateNum) -> Vec { + self.cx.crate_functions(crate_num) + } /// Retrieve all static items defined in this crate. - fn crate_statics(&self, crate_num: CrateNum) -> Vec; - fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule; - fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec; - fn all_trait_decls(&self) -> TraitDecls; - fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls; - fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl; - fn all_trait_impls(&self) -> ImplTraitDecls; - fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls; - fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait; - fn generics_of(&self, def_id: DefId) -> Generics; - fn predicates_of(&self, def_id: DefId) -> GenericPredicates; - fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates; + pub(crate) fn crate_statics(&self, crate_num: CrateNum) -> Vec { + self.cx.crate_statics(crate_num) + } + + pub(crate) fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule { + self.cx.foreign_module(mod_def) + } + + pub(crate) fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec { + self.cx.foreign_items(mod_def) + } + + pub(crate) fn all_trait_decls(&self) -> TraitDecls { + self.cx.all_trait_decls() + } + + pub(crate) fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls { + self.cx.trait_decls(crate_num) + } + + pub(crate) fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl { + self.cx.trait_decl(trait_def) + } + + pub(crate) fn all_trait_impls(&self) -> ImplTraitDecls { + self.cx.all_trait_impls() + } + + pub(crate) fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls { + self.cx.trait_impls(crate_num) + } + + pub(crate) fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait { + self.cx.trait_impl(trait_impl) + } + + pub(crate) fn generics_of(&self, def_id: DefId) -> Generics { + self.cx.generics_of(def_id) + } + + pub(crate) fn predicates_of(&self, def_id: DefId) -> GenericPredicates { + self.cx.predicates_of(def_id) + } + + pub(crate) fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates { + self.cx.explicit_predicates_of(def_id) + } + /// Get information about the local crate. - fn local_crate(&self) -> Crate; + pub(crate) fn local_crate(&self) -> Crate { + self.cx.local_crate() + } + /// Retrieve a list of all external crates. - fn external_crates(&self) -> Vec; + pub(crate) fn external_crates(&self) -> Vec { + self.cx.external_crates() + } /// Find a crate with the given name. - fn find_crates(&self, name: &str) -> Vec; + pub(crate) fn find_crates(&self, name: &str) -> Vec { + self.cx.find_crates(name) + } - /// Returns the name of given `DefId` - fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; + /// Returns the name of given `DefId`. + pub(crate) fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol { + self.cx.def_name(def_id, trimmed) + } /// Return registered tool attributes with the given attribute name. /// @@ -71,218 +141,362 @@ pub trait Context { /// /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. - fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec; + pub(crate) fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec { + self.cx.tool_attrs(def_id, attr) + } /// Get all tool attributes of a definition. - fn all_tool_attrs(&self, def_id: DefId) -> Vec; + pub(crate) fn all_tool_attrs(&self, def_id: DefId) -> Vec { + self.cx.all_tool_attrs(def_id) + } - /// Returns printable, human readable form of `Span` - fn span_to_string(&self, span: Span) -> String; + /// Returns printable, human readable form of `Span`. + pub(crate) fn span_to_string(&self, span: Span) -> String { + self.cx.span_to_string(span) + } - /// Return filename from given `Span`, for diagnostic purposes - fn get_filename(&self, span: &Span) -> Filename; + /// Return filename from given `Span`, for diagnostic purposes. + pub(crate) fn get_filename(&self, span: &Span) -> Filename { + self.cx.get_filename(span) + } - /// Return lines corresponding to this `Span` - fn get_lines(&self, span: &Span) -> LineInfo; + /// Return lines corresponding to this `Span`. + pub(crate) fn get_lines(&self, span: &Span) -> LineInfo { + self.cx.get_lines(span) + } - /// Returns the `kind` of given `DefId` - fn item_kind(&self, item: CrateItem) -> ItemKind; + /// Returns the `kind` of given `DefId`. + pub(crate) fn item_kind(&self, item: CrateItem) -> ItemKind { + self.cx.item_kind(item) + } /// Returns whether this is a foreign item. - fn is_foreign_item(&self, item: DefId) -> bool; + pub(crate) fn is_foreign_item(&self, item: DefId) -> bool { + self.cx.is_foreign_item(item) + } /// Returns the kind of a given foreign item. - fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind; + pub(crate) fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind { + self.cx.foreign_item_kind(def) + } - /// Returns the kind of a given algebraic data type - fn adt_kind(&self, def: AdtDef) -> AdtKind; + /// Returns the kind of a given algebraic data type. + pub(crate) fn adt_kind(&self, def: AdtDef) -> AdtKind { + self.cx.adt_kind(def) + } /// Returns if the ADT is a box. - fn adt_is_box(&self, def: AdtDef) -> bool; + pub(crate) fn adt_is_box(&self, def: AdtDef) -> bool { + self.cx.adt_is_box(def) + } /// Returns whether this ADT is simd. - fn adt_is_simd(&self, def: AdtDef) -> bool; + pub(crate) fn adt_is_simd(&self, def: AdtDef) -> bool { + self.cx.adt_is_simd(def) + } /// Returns whether this definition is a C string. - fn adt_is_cstr(&self, def: AdtDef) -> bool; + pub(crate) fn adt_is_cstr(&self, def: AdtDef) -> bool { + self.cx.adt_is_cstr(def) + } /// Retrieve the function signature for the given generic arguments. - fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig; + pub(crate) fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { + self.cx.fn_sig(def, args) + } /// Retrieve the intrinsic definition if the item corresponds one. - fn intrinsic(&self, item: DefId) -> Option; + pub(crate) fn intrinsic(&self, item: DefId) -> Option { + self.cx.intrinsic(item) + } /// Retrieve the plain function name of an intrinsic. - fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol; + pub(crate) fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { + self.cx.intrinsic_name(def) + } /// Retrieve the closure signature for the given generic arguments. - fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig; + pub(crate) fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { + self.cx.closure_sig(args) + } /// The number of variants in this ADT. - fn adt_variants_len(&self, def: AdtDef) -> usize; + pub(crate) fn adt_variants_len(&self, def: AdtDef) -> usize { + self.cx.adt_variants_len(def) + } /// The name of a variant. - fn variant_name(&self, def: VariantDef) -> Symbol; - fn variant_fields(&self, def: VariantDef) -> Vec; + pub(crate) fn variant_name(&self, def: VariantDef) -> Symbol { + self.cx.variant_name(def) + } + + pub(crate) fn variant_fields(&self, def: VariantDef) -> Vec { + self.cx.variant_fields(def) + } /// Evaluate constant as a target usize. - fn eval_target_usize(&self, cnst: &MirConst) -> Result; - fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result; + pub(crate) fn eval_target_usize(&self, cnst: &MirConst) -> Result { + self.cx.eval_target_usize(cnst) + } + + pub(crate) fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { + self.cx.eval_target_usize_ty(cnst) + } /// Create a new zero-sized constant. - fn try_new_const_zst(&self, ty: Ty) -> Result; + pub(crate) fn try_new_const_zst(&self, ty: Ty) -> Result { + self.cx.try_new_const_zst(ty) + } /// Create a new constant that represents the given string value. - fn new_const_str(&self, value: &str) -> MirConst; + pub(crate) fn new_const_str(&self, value: &str) -> MirConst { + self.cx.new_const_str(value) + } /// Create a new constant that represents the given boolean value. - fn new_const_bool(&self, value: bool) -> MirConst; + pub(crate) fn new_const_bool(&self, value: bool) -> MirConst { + self.cx.new_const_bool(value) + } /// Create a new constant that represents the given value. - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; - fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; + pub(crate) fn try_new_const_uint( + &self, + value: u128, + uint_ty: UintTy, + ) -> Result { + self.cx.try_new_const_uint(value, uint_ty) + } + + pub(crate) fn try_new_ty_const_uint( + &self, + value: u128, + uint_ty: UintTy, + ) -> Result { + self.cx.try_new_ty_const_uint(value, uint_ty) + } /// Create a new type from the given kind. - fn new_rigid_ty(&self, kind: RigidTy) -> Ty; + pub(crate) fn new_rigid_ty(&self, kind: RigidTy) -> Ty { + self.cx.new_rigid_ty(kind) + } /// Create a new box type, `Box`, for the given inner type `T`. - fn new_box_ty(&self, ty: Ty) -> Ty; + pub(crate) fn new_box_ty(&self, ty: Ty) -> Ty { + self.cx.new_box_ty(ty) + } /// Returns the type of given crate item. - fn def_ty(&self, item: DefId) -> Ty; + pub(crate) fn def_ty(&self, item: DefId) -> Ty { + self.cx.def_ty(item) + } /// Returns the type of given definition instantiated with the given arguments. - fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; + pub(crate) fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty { + self.cx.def_ty_with_args(item, args) + } /// Returns literal value of a const as a string. - fn mir_const_pretty(&self, cnst: &MirConst) -> String; + pub(crate) fn mir_const_pretty(&self, cnst: &MirConst) -> String { + self.cx.mir_const_pretty(cnst) + } - /// `Span` of an item - fn span_of_an_item(&self, def_id: DefId) -> Span; + /// `Span` of an item. + pub(crate) fn span_of_an_item(&self, def_id: DefId) -> Span { + self.cx.span_of_an_item(def_id) + } - fn ty_const_pretty(&self, ct: TyConstId) -> String; + pub(crate) fn ty_const_pretty(&self, ct: TyConstId) -> String { + self.cx.ty_const_pretty(ct) + } /// Obtain the representation of a type. - fn ty_pretty(&self, ty: Ty) -> String; + pub(crate) fn ty_pretty(&self, ty: Ty) -> String { + self.cx.ty_pretty(ty) + } /// Obtain the representation of a type. - fn ty_kind(&self, ty: Ty) -> TyKind; + pub(crate) fn ty_kind(&self, ty: Ty) -> TyKind { + self.cx.ty_kind(ty) + } - // Get the discriminant Ty for this Ty if there's one. - fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty; + /// Get the discriminant Ty for this Ty if there's one. + pub(crate) fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty { + self.cx.rigid_ty_discriminant_ty(ty) + } /// Get the body of an Instance which is already monomorphized. - fn instance_body(&self, instance: InstanceDef) -> Option; + pub(crate) fn instance_body(&self, instance: InstanceDef) -> Option { + self.cx.instance_body(instance) + } /// Get the instance type with generic instantiations applied and lifetimes erased. - fn instance_ty(&self, instance: InstanceDef) -> Ty; + pub(crate) fn instance_ty(&self, instance: InstanceDef) -> Ty { + self.cx.instance_ty(instance) + } /// Get the instantiation types. - fn instance_args(&self, def: InstanceDef) -> GenericArgs; + pub(crate) fn instance_args(&self, def: InstanceDef) -> GenericArgs { + self.cx.instance_args(def) + } /// Get the instance. - fn instance_def_id(&self, instance: InstanceDef) -> DefId; + pub(crate) fn instance_def_id(&self, instance: InstanceDef) -> DefId { + self.cx.instance_def_id(instance) + } /// Get the instance mangled name. - fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol; + pub(crate) fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol { + self.cx.instance_mangled_name(instance) + } /// Check if this is an empty DropGlue shim. - fn is_empty_drop_shim(&self, def: InstanceDef) -> bool; + pub(crate) fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { + self.cx.is_empty_drop_shim(def) + } /// Check if this is an empty AsyncDropGlueCtor shim. - fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool; + pub(crate) fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { + self.cx.is_empty_async_drop_ctor_shim(def) + } /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. - fn mono_instance(&self, def_id: DefId) -> Instance; + pub(crate) fn mono_instance(&self, def_id: DefId) -> Instance { + self.cx.mono_instance(def_id) + } /// Item requires monomorphization. - fn requires_monomorphization(&self, def_id: DefId) -> bool; + pub(crate) fn requires_monomorphization(&self, def_id: DefId) -> bool { + self.cx.requires_monomorphization(def_id) + } /// Resolve an instance from the given function definition and generic arguments. - fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; + pub(crate) fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option { + self.cx.resolve_instance(def, args) + } /// Resolve an instance for drop_in_place for the given type. - fn resolve_drop_in_place(&self, ty: Ty) -> Instance; + pub(crate) fn resolve_drop_in_place(&self, ty: Ty) -> Instance { + self.cx.resolve_drop_in_place(ty) + } /// Resolve instance for a function pointer. - fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option; + pub(crate) fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option { + self.cx.resolve_for_fn_ptr(def, args) + } /// Resolve instance for a closure with the requested type. - fn resolve_closure( + pub(crate) fn resolve_closure( &self, def: ClosureDef, args: &GenericArgs, kind: ClosureKind, - ) -> Option; + ) -> Option { + self.cx.resolve_closure(def, args, kind) + } /// Evaluate a static's initializer. - fn eval_static_initializer(&self, def: StaticDef) -> Result; + pub(crate) fn eval_static_initializer(&self, def: StaticDef) -> Result { + self.cx.eval_static_initializer(def) + } /// Try to evaluate an instance into a constant. - fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result; + pub(crate) fn eval_instance( + &self, + def: InstanceDef, + const_ty: Ty, + ) -> Result { + self.cx.eval_instance(def, const_ty) + } /// Retrieve global allocation for the given allocation ID. - fn global_alloc(&self, id: AllocId) -> GlobalAlloc; + pub(crate) fn global_alloc(&self, id: AllocId) -> GlobalAlloc { + self.cx.global_alloc(id) + } /// Retrieve the id for the virtual table. - fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option; - fn krate(&self, def_id: DefId) -> Crate; - fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol; + pub(crate) fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option { + self.cx.vtable_allocation(global_alloc) + } + + pub(crate) fn krate(&self, def_id: DefId) -> Crate { + self.cx.krate(def_id) + } + + pub(crate) fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol { + self.cx.instance_name(def, trimmed) + } /// Return information about the target machine. - fn target_info(&self) -> MachineInfo; + pub(crate) fn target_info(&self) -> MachineInfo { + self.cx.target_info() + } /// Get an instance ABI. - fn instance_abi(&self, def: InstanceDef) -> Result; + pub(crate) fn instance_abi(&self, def: InstanceDef) -> Result { + self.cx.instance_abi(def) + } /// Get the ABI of a function pointer. - fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result; + pub(crate) fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result { + self.cx.fn_ptr_abi(fn_ptr) + } /// Get the layout of a type. - fn ty_layout(&self, ty: Ty) -> Result; + pub(crate) fn ty_layout(&self, ty: Ty) -> Result { + self.cx.ty_layout(ty) + } /// Get the layout shape. - fn layout_shape(&self, id: Layout) -> LayoutShape; + pub(crate) fn layout_shape(&self, id: Layout) -> LayoutShape { + self.cx.layout_shape(id) + } /// Get a debug string representation of a place. - fn place_pretty(&self, place: &Place) -> String; + pub(crate) fn place_pretty(&self, place: &Place) -> String { + self.cx.place_pretty(place) + } /// Get the resulting type of binary operation. - fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; + pub(crate) fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + self.cx.binop_ty(bin_op, rhs, lhs) + } /// Get the resulting type of unary operation. - fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty; + pub(crate) fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { + self.cx.unop_ty(un_op, arg) + } /// Get all associated items of a definition. - fn associated_items(&self, def_id: DefId) -> AssocItems; + pub(crate) fn associated_items(&self, def_id: DefId) -> AssocItems { + self.cx.associated_items(def_id) + } } -// A thread local variable that stores a pointer to the tables mapping between TyCtxt -// datastructures and stable MIR datastructures +// A thread local variable that stores a pointer to [`SmirInterface`]. scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>); -pub fn run(context: &dyn Context, f: F) -> Result +pub(crate) fn run<'tcx, T, F>(interface: &SmirInterface<'tcx>, f: F) -> Result where F: FnOnce() -> T, { if TLV.is_set() { Err(Error::from("StableMIR already running")) } else { - let ptr: *const () = (&raw const context) as _; + let ptr: *const () = (interface as *const SmirInterface<'tcx>) as *const (); TLV.set(&Cell::new(ptr), || Ok(f())) } } -/// Execute the given function with access the compiler [Context]. +/// Execute the given function with access the [`SmirInterface`]. /// -/// I.e., This function will load the current context and calls a function with it. +/// I.e., This function will load the current interface and calls a function with it. /// Do not nest these, as that will ICE. -pub(crate) fn with(f: impl FnOnce(&dyn Context) -> R) -> R { +pub(crate) fn with(f: impl FnOnce(&SmirInterface<'_>) -> R) -> R { assert!(TLV.is_set()); TLV.with(|tlv| { let ptr = tlv.get(); assert!(!ptr.is_null()); - f(unsafe { *(ptr as *const &dyn Context) }) + f(unsafe { &*(ptr as *const SmirInterface<'_>) }) }) } diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index da298080ed2f6..28335734f4dec 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -45,7 +45,7 @@ pub const ALL_EDITIONS: &[Edition] = &[ Edition::EditionFuture, ]; -pub const EDITION_NAME_LIST: &str = "2015|2018|2021|2024"; +pub const EDITION_NAME_LIST: &str = "<2015|2018|2021|2024|future>"; pub const DEFAULT_EDITION: Edition = Edition::Edition2015; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f788fd48037a0..fccdaed21a20b 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,6 +17,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -24,7 +25,6 @@ #![feature(core_io_borrowed_buf)] #![feature(hash_set_entry)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index cc33974cc6275..ca8918e06aaf3 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -89,9 +89,9 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 7ecc46cc69db9..ae366e29e3232 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -144,6 +144,7 @@ pub struct ArgAttributes { /// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be /// set on a null pointer, but all non-null pointers must be dereferenceable). pub pointee_size: Size, + /// The minimum alignment of the pointee, if any. pub pointee_align: Option, } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index df99280f5712e..922c18448d51a 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -9,12 +9,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(debug_closure_helpers)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3c769dad630a1..37ea0d6e7b58c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -42,7 +42,9 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; -use rustc_abi::{Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; +use rustc_abi::{ + Align, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -3599,6 +3601,25 @@ impl Target { _ => return None, }) } + + /// Returns whether this target is known to have unreliable alignment: + /// native C code for the target fails to align some data to the degree + /// required by the C standard. We can't *really* do anything about that + /// since unsafe Rust code may assume alignment any time, but we can at least + /// inhibit some optimizations, and we suppress the alignment checks that + /// would detect this unsoundness. + /// + /// Every target that returns less than `Align::MAX` here is still has a soundness bug. + pub fn max_reliable_alignment(&self) -> Align { + // FIXME(#112480) MSVC on x86-32 is unsound and fails to properly align many types with + // more-than-4-byte-alignment on the stack. This makes alignments larger than 4 generally + // unreliable on 32bit Windows. + if self.is_like_windows && self.arch == "x86" { + Align::from_bytes(4).unwrap() + } else { + Align::MAX + } + } } /// Either a target tuple string or a path to a JSON file. diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index aeace6a40c72e..69c8b9119ab23 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -516,7 +516,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zawrs", Unstable(sym::riscv_target_feature), &[]), ("zba", Stable, &[]), ("zbb", Stable, &[]), - ("zbc", Stable, &[]), + ("zbc", Stable, &["zbkc"]), // Zbc ⊃ Zbkc ("zbkb", Stable, &[]), ("zbkc", Stable, &[]), ("zbkx", Stable, &[]), @@ -545,20 +545,20 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zknd", Stable, &[]), ("zkne", Stable, &[]), ("zknh", Stable, &[]), - ("zkr", Stable, &["zicsr"]), + ("zkr", Stable, &[]), ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), ("zksed", Stable, &[]), ("zksh", Stable, &[]), ("zkt", Stable, &[]), ("ztso", Unstable(sym::riscv_target_feature), &[]), - ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), + ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), // Zvbb ⊃ Zvkb ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]), ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]), ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]), ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]), ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]), ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]), - ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zfhmin"]), + ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zve32f", "zfhmin"]), // Zvfh ⊃ Zvfhmin ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]), ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]), ("zvkg", Unstable(sym::riscv_target_feature), &["zve32x"]), @@ -567,7 +567,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zvkned", Unstable(sym::riscv_target_feature), &["zve32x"]), ("zvkng", Unstable(sym::riscv_target_feature), &["zvkn", "zvkg"]), ("zvknha", Unstable(sym::riscv_target_feature), &["zve32x"]), - ("zvknhb", Unstable(sym::riscv_target_feature), &["zve64x"]), + ("zvknhb", Unstable(sym::riscv_target_feature), &["zvknha", "zve64x"]), // Zvknhb ⊃ Zvknha ("zvks", Unstable(sym::riscv_target_feature), &["zvksed", "zvksh", "zvkb", "zvkt"]), ("zvksc", Unstable(sym::riscv_target_feature), &["zvks", "zvbc"]), ("zvksed", Unstable(sym::riscv_target_feature), &["zve32x"]), @@ -775,7 +775,7 @@ const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[ (32768, "zvl32768b"), (65536, "zvl65536b"), ]; -// Always warn on SPARC, as the necessary target features cannot be enabled in Rust at the moment. +// Always error on SPARC, as the necessary target features cannot be enabled in Rust at the moment. const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/]; const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index 1c3e570b67695..d8b405e904c06 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -81,9 +81,7 @@ pub fn call_kind<'tcx>( } }); - let fn_call = parent.and_then(|p| { - lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) - }); + let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some()); let operator = if !from_hir_call && let Some(p) = parent { lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5648021f6134e..4330f1f22925d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -146,7 +146,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && leaf_trait_predicate.def_id() != root_pred.def_id() // The root trait is not `Unsize`, as to avoid talking about it in // `tests/ui/coercion/coerce-issue-49593-box-never.rs`. - && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait() + && !self.tcx.is_lang_item(root_pred.def_id(), LangItem::Unsize) { ( self.resolve_vars_if_possible( @@ -1523,19 +1523,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return None; }; - let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_term.def_id)?; - let trait_assoc_ident = trait_assoc_item.ident(self.tcx); - let mut associated_items = vec![]; self.tcx.for_each_relevant_impl( self.tcx.trait_of_item(proj.projection_term.def_id)?, proj.projection_term.self_ty(), |impl_def_id| { associated_items.extend( - self.tcx - .associated_items(impl_def_id) - .in_definition_order() - .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident), + self.tcx.associated_items(impl_def_id).in_definition_order().find( + |assoc| { + assoc.trait_item_def_id == Some(proj.projection_term.def_id) + }, + ), ); }, ); @@ -2274,10 +2272,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // auto-traits or fundamental traits that might not be exactly what // the user might expect to be presented with. Instead this is // useful for less general traits. - if peeled - && !self.tcx.trait_is_auto(def_id) - && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) - { + if peeled && !self.tcx.trait_is_auto(def_id) && self.tcx.as_lang_item(def_id).is_none() { let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( &impl_candidates, @@ -3013,8 +3008,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // This shouldn't be common unless manually implementing one of the // traits manually, but don't make it more confusing when it does // happen. - if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled - { + if !self.tcx.is_lang_item(expected_trait_ref.def_id, LangItem::Coroutine) && not_tupled { return Ok(self.report_and_explain_type_error( TypeTrace::trait_refs(&obligation.cause, expected_trait_ref, found_trait_ref), obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 7d95a7b3fed24..de251ae2893c4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3844,12 +3844,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() && let ty::ClauseKind::Trait(pred) = clause - && [ - tcx.lang_items().fn_once_trait(), - tcx.lang_items().fn_mut_trait(), - tcx.lang_items().fn_trait(), - ] - .contains(&Some(pred.def_id())) + && tcx.fn_trait_kind_from_def_id(pred.def_id()).is_some() { if let [stmt, ..] = block.stmts && let hir::StmtKind::Semi(value) = stmt.kind diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 756d9a57b935c..1063115ed237f 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; -use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, IsAnonInPath, Node}; +use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt}; @@ -551,19 +551,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { impl<'v> Visitor<'v> for ImplicitLifetimeFinder { fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { - let make_suggestion = |lifetime: &hir::Lifetime| { - if lifetime.is_anon_in_path == IsAnonInPath::Yes - && lifetime.ident.span.is_empty() - { - format!("{}, ", self.suggestion_param_name) - } else if lifetime.ident.name == kw::UnderscoreLifetime - && lifetime.ident.span.is_empty() - { - format!("{} ", self.suggestion_param_name) - } else { - self.suggestion_param_name.clone() - } - }; match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { for segment in path.segments { @@ -572,7 +559,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { matches!( arg, hir::GenericArg::Lifetime(lifetime) - if lifetime.is_anon_in_path == IsAnonInPath::Yes + if lifetime.is_syntactically_hidden() ) }) { self.suggestions.push(( @@ -591,10 +578,10 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { if let hir::GenericArg::Lifetime(lifetime) = arg && lifetime.is_anonymous() { - self.suggestions.push(( - lifetime.ident.span, - make_suggestion(lifetime), - )); + self.suggestions.push( + lifetime + .suggestion(&self.suggestion_param_name), + ); } } } @@ -602,7 +589,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { } } hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { - self.suggestions.push((lifetime.ident.span, make_suggestion(lifetime))); + self.suggestions.push(lifetime.suggestion(&self.suggestion_param_name)); } _ => {} } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 84ac229b743d9..0dab3adadb033 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -34,7 +34,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#132279): This should be removed as it causes us to incorrectly // handle opaques in their defining scope. - if !(param_env, ty).has_infer() { + if !self.next_trait_solver() && !(param_env, ty).has_infer() { return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 93c1180530452..7613a0cef52a7 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -14,6 +14,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -23,7 +24,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(try_blocks)] diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index d425ab50ae0fd..0c2451a80a705 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -9,5 +9,8 @@ mod select; pub(crate) use delegate::SolverDelegate; pub use fulfill::{FulfillmentCtxt, NextSolverError}; pub(crate) use normalize::deeply_normalize_for_diagnostics; -pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +pub use normalize::{ + deeply_normalize, deeply_normalize_with_skipped_universes, + deeply_normalize_with_skipped_universes_and_ambiguous_goals, +}; pub use select::InferCtxtSelectExt; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 192e632a2d5b9..1f4fa5aac102a 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,18 +1,26 @@ use std::marker::PhantomData; use std::mem; +use std::ops::ControlFlow; use rustc_data_structures::thinvec::ExtractIf; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, +}; use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; +use rustc_span::Span; +use rustc_type_ir::data_structures::DelayedSet; use tracing::instrument; use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; +use super::inspect::{self, ProofTreeInferCtxtExt}; use crate::traits::{FulfillmentError, ScrubbedTraitError}; mod derive_errors; @@ -39,7 +47,7 @@ pub struct FulfillmentCtxt<'tcx, E: 'tcx> { _errors: PhantomData, } -#[derive(Default)] +#[derive(Default, Debug)] struct ObligationStorage<'tcx> { /// Obligations which resulted in an overflow in fulfillment itself. /// @@ -55,20 +63,23 @@ impl<'tcx> ObligationStorage<'tcx> { self.pending.push(obligation); } + fn has_pending_obligations(&self) -> bool { + !self.pending.is_empty() || !self.overflowed.is_empty() + } + fn clone_pending(&self) -> PredicateObligations<'tcx> { let mut obligations = self.pending.clone(); obligations.extend(self.overflowed.iter().cloned()); obligations } - fn take_pending(&mut self) -> PredicateObligations<'tcx> { - let mut obligations = mem::take(&mut self.pending); - obligations.append(&mut self.overflowed); - obligations - } - - fn unstalled_for_select(&mut self) -> impl Iterator> + 'tcx { - mem::take(&mut self.pending).into_iter() + fn drain_pending( + &mut self, + cond: impl Fn(&PredicateObligation<'tcx>) -> bool, + ) -> PredicateObligations<'tcx> { + let (unstalled, pending) = mem::take(&mut self.pending).into_iter().partition(cond); + self.pending = pending; + unstalled } fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) { @@ -152,15 +163,15 @@ where fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); - for i in 0.. { - if !infcx.tcx.recursion_limit().value_within_limit(i) { - self.obligations.on_fulfillment_overflow(infcx); - // Only return true errors that we have accumulated while processing. - return errors; - } - + loop { let mut has_changed = false; - for obligation in self.obligations.unstalled_for_select() { + for mut obligation in self.obligations.drain_pending(|_| true) { + if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + let goal = obligation.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) @@ -178,6 +189,13 @@ where }; if changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; has_changed = true; } @@ -196,15 +214,95 @@ where } fn has_pending_obligations(&self) -> bool { - !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty() + self.obligations.has_pending_obligations() } fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.obligations.clone_pending() } - fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> { - self.obligations.take_pending() + fn drain_stalled_obligations_for_coroutines( + &mut self, + infcx: &InferCtxt<'tcx>, + ) -> PredicateObligations<'tcx> { + let stalled_generators = match infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + TypingMode::Coherence + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::PostAnalysis => return Default::default(), + }; + + if stalled_generators.is_empty() { + return Default::default(); + } + + self.obligations.drain_pending(|obl| { + infcx.probe(|_| { + infcx + .visit_proof_tree( + obl.as_goal(), + &mut StalledOnCoroutines { + stalled_generators, + span: obl.cause.span, + cache: Default::default(), + }, + ) + .is_break() + }) + }) + } +} + +/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root. +/// +/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to +/// be complete. However, this will lead to ambiguity errors, so we want to make it +/// accurate. +/// +/// This function can be also return false positives, which will lead to poor diagnostics +/// so we want to keep this visitor *precise* too. +struct StalledOnCoroutines<'tcx> { + stalled_generators: &'tcx ty::List, + span: Span, + cache: DelayedSet>, +} + +impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> { + type Result = ControlFlow<()>; + + fn span(&self) -> rustc_span::Span { + self.span + } + + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + inspect_goal.goal().predicate.visit_with(self)?; + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Continue(()) + } + } +} + +impl<'tcx> TypeVisitor> for StalledOnCoroutines<'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if !self.cache.insert(ty) { + return ControlFlow::Continue(()); + } + + if let ty::CoroutineWitness(def_id, _) = *ty.kind() + && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id)) + { + return ControlFlow::Break(()); + } + + ty.super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index d8dcd12aecb98..f6c650c68c01c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -1,5 +1,6 @@ use std::ops::ControlFlow; +use rustc_hir::LangItem; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ @@ -109,10 +110,16 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( false, ), Ok((_, Certainty::Yes)) => { - bug!("did not expect successful goal when collecting ambiguity errors") + bug!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) } Err(_) => { - bug!("did not expect selection error when collecting ambiguity errors") + bug!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) } } }); @@ -452,9 +459,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { // We do this as a separate loop so that we do not choose to tell the user about some nested // goal before we encounter a `T: FnPtr` nested goal. for nested_goal in &nested_goals { - if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() - && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() - && poly_trait_pred.def_id() == fn_ptr_trait + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && tcx.is_lang_item(poly_trait_pred.def_id(), LangItem::FnPtrTrait) && let Err(NoSolution) = nested_goal.result() { return ControlFlow::Break(self.obligation.clone()); diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 79fb044a67f88..5f1e63ab225ab 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,10 +1,10 @@ use std::assert_matches::assert_matches; use std::fmt::Debug; -use std::marker::PhantomData; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::InferCtxt; use rustc_infer::infer::at::At; +use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ @@ -41,15 +41,41 @@ pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>( value: T, universes: Vec>, ) -> Result> +where + T: TypeFoldable>, + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ + let (value, goals) = + deeply_normalize_with_skipped_universes_and_ambiguous_goals(at, value, universes)?; + assert_eq!(goals, vec![]); + + Ok(value) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +/// +/// This returns a set of stalled obligations if the typing mode of the underlying infcx +/// has any stalled coroutine def ids. +pub fn deeply_normalize_with_skipped_universes_and_ambiguous_goals<'tcx, T, E>( + at: At<'_, 'tcx>, + value: T, + universes: Vec>, +) -> Result<(T, Vec>>), Vec> where T: TypeFoldable>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { let fulfill_cx = FulfillmentCtxt::new(at.infcx); let mut folder = - NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData }; - - value.try_fold_with(&mut folder) + NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_goals: vec![] }; + let value = value.try_fold_with(&mut folder)?; + let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + if errors.is_empty() { Ok((value, folder.stalled_goals)) } else { Err(errors) } } struct NormalizationFolder<'me, 'tcx, E> { @@ -57,7 +83,7 @@ struct NormalizationFolder<'me, 'tcx, E> { fulfill_cx: FulfillmentCtxt<'tcx, E>, depth: usize, universes: Vec>, - _errors: PhantomData, + stalled_goals: Vec>>, } impl<'tcx, E> NormalizationFolder<'_, 'tcx, E> @@ -98,10 +124,7 @@ where ); self.fulfill_cx.register_predicate_obligation(infcx, obligation); - let errors = self.fulfill_cx.select_all_or_error(infcx); - if !errors.is_empty() { - return Err(errors); - } + self.select_all_and_stall_coroutine_predicates()?; // Alias is guaranteed to be fully structurally resolved, // so we can super fold here. @@ -139,7 +162,7 @@ where let result = if infcx.predicate_may_hold(&obligation) { self.fulfill_cx.register_predicate_obligation(infcx, obligation); - let errors = self.fulfill_cx.select_all_or_error(infcx); + let errors = self.fulfill_cx.select_where_possible(infcx); if !errors.is_empty() { return Err(errors); } @@ -152,6 +175,27 @@ where self.depth -= 1; Ok(result) } + + fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec> { + let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + self.stalled_goals.extend( + self.fulfill_cx + .drain_stalled_obligations_for_coroutines(self.at.infcx) + .into_iter() + .map(|obl| obl.as_goal()), + ); + + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(()) + } } impl<'tcx, E> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx, E> @@ -254,27 +298,31 @@ impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { let infcx = self.at.infcx; - infcx - .commit_if_ok(|_| { - deeply_normalize_with_skipped_universes( - self.at, - ty, - vec![None; ty.outer_exclusive_binder().as_usize()], - ) - }) - .unwrap_or_else(|_: Vec>| ty.super_fold_with(self)) + let result = + infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_goals::< + _, + ScrubbedTraitError<'tcx>, + >(self.at, ty, vec![None; ty.outer_exclusive_binder().as_usize()]) + }); + match result { + Ok((ty, _)) => ty, + Err(_) => ty.super_fold_with(self), + } } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { let infcx = self.at.infcx; - infcx - .commit_if_ok(|_| { - deeply_normalize_with_skipped_universes( - self.at, - ct, - vec![None; ct.outer_exclusive_binder().as_usize()], - ) - }) - .unwrap_or_else(|_: Vec>| ct.super_fold_with(self)) + let result = + infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_goals::< + _, + ScrubbedTraitError<'tcx>, + >(self.at, ct, vec![None; ct.outer_exclusive_binder().as_usize()]) + }); + match result { + Ok((ct, _)) => ct, + Err(_) => ct.super_fold_with(self), + } } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 1b76d48e43109..a11f8d3a9ec6b 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -162,7 +162,7 @@ where self.select(selcx) } - fn drain_unstalled_obligations( + fn drain_stalled_obligations_for_coroutines( &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 4ac45172a0e1c..7551ac5aa9735 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -77,7 +77,15 @@ impl<'tcx> At<'_, 'tcx> { .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); let errors = fulfill_cx.select_all_or_error(self.infcx); let value = self.infcx.resolve_vars_if_possible(value); - if errors.is_empty() { Ok(value) } else { Err(errors) } + if errors.is_empty() { + Ok(value) + } else { + // Drop pending obligations, since deep normalization may happen + // in a loop and we don't want to trigger the assertion on the next + // iteration due to pending ambiguous obligations we've left over. + let _ = fulfill_cx.collect_remaining_errors(self.infcx); + Err(errors) + } } } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0dce504903ca4..99fa791b37550 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -965,36 +965,38 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let tcx = selcx.tcx(); - let lang_items = selcx.tcx().lang_items(); - if [ - lang_items.coroutine_trait(), - lang_items.future_trait(), - lang_items.iterator_trait(), - lang_items.async_iterator_trait(), - lang_items.fn_trait(), - lang_items.fn_mut_trait(), - lang_items.fn_once_trait(), - lang_items.async_fn_trait(), - lang_items.async_fn_mut_trait(), - lang_items.async_fn_once_trait(), - ] - .contains(&Some(trait_ref.def_id)) - { - true - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncFnKindHelper) { - // FIXME(async_closures): Validity constraints here could be cleaned up. - if obligation.predicate.args.type_at(0).is_ty_var() - || obligation.predicate.args.type_at(4).is_ty_var() - || obligation.predicate.args.type_at(5).is_ty_var() - { - candidate_set.mark_ambiguous(); - true - } else { - obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() - && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some() + match selcx.tcx().as_lang_item(trait_ref.def_id) { + Some( + LangItem::Coroutine + | LangItem::Future + | LangItem::Iterator + | LangItem::AsyncIterator + | LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::AsyncFn + | LangItem::AsyncFnMut + | LangItem::AsyncFnOnce, + ) => true, + Some(LangItem::AsyncFnKindHelper) => { + // FIXME(async_closures): Validity constraints here could be cleaned up. + if obligation.predicate.args.type_at(0).is_ty_var() + || obligation.predicate.args.type_at(4).is_ty_var() + || obligation.predicate.args.type_at(5).is_ty_var() + { + candidate_set.mark_ambiguous(); + true + } else { + obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() + && obligation + .predicate + .args + .type_at(1) + .to_opt_closure_kind() + .is_some() + } } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::DiscriminantKind) { - match self_ty.kind() { + Some(LangItem::DiscriminantKind) => match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1031,9 +1033,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Placeholder(..) | ty::Infer(..) | ty::Error(_) => false, - } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) { - match self_ty.kind() { + }, + Some(LangItem::AsyncDestruct) => match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1068,101 +1069,104 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Placeholder(..) | ty::Infer(_) | ty::Error(_) => false, - } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) { - let tail = selcx.tcx().struct_tail_raw( - self_ty, - |ty| { - // We throw away any obligations we get from this, since we normalize - // and confirm these obligations once again during confirmation - normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - ty, - ) - .value - }, - || {}, - ); + }, + Some(LangItem::PointeeTrait) => { + let tail = selcx.tcx().struct_tail_raw( + self_ty, + |ty| { + // We throw away any obligations we get from this, since we normalize + // and confirm these obligations once again during confirmation + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }, + || {}, + ); - match tail.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(_) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - // Extern types have unit metadata, according to RFC 2850 - | ty::Foreign(_) - // If returned by `struct_tail` this is a unit struct - // without any fields, or not a struct, and therefore is Sized. - | ty::Adt(..) - // If returned by `struct_tail` this is the empty tuple. - | ty::Tuple(..) - // Integers and floats are always Sized, and so have unit type metadata. - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) - // This happens if we reach the recursion limit when finding the struct tail. - | ty::Error(..) => true, - - // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. - // Otherwise, type parameters, opaques, and unnormalized projections have - // unit metadata if they're known (e.g. by the param_env) to be sized. - ty::Param(_) | ty::Alias(..) - if self_ty != tail - || selcx.infcx.predicate_must_hold_modulo_regions( - &obligation.with( - selcx.tcx(), - ty::TraitRef::new( + match tail.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Pat(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + // Extern types have unit metadata, according to RFC 2850 + | ty::Foreign(_) + // If returned by `struct_tail` this is a unit struct + // without any fields, or not a struct, and therefore is Sized. + | ty::Adt(..) + // If returned by `struct_tail` this is the empty tuple. + | ty::Tuple(..) + // Integers and floats are always Sized, and so have unit type metadata. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) + // This happens if we reach the recursion limit when finding the struct tail. + | ty::Error(..) => true, + + // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. + // Otherwise, type parameters, opaques, and unnormalized projections have + // unit metadata if they're known (e.g. by the param_env) to be sized. + ty::Param(_) | ty::Alias(..) + if self_ty != tail + || selcx.infcx.predicate_must_hold_modulo_regions( + &obligation.with( selcx.tcx(), - selcx.tcx().require_lang_item( - LangItem::Sized, - Some(obligation.cause.span), + ty::TraitRef::new( + selcx.tcx(), + selcx.tcx().require_lang_item( + LangItem::Sized, + Some(obligation.cause.span), + ), + [self_ty], ), - [self_ty], ), - ), - ) => - { - true - } + ) => + { + true + } - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), - // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? - ty::Param(_) - | ty::Alias(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) => { - if tail.has_infer_types() { - candidate_set.mark_ambiguous(); + // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? + ty::Param(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false } - false } } - } else if tcx.trait_is_auto(trait_ref.def_id) { - tcx.dcx().span_delayed_bug( - tcx.def_span(obligation.predicate.def_id), - "associated types not allowed on auto traits", - ); - false - } else { - bug!("unexpected builtin trait with associated type: {trait_ref:?}") + _ if tcx.trait_is_auto(trait_ref.def_id) => { + tcx.dcx().span_delayed_bug( + tcx.def_span(obligation.predicate.def_id), + "associated types not allowed on auto traits", + ); + false + } + _ => { + bug!("unexpected builtin trait with associated type: {trait_ref:?}") + } } } ImplSource::Param(..) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 4eecde00eaa1e..f059bd0076897 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -117,8 +117,7 @@ fn relate_mir_and_user_args<'tcx>( CRATE_DEF_ID, ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span), ); - let instantiated_predicate = - ocx.normalize(&cause.clone(), param_env, instantiated_predicate); + let instantiated_predicate = ocx.normalize(&cause, param_env, instantiated_predicate); ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); } 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 cf6d2bc151fb0..00101010f14fa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -240,8 +240,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) { continue; } - // FIXME(oli-obk): it is suspicious that we are dropping the constness and - // polarity here. let wc = self.where_clause_may_apply(stack, bound_trait_ref)?; if wc.may_apply() { candidates.vec.push(ParamCandidate(bound)); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4d88a23250a7e..c7ce13c8014f0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1498,7 +1498,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // However, if we disqualify *all* goals from being cached, perf suffers. // This is likely fixed by better caching in general in the new solver. // See: . - TypingMode::Analysis { defining_opaque_types } + TypingMode::Analysis { + defining_opaque_types_and_generators: defining_opaque_types, + } | TypingMode::Borrowck { defining_opaque_types } => { defining_opaque_types.is_empty() || !pred.has_opaque_types() } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 54b6c22b2d821..cad7e42fd8212 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1,3 +1,8 @@ +//! Core logic responsible for determining what it means for various type system +//! primitives to be "well formed". Actually checking whether these primitives are +//! well formed is performed elsewhere (e.g. during type checking or item well formedness +//! checking). + use std::iter; use rustc_hir as hir; @@ -15,12 +20,13 @@ use tracing::{debug, instrument, trace}; use crate::infer::InferCtxt; use crate::traits; + /// Returns the set of obligations needed to make `arg` well-formed. /// If `arg` contains unresolved inference variables, this may include /// further WF obligations. However, if `arg` IS an unresolved /// inference variable, returns `None`, because we are not able to -/// make any progress at all. This is to prevent "livelock" where we -/// say "$0 is WF if $0 is WF". +/// make any progress at all. This is to prevent cycles where we +/// say "?0 is WF if ?0 is WF". pub fn obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -29,14 +35,14 @@ pub fn obligations<'tcx>( arg: GenericArg<'tcx>, span: Span, ) -> Option> { - // Handle the "livelock" case (see comment above) by bailing out if necessary. + // Handle the "cycle" case (see comment above) by bailing out if necessary. let arg = match arg.unpack() { GenericArgKind::Type(ty) => { match ty.kind() { ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); if resolved_ty == ty { - // No progress, bail out to prevent "livelock". + // No progress, bail out to prevent cycles. return None; } else { resolved_ty @@ -51,7 +57,7 @@ pub fn obligations<'tcx>( ty::ConstKind::Infer(_) => { let resolved = infcx.shallow_resolve_const(ct); if resolved == ct { - // No progress. + // No progress, bail out to prevent cycles. return None; } else { resolved @@ -74,7 +80,7 @@ pub fn obligations<'tcx>( recursion_depth, item: None, }; - wf.compute(arg); + wf.add_wf_preds_for_generic_arg(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); let result = wf.normalize(infcx); @@ -97,7 +103,7 @@ pub fn unnormalized_obligations<'tcx>( // However, if `arg` IS an unresolved inference variable, returns `None`, // because we are not able to make any progress at all. This is to prevent - // "livelock" where we say "$0 is WF if $0 is WF". + // cycles where we say "?0 is WF if ?0 is WF". if arg.is_non_region_infer() { return None; } @@ -115,7 +121,7 @@ pub fn unnormalized_obligations<'tcx>( recursion_depth: 0, item: None, }; - wf.compute(arg); + wf.add_wf_preds_for_generic_arg(arg); Some(wf.out) } @@ -140,7 +146,7 @@ pub fn trait_obligations<'tcx>( recursion_depth: 0, item: Some(item), }; - wf.compute_trait_pred(trait_pred, Elaborate::All); + wf.add_wf_preds_for_trait_pred(trait_pred, Elaborate::All); debug!(obligations = ?wf.out); wf.normalize(infcx) } @@ -171,7 +177,7 @@ pub fn clause_obligations<'tcx>( // It's ok to skip the binder here because wf code is prepared for it match clause.kind().skip_binder() { ty::ClauseKind::Trait(t) => { - wf.compute_trait_pred(t, Elaborate::None); + wf.add_wf_preds_for_trait_pred(t, Elaborate::None); } ty::ClauseKind::HostEffect(..) => { // Technically the well-formedness of this predicate is implied by @@ -179,22 +185,22 @@ pub fn clause_obligations<'tcx>( } ty::ClauseKind::RegionOutlives(..) => {} ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { - wf.compute(ty.into()); + wf.add_wf_preds_for_generic_arg(ty.into()); } ty::ClauseKind::Projection(t) => { - wf.compute_alias_term(t.projection_term); - wf.compute(t.term.into_arg()); + wf.add_wf_preds_for_alias_term(t.projection_term); + wf.add_wf_preds_for_generic_arg(t.term.into_arg()); } ty::ClauseKind::ConstArgHasType(ct, ty) => { - wf.compute(ct.into()); - wf.compute(ty.into()); + wf.add_wf_preds_for_generic_arg(ct.into()); + wf.add_wf_preds_for_generic_arg(ty.into()); } ty::ClauseKind::WellFormed(arg) => { - wf.compute(arg); + wf.add_wf_preds_for_generic_arg(arg); } ty::ClauseKind::ConstEvaluatable(ct) => { - wf.compute(ct.into()); + wf.add_wf_preds_for_generic_arg(ct.into()); } } @@ -372,14 +378,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn compute_trait_pred(&mut self, trait_pred: ty::TraitPredicate<'tcx>, elaborate: Elaborate) { + fn add_wf_preds_for_trait_pred( + &mut self, + trait_pred: ty::TraitPredicate<'tcx>, + elaborate: Elaborate, + ) { let tcx = self.tcx(); let trait_ref = trait_pred.trait_ref; // Negative trait predicates don't require supertraits to hold, just // that their args are WF. if trait_pred.polarity == ty::PredicatePolarity::Negative { - self.compute_negative_trait_pred(trait_ref); + self.add_wf_preds_for_negative_trait_pred(trait_ref); return; } @@ -445,15 +455,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Compute the obligations that are required for `trait_ref` to be WF, // given that it is a *negative* trait predicate. - fn compute_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { + fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { for arg in trait_ref.args { - self.compute(arg); + self.add_wf_preds_for_generic_arg(arg); } } /// Pushes the obligations required for an alias (except inherent) to be WF /// into `self.out`. - fn compute_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { + fn add_wf_preds_for_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { // A projection is well-formed if // // (a) its predicates hold (*) @@ -478,13 +488,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(data.def_id, data.args); self.out.extend(obligations); - self.compute_projection_args(data.args); + self.add_wf_preds_for_projection_args(data.args); } /// Pushes the obligations required for an inherent alias to be WF /// into `self.out`. // FIXME(inherent_associated_types): Merge this function with `fn compute_alias`. - fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { // An inherent projection is well-formed if // // (a) its predicates hold (*) @@ -511,7 +521,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { data.args.visit_with(self); } - fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) { + fn add_wf_preds_for_projection_args(&mut self, args: GenericArgsRef<'tcx>) { let tcx = self.tcx(); let cause = self.cause(ObligationCauseCode::WellFormed(None)); let param_env = self.param_env; @@ -557,7 +567,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes all the predicates needed to validate that `ty` is WF into `out`. #[instrument(level = "debug", skip(self))] - fn compute(&mut self, arg: GenericArg<'tcx>) { + fn add_wf_preds_for_generic_arg(&mut self, arg: GenericArg<'tcx>) { arg.visit_with(self); debug!(?self.out); } @@ -596,7 +606,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .collect() } - fn from_object_ty( + fn add_wf_preds_for_dyn_ty( &mut self, ty: Ty<'tcx>, data: &'tcx ty::List>, @@ -651,6 +661,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { outlives, )); } + + // We don't add any wf predicates corresponding to the trait ref's generic arguments + // which allows code like this to compile: + // ```rust + // trait Trait {} + // fn foo(_: &dyn Trait<[u32]>) {} + // ``` } } } @@ -761,7 +778,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.out.extend(obligations); } ty::Alias(ty::Inherent, data) => { - self.compute_inherent_projection(data); + self.add_wf_preds_for_inherent_projection(data); return; // Subtree handled by compute_inherent_projection. } @@ -895,7 +912,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // // Here, we defer WF checking due to higher-ranked // regions. This is perhaps not ideal. - self.from_object_ty(t, data, r); + self.add_wf_preds_for_dyn_ty(t, data, r); // FIXME(#27579) RFC also considers adding trait // obligations that don't refer to Self and @@ -917,11 +934,11 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // 1. Check if they have been resolved, and if so proceed with // THAT type. // 2. If not, we've at least simplified things (e.g., we went - // from `Vec<$0>: WF` to `$0: WF`), so we can + // from `Vec?0>: WF` to `?0: WF`), so we can // register a pending obligation and keep // moving. (Goal is that an "inductive hypothesis" // is satisfied to ensure termination.) - // See also the comment on `fn obligations`, describing "livelock" + // See also the comment on `fn obligations`, describing cycle // prevention, which happens before this can be reached. ty::Infer(_) => { let cause = self.cause(ObligationCauseCode::WellFormed(None)); diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index f0c783b30020e..0250cc0ea0788 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -5,11 +5,13 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +itertools = "0.12" rustc_abi = { path = "../rustc_abi", optional = true } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir", optional = true } rustc_middle = { path = "../rustc_middle", optional = true } rustc_span = { path = "../rustc_span", optional = true } +smallvec = "1.8.1" tracing = "0.1" # tidy-alphabetical-end @@ -20,8 +22,3 @@ rustc = [ "dep:rustc_middle", "dep:rustc_span", ] - -[dev-dependencies] -# tidy-alphabetical-start -itertools = "0.12" -# tidy-alphabetical-end diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index bb909c54d2bc3..d1f58157b696b 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -1,8 +1,9 @@ use std::fmt; +use std::ops::RangeInclusive; use std::sync::atomic::{AtomicU32, Ordering}; use super::{Byte, Ref, Tree, Uninhabited}; -use crate::Map; +use crate::{Map, Set}; #[derive(PartialEq)] #[cfg_attr(test, derive(Clone))] @@ -20,7 +21,7 @@ pub(crate) struct Transitions where R: Ref, { - byte_transitions: Map, + byte_transitions: EdgeSet, ref_transitions: Map, } @@ -29,7 +30,7 @@ where R: Ref, { fn default() -> Self { - Self { byte_transitions: Map::default(), ref_transitions: Map::default() } + Self { byte_transitions: EdgeSet::empty(), ref_transitions: Map::default() } } } @@ -56,15 +57,10 @@ where { #[cfg(test)] pub(crate) fn bool() -> Self { - let mut transitions: Map> = Map::default(); - let start = State::new(); - let accept = State::new(); - - transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x00), accept); - - transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x01), accept); - - Self { transitions, start, accept } + Self::from_transitions(|accept| Transitions { + byte_transitions: EdgeSet::new(Byte::new(0x00..=0x01), accept), + ref_transitions: Map::default(), + }) } pub(crate) fn unit() -> Self { @@ -76,23 +72,24 @@ where } pub(crate) fn from_byte(byte: Byte) -> Self { - let mut transitions: Map> = Map::default(); - let start = State::new(); - let accept = State::new(); - - transitions.entry(start).or_default().byte_transitions.insert(byte, accept); - - Self { transitions, start, accept } + Self::from_transitions(|accept| Transitions { + byte_transitions: EdgeSet::new(byte, accept), + ref_transitions: Map::default(), + }) } pub(crate) fn from_ref(r: R) -> Self { - let mut transitions: Map> = Map::default(); + Self::from_transitions(|accept| Transitions { + byte_transitions: EdgeSet::empty(), + ref_transitions: [(r, accept)].into_iter().collect(), + }) + } + + fn from_transitions(f: impl FnOnce(State) -> Transitions) -> Self { let start = State::new(); let accept = State::new(); - transitions.entry(start).or_default().ref_transitions.insert(r, accept); - - Self { transitions, start, accept } + Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept } } pub(crate) fn from_tree(tree: Tree) -> Result { @@ -132,13 +129,16 @@ where for (source, transition) in other.transitions { let fix_state = |state| if state == other.start { self.accept } else { state }; - let entry = transitions.entry(fix_state(source)).or_default(); - for (edge, destination) in transition.byte_transitions { - entry.byte_transitions.insert(edge, fix_state(destination)); - } - for (edge, destination) in transition.ref_transitions { - entry.ref_transitions.insert(edge, fix_state(destination)); - } + let byte_transitions = transition.byte_transitions.map_states(&fix_state); + let ref_transitions = transition + .ref_transitions + .into_iter() + .map(|(r, state)| (r, fix_state(state))) + .collect(); + + let old = transitions + .insert(fix_state(source), Transitions { byte_transitions, ref_transitions }); + assert!(old.is_none()); } Self { transitions, start, accept } @@ -170,67 +170,111 @@ where let start = mapped((Some(a.start), Some(b.start))); let mut transitions: Map> = Map::default(); - let mut queue = vec![(Some(a.start), Some(b.start))]; let empty_transitions = Transitions::default(); - while let Some((a_src, b_src)) = queue.pop() { + struct WorkQueue { + queue: Vec<(Option, Option)>, + // Track all entries ever enqueued to avoid duplicating work. This + // gives us a guarantee that a given (a_state, b_state) pair will + // only ever be visited once. + enqueued: Set<(Option, Option)>, + } + impl WorkQueue { + fn enqueue(&mut self, a_state: Option, b_state: Option) { + if self.enqueued.insert((a_state, b_state)) { + self.queue.push((a_state, b_state)); + } + } + } + let mut queue = WorkQueue { queue: Vec::new(), enqueued: Set::default() }; + queue.enqueue(Some(a.start), Some(b.start)); + + while let Some((a_src, b_src)) = queue.queue.pop() { + let src = mapped((a_src, b_src)); + if src == accept { + // While it's possible to have a DFA whose accept state has + // out-edges, these do not affect the semantics of the DFA, and + // so there's no point in processing them. Continuing here also + // has the advantage of guaranteeing that we only ever process a + // given node in the output DFA once. In particular, with the + // exception of the accept state, we ensure that we only push a + // given node to the `queue` once. This allows the following + // code to assume that we're processing a node we've never + // processed before, which means we never need to merge two edge + // sets - we only ever need to construct a new edge set from + // whole cloth. + continue; + } + let a_transitions = a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions); let b_transitions = b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions); let byte_transitions = - a_transitions.byte_transitions.keys().chain(b_transitions.byte_transitions.keys()); - - for byte_transition in byte_transitions { - let a_dst = a_transitions.byte_transitions.get(byte_transition).copied(); - let b_dst = b_transitions.byte_transitions.get(byte_transition).copied(); + a_transitions.byte_transitions.union(&b_transitions.byte_transitions); + let byte_transitions = byte_transitions.map_states(|(a_dst, b_dst)| { assert!(a_dst.is_some() || b_dst.is_some()); - let src = mapped((a_src, b_src)); - let dst = mapped((a_dst, b_dst)); - - transitions.entry(src).or_default().byte_transitions.insert(*byte_transition, dst); - - if !transitions.contains_key(&dst) { - queue.push((a_dst, b_dst)) - } - } + queue.enqueue(a_dst, b_dst); + mapped((a_dst, b_dst)) + }); let ref_transitions = a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys()); - for ref_transition in ref_transitions { - let a_dst = a_transitions.ref_transitions.get(ref_transition).copied(); - let b_dst = b_transitions.ref_transitions.get(ref_transition).copied(); + let ref_transitions = ref_transitions + .map(|ref_transition| { + let a_dst = a_transitions.ref_transitions.get(ref_transition).copied(); + let b_dst = b_transitions.ref_transitions.get(ref_transition).copied(); - assert!(a_dst.is_some() || b_dst.is_some()); - - let src = mapped((a_src, b_src)); - let dst = mapped((a_dst, b_dst)); + assert!(a_dst.is_some() || b_dst.is_some()); - transitions.entry(src).or_default().ref_transitions.insert(*ref_transition, dst); + queue.enqueue(a_dst, b_dst); + (*ref_transition, mapped((a_dst, b_dst))) + }) + .collect(); - if !transitions.contains_key(&dst) { - queue.push((a_dst, b_dst)) - } - } + let old = transitions.insert(src, Transitions { byte_transitions, ref_transitions }); + // See `if src == accept { ... }` above. The comment there explains + // why this assert is valid. + assert_eq!(old, None); } Self { transitions, start, accept } } - pub(crate) fn bytes_from(&self, start: State) -> Option<&Map> { - Some(&self.transitions.get(&start)?.byte_transitions) + pub(crate) fn states_from( + &self, + state: State, + src_validity: RangeInclusive, + ) -> impl Iterator { + self.transitions + .get(&state) + .map(move |t| t.byte_transitions.states_from(src_validity)) + .into_iter() + .flatten() + } + + pub(crate) fn get_uninit_edge_dst(&self, state: State) -> Option { + let transitions = self.transitions.get(&state)?; + transitions.byte_transitions.get_uninit_edge_dst() } - pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option { - self.transitions.get(&start)?.byte_transitions.get(&byte).copied() + pub(crate) fn bytes_from(&self, start: State) -> impl Iterator { + self.transitions + .get(&start) + .into_iter() + .flat_map(|transitions| transitions.byte_transitions.iter()) } - pub(crate) fn refs_from(&self, start: State) -> Option<&Map> { - Some(&self.transitions.get(&start)?.ref_transitions) + pub(crate) fn refs_from(&self, start: State) -> impl Iterator { + self.transitions + .get(&start) + .into_iter() + .flat_map(|transitions| transitions.ref_transitions.iter()) + .map(|(r, s)| (*r, *s)) } #[cfg(test)] @@ -241,15 +285,25 @@ where ) -> Self { let start = State(start); let accept = State(accept); - let mut transitions: Map> = Map::default(); + let mut transitions: Map> = Map::default(); - for &(src, edge, dst) in edges { - let src = State(src); - let dst = State(dst); - let old = transitions.entry(src).or_default().byte_transitions.insert(edge.into(), dst); - assert!(old.is_none()); + for (src, edge, dst) in edges.iter().copied() { + transitions.entry(State(src)).or_default().push((edge.into(), State(dst))); } + let transitions = transitions + .into_iter() + .map(|(src, edges)| { + ( + src, + Transitions { + byte_transitions: EdgeSet::from_edges(edges), + ref_transitions: Map::default(), + }, + ) + }) + .collect(); + Self { start, accept, transitions } } } @@ -277,3 +331,242 @@ where writeln!(f, "}}") } } + +use edge_set::EdgeSet; +mod edge_set { + use std::cmp; + + use run::*; + use smallvec::{SmallVec, smallvec}; + + use super::*; + mod run { + use std::ops::{Range, RangeInclusive}; + + use super::*; + use crate::layout::Byte; + + /// A logical set of edges. + /// + /// A `Run` encodes one edge for every byte value in `start..=end` + /// pointing to `dst`. + #[derive(Eq, PartialEq, Copy, Clone, Debug)] + pub(super) struct Run { + // `start` and `end` are both inclusive (ie, closed) bounds, as this + // is required in order to be able to store 0..=255. We provide + // setters and getters which operate on closed/open ranges, which + // are more intuitive and easier for performing offset math. + start: u8, + end: u8, + pub(super) dst: S, + } + + impl Run { + pub(super) fn new(range: RangeInclusive, dst: S) -> Self { + Self { start: *range.start(), end: *range.end(), dst } + } + + pub(super) fn from_inclusive_exclusive(range: Range, dst: S) -> Self { + Self { + start: range.start.try_into().unwrap(), + end: (range.end - 1).try_into().unwrap(), + dst, + } + } + + pub(super) fn contains(&self, idx: u16) -> bool { + idx >= u16::from(self.start) && idx <= u16::from(self.end) + } + + pub(super) fn as_inclusive_exclusive(&self) -> (u16, u16) { + (u16::from(self.start), u16::from(self.end) + 1) + } + + pub(super) fn as_byte(&self) -> Byte { + Byte::new(self.start..=self.end) + } + + pub(super) fn map_state(self, f: impl FnOnce(S) -> SS) -> Run { + let Run { start, end, dst } = self; + Run { start, end, dst: f(dst) } + } + + /// Produces a new `Run` whose lower bound is the greater of + /// `self`'s existing lower bound and `lower_bound`. + pub(super) fn clamp_lower(self, lower_bound: u8) -> Self { + let Run { start, end, dst } = self; + Run { start: cmp::max(start, lower_bound), end, dst } + } + } + } + + /// The set of outbound byte edges associated with a DFA node (not including + /// reference edges). + #[derive(Eq, PartialEq, Clone, Debug)] + pub(super) struct EdgeSet { + // A sequence of runs stored in ascending order. Since the graph is a + // DFA, these must be non-overlapping with one another. + runs: SmallVec<[Run; 1]>, + // The edge labeled with the uninit byte, if any. + // + // FIXME(@joshlf): Make `State` a `NonZero` so that this is NPO'd. + uninit: Option, + } + + impl EdgeSet { + pub(crate) fn new(byte: Byte, dst: S) -> Self { + match byte.range() { + Some(range) => Self { runs: smallvec![Run::new(range, dst)], uninit: None }, + None => Self { runs: SmallVec::new(), uninit: Some(dst) }, + } + } + + pub(crate) fn empty() -> Self { + Self { runs: SmallVec::new(), uninit: None } + } + + #[cfg(test)] + pub(crate) fn from_edges(mut edges: Vec<(Byte, S)>) -> Self + where + S: Ord, + { + edges.sort(); + Self { + runs: edges + .into_iter() + .map(|(byte, state)| Run::new(byte.range().unwrap(), state)) + .collect(), + uninit: None, + } + } + + pub(crate) fn iter(&self) -> impl Iterator + where + S: Copy, + { + self.uninit + .map(|dst| (Byte::uninit(), dst)) + .into_iter() + .chain(self.runs.iter().map(|run| (run.as_byte(), run.dst))) + } + + pub(crate) fn states_from( + &self, + byte: RangeInclusive, + ) -> impl Iterator + where + S: Copy, + { + // FIXME(@joshlf): Optimize this. A manual scan over `self.runs` may + // permit us to more efficiently discard runs which will not be + // produced by this iterator. + self.iter().filter(move |(o, _)| Byte::new(byte.clone()).transmutable_into(&o)) + } + + pub(crate) fn get_uninit_edge_dst(&self) -> Option + where + S: Copy, + { + self.uninit + } + + pub(crate) fn map_states(self, mut f: impl FnMut(S) -> SS) -> EdgeSet { + EdgeSet { + // NOTE: It appears as through ` as + // IntoIterator>::IntoIter` and `std::iter::Map` both implement + // `TrustedLen`, which in turn means that this `.collect()` + // allocates the correct number of elements once up-front [1]. + // + // [1] https://doc.rust-lang.org/1.85.0/src/alloc/vec/spec_from_iter_nested.rs.html#47 + runs: self.runs.into_iter().map(|run| run.map_state(&mut f)).collect(), + uninit: self.uninit.map(f), + } + } + + /// Unions two edge sets together. + /// + /// If `u = a.union(b)`, then for each byte value, `u` will have an edge + /// with that byte value and with the destination `(Some(_), None)`, + /// `(None, Some(_))`, or `(Some(_), Some(_))` depending on whether `a`, + /// `b`, or both have an edge with that byte value. + /// + /// If neither `a` nor `b` have an edge with a particular byte value, + /// then no edge with that value will be present in `u`. + pub(crate) fn union(&self, other: &Self) -> EdgeSet<(Option, Option)> + where + S: Copy, + { + let uninit = match (self.uninit, other.uninit) { + (None, None) => None, + (s, o) => Some((s, o)), + }; + + let mut runs = SmallVec::new(); + + // Iterate over `self.runs` and `other.runs` simultaneously, + // advancing `idx` as we go. At each step, we advance `idx` as far + // as we can without crossing a run boundary in either `self.runs` + // or `other.runs`. + + // INVARIANT: `idx < s[0].end && idx < o[0].end`. + let (mut s, mut o) = (self.runs.as_slice(), other.runs.as_slice()); + let mut idx = 0u16; + while let (Some((s_run, s_rest)), Some((o_run, o_rest))) = + (s.split_first(), o.split_first()) + { + let (s_start, s_end) = s_run.as_inclusive_exclusive(); + let (o_start, o_end) = o_run.as_inclusive_exclusive(); + + // Compute `end` as the end of the current run (which starts + // with `idx`). + let (end, dst) = match (s_run.contains(idx), o_run.contains(idx)) { + // `idx` is in an existing run in both `s` and `o`, so `end` + // is equal to the smallest of the two ends of those runs. + (true, true) => (cmp::min(s_end, o_end), (Some(s_run.dst), Some(o_run.dst))), + // `idx` is in an existing run in `s`, but not in any run in + // `o`. `end` is either the end of the `s` run or the + // beginning of the next `o` run, whichever comes first. + (true, false) => (cmp::min(s_end, o_start), (Some(s_run.dst), None)), + // The inverse of the previous case. + (false, true) => (cmp::min(s_start, o_end), (None, Some(o_run.dst))), + // `idx` is not in a run in either `s` or `o`, so advance it + // to the beginning of the next run. + (false, false) => { + idx = cmp::min(s_start, o_start); + continue; + } + }; + + // FIXME(@joshlf): If this is contiguous with the previous run + // and has the same `dst`, just merge it into that run rather + // than adding a new one. + runs.push(Run::from_inclusive_exclusive(idx..end, dst)); + idx = end; + + if idx >= s_end { + s = s_rest; + } + if idx >= o_end { + o = o_rest; + } + } + + // At this point, either `s` or `o` have been exhausted, so the + // remaining elements in the other slice are guaranteed to be + // non-overlapping. We can add all remaining runs to `runs` with no + // further processing. + if let Ok(idx) = u8::try_from(idx) { + let (slc, map) = if !s.is_empty() { + let map: fn(_) -> _ = |st| (Some(st), None); + (s, map) + } else { + let map: fn(_) -> _ = |st| (None, Some(st)); + (o, map) + }; + runs.extend(slc.iter().map(|run| run.clamp_lower(idx).map_state(map))); + } + + EdgeSet { runs, uninit } + } + } +} diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index c940f7c42a82f..d555ea702a9fe 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Debug}; use std::hash::Hash; +use std::ops::RangeInclusive; pub(crate) mod tree; pub(crate) use tree::Tree; @@ -10,18 +11,56 @@ pub(crate) use dfa::Dfa; #[derive(Debug)] pub(crate) struct Uninhabited; -/// An instance of a byte is either initialized to a particular value, or uninitialized. -#[derive(Hash, Eq, PartialEq, Clone, Copy)] -pub(crate) enum Byte { - Uninit, - Init(u8), +/// A range of byte values, or the uninit byte. +#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub(crate) struct Byte { + // An inclusive-inclusive range. We use this instead of `RangeInclusive` + // because `RangeInclusive: !Copy`. + // + // `None` means uninit. + // + // FIXME(@joshlf): Optimize this representation. Some pairs of values (where + // `lo > hi`) are illegal, and we could use these to represent `None`. + range: Option<(u8, u8)>, +} + +impl Byte { + fn new(range: RangeInclusive) -> Self { + Self { range: Some((*range.start(), *range.end())) } + } + + fn from_val(val: u8) -> Self { + Self { range: Some((val, val)) } + } + + pub(crate) fn uninit() -> Byte { + Byte { range: None } + } + + /// Returns `None` if `self` is the uninit byte. + pub(crate) fn range(&self) -> Option> { + self.range.map(|(lo, hi)| lo..=hi) + } + + /// Are any of the values in `self` transmutable into `other`? + /// + /// Note two special cases: An uninit byte is only transmutable into another + /// uninit byte. Any byte is transmutable into an uninit byte. + pub(crate) fn transmutable_into(&self, other: &Byte) -> bool { + match (self.range, other.range) { + (None, None) => true, + (None, Some(_)) => false, + (Some(_), None) => true, + (Some((slo, shi)), Some((olo, ohi))) => slo <= ohi && olo <= shi, + } + } } impl fmt::Debug for Byte { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self { - Self::Uninit => f.write_str("??u8"), - Self::Init(b) => write!(f, "{b:#04x}u8"), + match self.range { + None => write!(f, "uninit"), + Some((lo, hi)) => write!(f, "{lo}..={hi}"), } } } @@ -29,7 +68,7 @@ impl fmt::Debug for Byte { #[cfg(test)] impl From for Byte { fn from(src: u8) -> Self { - Self::Init(src) + Self::from_val(src) } } @@ -62,6 +101,21 @@ impl Ref for ! { } } +#[cfg(test)] +impl Ref for [(); N] { + fn min_align(&self) -> usize { + N + } + + fn size(&self) -> usize { + N + } + + fn is_mutable(&self) -> bool { + false + } +} + #[cfg(feature = "rustc")] pub mod rustc { use std::fmt::{self, Write}; diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index a21be5dda4ee0..6a09be18ef944 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -54,22 +54,22 @@ where /// A `Tree` containing a single, uninitialized byte. pub(crate) fn uninit() -> Self { - Self::Byte(Byte::Uninit) + Self::Byte(Byte::uninit()) } /// A `Tree` representing the layout of `bool`. pub(crate) fn bool() -> Self { - Self::from_bits(0x00).or(Self::from_bits(0x01)) + Self::Byte(Byte::new(0x00..=0x01)) } /// A `Tree` whose layout matches that of a `u8`. pub(crate) fn u8() -> Self { - Self::Alt((0u8..=255).map(Self::from_bits).collect()) + Self::Byte(Byte::new(0x00..=0xFF)) } /// A `Tree` whose layout accepts exactly the given bit pattern. pub(crate) fn from_bits(bits: u8) -> Self { - Self::Byte(Byte::Init(bits)) + Self::Byte(Byte::from_val(bits)) } /// A `Tree` whose layout is a number of the given width. @@ -514,7 +514,7 @@ pub(crate) mod rustc { } } ty::Tuple(fields) => fields[i.as_usize()], - kind @ _ => unimplemented!( + kind => unimplemented!( "only a subset of `Ty::ty_and_layout_field`'s functionality is implemented. implementation needed for {:?}", kind ), diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 76fa6ceabe7e7..ce18dad55179c 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,8 +1,9 @@ // tidy-alphabetical-start +#![cfg_attr(test, feature(test))] #![feature(never_type)] // tidy-alphabetical-end -pub(crate) use rustc_data_structures::fx::FxIndexMap as Map; +pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; pub mod layout; mod maybe_transmutable; diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index db0e1ab8e986a..0a19cccc2ed03 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -1,10 +1,14 @@ +use std::rc::Rc; +use std::{cmp, iter}; + +use itertools::Either; use tracing::{debug, instrument, trace}; pub(crate) mod query_context; #[cfg(test)] mod tests; -use crate::layout::{self, Byte, Def, Dfa, Ref, Tree, Uninhabited, dfa}; +use crate::layout::{self, Byte, Def, Dfa, Ref, Tree, dfa}; use crate::maybe_transmutable::query_context::QueryContext; use crate::{Answer, Condition, Map, Reason}; @@ -111,7 +115,7 @@ where // the `src` type do not exist. let src = match Dfa::from_tree(src) { Ok(src) => src, - Err(Uninhabited) => return Answer::Yes, + Err(layout::Uninhabited) => return Answer::Yes, }; // Convert `dst` from a tree-based representation to an DFA-based @@ -122,7 +126,7 @@ where // free of safety invariants. let dst = match Dfa::from_tree(dst) { Ok(dst) => dst, - Err(Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants), + Err(layout::Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants), }; MaybeTransmutableQuery { src, dst, assume, context }.answer() @@ -174,8 +178,8 @@ where // are able to safely transmute, even with truncation. Answer::Yes } else if src_state == self.src.accept { - // extension: `size_of(Src) >= size_of(Dst)` - if let Some(dst_state_prime) = self.dst.byte_from(dst_state, Byte::Uninit) { + // extension: `size_of(Src) <= size_of(Dst)` + if let Some(dst_state_prime) = self.dst.get_uninit_edge_dst(dst_state) { self.answer_memo(cache, src_state, dst_state_prime) } else { Answer::No(Reason::DstIsTooBig) @@ -193,26 +197,120 @@ where Quantifier::ForAll }; + let c = &core::cell::RefCell::new(&mut *cache); let bytes_answer = src_quantifier.apply( - // for each of the byte transitions out of the `src_state`... - self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map( - |(&src_validity, &src_state_prime)| { - // ...try to find a matching transition out of `dst_state`. - if let Some(dst_state_prime) = - self.dst.byte_from(dst_state, src_validity) - { - self.answer_memo(cache, src_state_prime, dst_state_prime) - } else if let Some(dst_state_prime) = - // otherwise, see if `dst_state` has any outgoing `Uninit` transitions - // (any init byte is a valid uninit byte) - self.dst.byte_from(dst_state, Byte::Uninit) - { - self.answer_memo(cache, src_state_prime, dst_state_prime) - } else { - // otherwise, we've exhausted our options. - // the DFAs, from this point onwards, are bit-incompatible. - Answer::No(Reason::DstIsBitIncompatible) + // for each of the byte set transitions out of the `src_state`... + self.src.bytes_from(src_state).flat_map( + move |(src_validity, src_state_prime)| { + // ...find all matching transitions out of `dst_state`. + + let Some(src_validity) = src_validity.range() else { + // NOTE: We construct an iterator here rather + // than just computing the value directly (via + // `self.answer_memo`) so that, if the iterator + // we produce from this branch is + // short-circuited, we don't waste time + // computing `self.answer_memo` unnecessarily. + // That will specifically happen if + // `src_quantifier == Quantifier::ThereExists`, + // since we emit `Answer::Yes` first (before + // chaining `answer_iter`). + let answer_iter = if let Some(dst_state_prime) = + self.dst.get_uninit_edge_dst(dst_state) + { + Either::Left(iter::once_with(move || { + let mut c = c.borrow_mut(); + self.answer_memo(&mut *c, src_state_prime, dst_state_prime) + })) + } else { + Either::Right(iter::once(Answer::No( + Reason::DstIsBitIncompatible, + ))) + }; + + // When `answer == Answer::No(...)`, there are + // two cases to consider: + // - If `assume.validity`, then we should + // succeed because the user is responsible for + // ensuring that the *specific* byte value + // appearing at runtime is valid for the + // destination type. When `assume.validity`, + // `src_quantifier == + // Quantifier::ThereExists`, so adding an + // `Answer::Yes` has the effect of ensuring + // that the "there exists" is always + // satisfied. + // - If `!assume.validity`, then we should fail. + // In this case, `src_quantifier == + // Quantifier::ForAll`, so adding an + // `Answer::Yes` has no effect. + return Either::Left(iter::once(Answer::Yes).chain(answer_iter)); + }; + + #[derive(Copy, Clone, Debug)] + struct Accum { + // The number of matching byte edges that we + // have found in the destination so far. + sum: usize, + found_uninit: bool, } + + let accum1 = Rc::new(std::cell::Cell::new(Accum { + sum: 0, + found_uninit: false, + })); + let accum2 = Rc::clone(&accum1); + let sv = src_validity.clone(); + let update_accum = move |mut accum: Accum, dst_validity: Byte| { + if let Some(dst_validity) = dst_validity.range() { + // Only add the part of `dst_validity` that + // overlaps with `src_validity`. + let start = cmp::max(*sv.start(), *dst_validity.start()); + let end = cmp::min(*sv.end(), *dst_validity.end()); + + // We add 1 here to account for the fact + // that `end` is an inclusive bound. + accum.sum += 1 + usize::from(end.saturating_sub(start)); + } else { + accum.found_uninit = true; + } + accum + }; + + let answers = self + .dst + .states_from(dst_state, src_validity.clone()) + .map(move |(dst_validity, dst_state_prime)| { + let mut c = c.borrow_mut(); + accum1.set(update_accum(accum1.get(), dst_validity)); + let answer = + self.answer_memo(&mut *c, src_state_prime, dst_state_prime); + answer + }) + .chain( + iter::once_with(move || { + let src_validity_len = usize::from(*src_validity.end()) + - usize::from(*src_validity.start()) + + 1; + let accum = accum2.get(); + + // If this condition is false, then + // there are some byte values in the + // source which have no corresponding + // transition in the destination DFA. In + // that case, we add a `No` to our list + // of answers. When + // `!self.assume.validity`, this will + // cause the query to fail. + if accum.found_uninit || accum.sum == src_validity_len { + None + } else { + Some(Answer::No(Reason::DstIsBitIncompatible)) + } + }) + .flatten(), + ); + Either::Right(answers) }, ), ); @@ -235,48 +333,38 @@ where let refs_answer = src_quantifier.apply( // for each reference transition out of `src_state`... - self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map( - |(&src_ref, &src_state_prime)| { - // ...there exists a reference transition out of `dst_state`... - Quantifier::ThereExists.apply( - self.dst - .refs_from(dst_state) - .unwrap_or(&Map::default()) - .into_iter() - .map(|(&dst_ref, &dst_state_prime)| { - if !src_ref.is_mutable() && dst_ref.is_mutable() { - Answer::No(Reason::DstIsMoreUnique) - } else if !self.assume.alignment - && src_ref.min_align() < dst_ref.min_align() - { - Answer::No(Reason::DstHasStricterAlignment { - src_min_align: src_ref.min_align(), - dst_min_align: dst_ref.min_align(), - }) - } else if dst_ref.size() > src_ref.size() { - Answer::No(Reason::DstRefIsTooBig { - src: src_ref, - dst: dst_ref, - }) - } else { - // ...such that `src` is transmutable into `dst`, if - // `src_ref` is transmutability into `dst_ref`. - and( - Answer::If(Condition::IfTransmutable { - src: src_ref, - dst: dst_ref, - }), - self.answer_memo( - cache, - src_state_prime, - dst_state_prime, - ), - ) - } - }), - ) - }, - ), + self.src.refs_from(src_state).map(|(src_ref, src_state_prime)| { + // ...there exists a reference transition out of `dst_state`... + Quantifier::ThereExists.apply(self.dst.refs_from(dst_state).map( + |(dst_ref, dst_state_prime)| { + if !src_ref.is_mutable() && dst_ref.is_mutable() { + Answer::No(Reason::DstIsMoreUnique) + } else if !self.assume.alignment + && src_ref.min_align() < dst_ref.min_align() + { + Answer::No(Reason::DstHasStricterAlignment { + src_min_align: src_ref.min_align(), + dst_min_align: dst_ref.min_align(), + }) + } else if dst_ref.size() > src_ref.size() { + Answer::No(Reason::DstRefIsTooBig { + src: src_ref, + dst: dst_ref, + }) + } else { + // ...such that `src` is transmutable into `dst`, if + // `src_ref` is transmutability into `dst_ref`. + and( + Answer::If(Condition::IfTransmutable { + src: src_ref, + dst: dst_ref, + }), + self.answer_memo(cache, src_state_prime, dst_state_prime), + ) + } + }, + )) + }), ); if self.assume.validity { diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index f8b59bdf32684..214da101be375 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -8,9 +8,17 @@ pub(crate) trait QueryContext { #[cfg(test)] pub(crate) mod test { + use std::marker::PhantomData; + use super::QueryContext; - pub(crate) struct UltraMinimal; + pub(crate) struct UltraMinimal(PhantomData); + + impl Default for UltraMinimal { + fn default() -> Self { + Self(PhantomData) + } + } #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub(crate) enum Def { @@ -24,9 +32,9 @@ pub(crate) mod test { } } - impl QueryContext for UltraMinimal { + impl QueryContext for UltraMinimal { type Def = Def; - type Ref = !; + type Ref = R; } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index cc6a4dce17b63..24e2a1acadd68 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -1,3 +1,5 @@ +extern crate test; + use itertools::Itertools; use super::query_context::test::{Def, UltraMinimal}; @@ -12,15 +14,25 @@ trait Representation { impl Representation for Tree { fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { - crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, UltraMinimal) - .answer() + crate::maybe_transmutable::MaybeTransmutableQuery::new( + src, + dst, + assume, + UltraMinimal::default(), + ) + .answer() } } impl Representation for Dfa { fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { - crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, UltraMinimal) - .answer() + crate::maybe_transmutable::MaybeTransmutableQuery::new( + src, + dst, + assume, + UltraMinimal::default(), + ) + .answer() } } @@ -89,6 +101,36 @@ mod safety { } } +mod size { + use super::*; + + #[test] + fn size() { + let small = Tree::number(1); + let large = Tree::number(2); + + for alignment in [false, true] { + for lifetimes in [false, true] { + for safety in [false, true] { + for validity in [false, true] { + let assume = Assume { alignment, lifetimes, safety, validity }; + assert_eq!( + is_transmutable(&small, &large, assume), + Answer::No(Reason::DstIsTooBig), + "assume: {assume:?}" + ); + assert_eq!( + is_transmutable(&large, &small, assume), + Answer::Yes, + "assume: {assume:?}" + ); + } + } + } + } + } +} + mod bool { use super::*; @@ -112,6 +154,27 @@ mod bool { ); } + #[test] + fn transmute_u8() { + let bool = &Tree::bool(); + let u8 = &Tree::u8(); + for (src, dst, assume_validity, answer) in [ + (bool, u8, false, Answer::Yes), + (bool, u8, true, Answer::Yes), + (u8, bool, false, Answer::No(Reason::DstIsBitIncompatible)), + (u8, bool, true, Answer::Yes), + ] { + assert_eq!( + is_transmutable( + src, + dst, + Assume { validity: assume_validity, ..Assume::default() } + ), + answer + ); + } + } + #[test] fn should_permit_validity_expansion_and_reject_contraction() { let b0 = layout::Tree::::from_bits(0); @@ -175,6 +238,62 @@ mod bool { } } +mod uninit { + use super::*; + + #[test] + fn size() { + let mu = Tree::uninit(); + let u8 = Tree::u8(); + + for alignment in [false, true] { + for lifetimes in [false, true] { + for safety in [false, true] { + for validity in [false, true] { + let assume = Assume { alignment, lifetimes, safety, validity }; + + let want = if validity { + Answer::Yes + } else { + Answer::No(Reason::DstIsBitIncompatible) + }; + + assert_eq!(is_transmutable(&mu, &u8, assume), want, "assume: {assume:?}"); + assert_eq!( + is_transmutable(&u8, &mu, assume), + Answer::Yes, + "assume: {assume:?}" + ); + } + } + } + } + } +} + +mod alt { + use super::*; + use crate::Answer; + + #[test] + fn should_permit_identity_transmutation() { + type Tree = layout::Tree; + + let x = Tree::Seq(vec![Tree::from_bits(0), Tree::from_bits(0)]); + let y = Tree::Seq(vec![Tree::bool(), Tree::from_bits(1)]); + let layout = Tree::Alt(vec![x, y]); + + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + layout.clone(), + layout.clone(), + crate::Assume::default(), + UltraMinimal::default(), + ) + .answer(); + assert_eq!(answer, Answer::Yes, "layout:{:#?}", layout); + } +} + mod union { use super::*; @@ -203,3 +322,59 @@ mod union { assert_eq!(is_transmutable(&t, &u, Assume::default()), Answer::Yes); } } + +mod r#ref { + use super::*; + + #[test] + fn should_permit_identity_transmutation() { + type Tree = crate::layout::Tree; + + let layout = Tree::Seq(vec![Tree::from_bits(0), Tree::Ref([()])]); + + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + layout.clone(), + layout, + Assume::default(), + UltraMinimal::default(), + ) + .answer(); + assert_eq!(answer, Answer::If(crate::Condition::IfTransmutable { src: [()], dst: [()] })); + } +} + +mod benches { + use std::hint::black_box; + + use test::Bencher; + + use super::*; + + #[bench] + fn bench_dfa_from_tree(b: &mut Bencher) { + let num = Tree::number(8).prune(&|_| false); + let num = black_box(num); + + b.iter(|| { + let _ = black_box(Dfa::from_tree(num.clone())); + }) + } + + #[bench] + fn bench_transmute(b: &mut Bencher) { + let num = Tree::number(8).prune(&|_| false); + let dfa = black_box(Dfa::from_tree(num).unwrap()); + + b.iter(|| { + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + dfa.clone(), + dfa.clone(), + Assume::default(), + UltraMinimal::default(), + ) + .answer(); + let answer = std::hint::black_box(answer); + assert_eq!(answer, Answer::Yes); + }) + } +} diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 3d4ab33240af2..63ea035bd0e8e 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -347,7 +347,8 @@ fn adjust_for_rust_scalar<'tcx>( None }; if let Some(kind) = kind { - attrs.pointee_align = Some(pointee.align); + attrs.pointee_align = + Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment())); // `Box` are not necessarily dereferenceable for the entire duration of the function as // they can be deallocated at any time. Same for non-frozen shared references (see diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 35cc6f3985652..ea0f6b8dfba72 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -13,7 +14,6 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iterator_try_collect)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] // tidy-alphabetical-end @@ -32,6 +32,7 @@ mod needs_drop; mod opaque_types; mod representability; pub mod sig_types; +mod stalled_generators; mod structural_match; mod ty; @@ -50,4 +51,5 @@ pub fn provide(providers: &mut Providers) { ty::provide(providers); instance::provide(providers); structural_match::provide(providers); + stalled_generators::provide(providers); } diff --git a/compiler/rustc_ty_utils/src/stalled_generators.rs b/compiler/rustc_ty_utils/src/stalled_generators.rs new file mode 100644 index 0000000000000..8b45e8b0f6f9a --- /dev/null +++ b/compiler/rustc_ty_utils/src/stalled_generators.rs @@ -0,0 +1,54 @@ +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit; +use rustc_hir::intravisit::Visitor; +use rustc_middle::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; + +fn stalled_generators_within<'tcx>( + tcx: TyCtxt<'tcx>, + item: LocalDefId, +) -> &'tcx ty::List { + if !tcx.next_trait_solver_globally() { + return ty::List::empty(); + } + + let body = tcx.hir_body_owned_by(item); + let mut collector = + StalledGeneratorVisitor { tcx, root_def_id: item.to_def_id(), stalled_coroutines: vec![] }; + collector.visit_body(body); + tcx.mk_local_def_ids(&collector.stalled_coroutines) +} + +struct StalledGeneratorVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + root_def_id: DefId, + stalled_coroutines: Vec, +} + +impl<'tcx> Visitor<'tcx> for StalledGeneratorVisitor<'tcx> { + fn visit_nested_body(&mut self, id: hir::BodyId) { + if self.tcx.typeck_root_def_id(self.tcx.hir_body_owner_def_id(id).to_def_id()) + == self.root_def_id + { + let body = self.tcx.hir_body(id); + self.visit_body(body); + } + } + + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Closure(&hir::Closure { + def_id, + kind: hir::ClosureKind::Coroutine(_), + .. + }) = ex.kind + { + self.stalled_coroutines.push(def_id); + } + intravisit::walk_expr(self, ex); + } +} + +pub(super) fn provide(providers: &mut Providers) { + *providers = Providers { stalled_generators_within, ..*providers }; +} diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index fec6e24e2cb4d..8fa56c3599963 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -65,7 +65,7 @@ pub enum TypingMode { /// let x: <() as Assoc>::Output = true; /// } /// ``` - Analysis { defining_opaque_types: I::DefiningOpaqueTypes }, + Analysis { defining_opaque_types_and_generators: I::LocalDefIds }, /// The behavior during MIR borrowck is identical to `TypingMode::Analysis` /// except that the initial value for opaque types is the type computed during /// HIR typeck with unique unconstrained region inference variables. @@ -73,13 +73,13 @@ pub enum TypingMode { /// This is currently only used with by the new solver as it results in new /// non-universal defining uses of opaque types, which is a breaking change. /// See tests/ui/impl-trait/non-defining-use/as-projection-term.rs. - Borrowck { defining_opaque_types: I::DefiningOpaqueTypes }, + Borrowck { defining_opaque_types: I::LocalDefIds }, /// Any analysis after borrowck for a given body should be able to use all the /// hidden types defined by borrowck, without being able to define any new ones. /// /// This is currently only used by the new solver, but should be implemented in /// the old solver as well. - PostBorrowckAnalysis { defined_opaque_types: I::DefiningOpaqueTypes }, + PostBorrowckAnalysis { defined_opaque_types: I::LocalDefIds }, /// After analysis, mostly during codegen and MIR optimizations, we're able to /// reveal all opaque types. As the concrete type should *never* be observable /// directly by the user, this should not be used by checks which may expose @@ -94,13 +94,25 @@ pub enum TypingMode { impl TypingMode { /// Analysis outside of a body does not define any opaque types. pub fn non_body_analysis() -> TypingMode { - TypingMode::Analysis { defining_opaque_types: Default::default() } + TypingMode::Analysis { defining_opaque_types_and_generators: Default::default() } + } + + pub fn typeck_for_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode { + TypingMode::Analysis { + defining_opaque_types_and_generators: cx + .opaque_types_and_generators_defined_by(body_def_id), + } } /// While typechecking a body, we need to be able to define the opaque /// types defined by that body. + /// + /// FIXME: This will be removed because it's generally not correct to define + /// opaques outside of HIR typeck. pub fn analysis_in_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode { - TypingMode::Analysis { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) } + TypingMode::Analysis { + defining_opaque_types_and_generators: cx.opaque_types_defined_by(body_def_id), + } } pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> TypingMode { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 417803e75ead6..9b066b6869f5e 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -442,6 +442,14 @@ pub trait Predicate>: { fn as_clause(self) -> Option; + fn as_normalizes_to(self) -> Option>> { + let kind = self.kind(); + match kind.skip_binder() { + ty::PredicateKind::NormalizesTo(pred) => Some(kind.rebind(pred)), + _ => None, + } + } + // FIXME: Eventually uplift the impl out of rustc and make this defaulted. fn allow_normalization(self) -> bool; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 71bfeabfda878..9758cecaf6ac7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -56,7 +56,7 @@ pub trait Interner: data: PredefinedOpaquesData, ) -> Self::PredefinedOpaques; - type DefiningOpaqueTypes: Copy + type LocalDefIds: Copy + Debug + Hash + Default @@ -286,6 +286,8 @@ pub trait Interner: fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool; + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool; fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder>; @@ -330,10 +332,12 @@ pub trait Interner: binder: ty::Binder, ) -> ty::Binder; - fn opaque_types_defined_by( + fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds; + + fn opaque_types_and_generators_defined_by( self, defining_anchor: Self::LocalDefId, - ) -> Self::DefiningOpaqueTypes; + ) -> Self::LocalDefIds; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/library/Cargo.lock b/library/Cargo.lock index e4f1c4ec96a3a..f7f09a11f3ac9 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.155" +version = "0.1.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341e0830ca6170a4fcf02e92e57daf4b6f10142d48da32a547023867a6c8b35e" +checksum = "c1ffbd2789fe5bb95b96a2e22cbe3128239dc46ff0374e0d38e9f102062d7055" dependencies = [ "cc", "rustc-std-workspace-core", @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index cedbd330cbde8..994221de86635 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -16,7 +16,7 @@ bench = false [dependencies] core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.155", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.156", features = ['rustc-dep-of-std'] } [features] compiler-builtins-mem = ['compiler_builtins/mem'] diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 041f80c1f2c52..7ad9e59dfede1 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -139,7 +139,7 @@ pub struct Iter<'a, T: 'a> { #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Iter").field(&self.iter.clone()).finish() + f.debug_tuple("Iter").field(&self.iter).finish() } } diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index ef8548d24293d..d5b19470e318b 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -574,7 +574,7 @@ impl CString { #[stable(feature = "as_c_str", since = "1.20.0")] #[rustc_diagnostic_item = "cstring_as_c_str"] pub fn as_c_str(&self) -> &CStr { - &*self + unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } } /// Converts this `CString` into a boxed [`CStr`]. @@ -705,14 +705,14 @@ impl ops::Deref for CString { #[inline] fn deref(&self) -> &CStr { - unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } + self.as_c_str() } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for CString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + fmt::Debug::fmt(self.as_c_str(), f) } } diff --git a/library/alloctests/tests/c_str2.rs b/library/alloctests/tests/c_str2.rs index 0f4c27fa12322..fe7686bd1c592 100644 --- a/library/alloctests/tests/c_str2.rs +++ b/library/alloctests/tests/c_str2.rs @@ -33,12 +33,6 @@ fn build_with_zero2() { assert!(CString::new(vec![0]).is_err()); } -#[test] -fn formatted() { - let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap(); - assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); -} - #[test] fn borrowed() { unsafe { diff --git a/library/alloctests/tests/fmt.rs b/library/alloctests/tests/fmt.rs index c13074c53b73d..a20e8c623360f 100644 --- a/library/alloctests/tests/fmt.rs +++ b/library/alloctests/tests/fmt.rs @@ -1,6 +1,7 @@ #![deny(warnings)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] +#![cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] use std::cell::RefCell; use std::fmt::{self, Write}; diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 10f2a11d558be..7aa3f3c6d7434 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -772,8 +772,8 @@ impl hash::Hash for TypeId { // (especially given the previous point about the lower 64 bits being // high quality on their own). // - It is correct to do so -- only hashing a subset of `self` is still - // with an `Eq` implementation that considers the entire value, as - // ours does. + // compatible with an `Eq` implementation that considers the entire + // value, as ours does. self.t.1.hash(state); } } diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 81d828a971c80..f19fde2b4c733 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -32,7 +32,7 @@ pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) { /// /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html /// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html -#[unstable(feature = "naked_functions", issue = "90957")] +#[stable(feature = "naked_functions", since = "CURRENT_RUSTC_VERSION")] #[rustc_builtin_macro] pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs index c8d0c701ba8b7..13127d645a257 100644 --- a/library/core/src/bstr/mod.rs +++ b/library/core/src/bstr/mod.rs @@ -36,8 +36,7 @@ use crate::ops::{Deref, DerefMut, DerefPure}; /// presented as hex escape sequences. /// /// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a -/// `str`, with invalid UTF-8 presented as the Unicode replacement character: � -/// +/// `str`, with invalid UTF-8 presented as the Unicode replacement character (�). #[unstable(feature = "bstr", issue = "134915")] #[repr(transparent)] #[doc(alias = "BStr")] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 17231df731d13..c7657350a0d9a 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -549,8 +549,6 @@ impl Cell { /// # Examples /// /// ``` - /// #![feature(cell_update)] - /// /// use std::cell::Cell; /// /// let c = Cell::new(5); @@ -558,7 +556,7 @@ impl Cell { /// assert_eq!(c.get(), 6); /// ``` #[inline] - #[unstable(feature = "cell_update", issue = "50186")] + #[stable(feature = "cell_update", since = "CURRENT_RUSTC_VERSION")] pub fn update(&self, f: impl FnOnce(T) -> T) { let old = self.get(); self.set(f(old)); diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index ac808038f8900..d820965a7463e 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -21,6 +21,7 @@ pub(super) const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. #[inline] #[must_use] +#[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { @@ -221,6 +222,7 @@ impl FromStr for char { } #[inline] +#[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] const fn char_try_from_u32(i: u32) -> Result { // This is an optimized version of the check // (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF), diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 080c0cef53304..85e87445a1b43 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -150,7 +150,6 @@ impl Error for FromBytesWithNulError { /// within the slice. /// /// This error is created by the [`CStr::from_bytes_until_nul`] method. -/// #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub struct FromBytesUntilNulError(()); diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a01efb2adebe3..a7563f918a241 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1497,6 +1497,7 @@ pub const fn forget(_: T); /// Turning raw bytes (`[u8; SZ]`) into `u32`, `f64`, etc.: /// /// ``` +/// # #![cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; /// /// let num = unsafe { @@ -2429,35 +2430,35 @@ pub unsafe fn float_to_int_unchecked(value: Float) -> In /// Stabilized as [`f16::algebraic_add`], [`f32::algebraic_add`], [`f64::algebraic_add`] and [`f128::algebraic_add`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fadd_algebraic(a: T, b: T) -> T; +pub const fn fadd_algebraic(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_sub`], [`f32::algebraic_sub`], [`f64::algebraic_sub`] and [`f128::algebraic_sub`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fsub_algebraic(a: T, b: T) -> T; +pub const fn fsub_algebraic(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_mul`], [`f32::algebraic_mul`], [`f64::algebraic_mul`] and [`f128::algebraic_mul`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fmul_algebraic(a: T, b: T) -> T; +pub const fn fmul_algebraic(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_div`], [`f32::algebraic_div`], [`f64::algebraic_div`] and [`f128::algebraic_div`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fdiv_algebraic(a: T, b: T) -> T; +pub const fn fdiv_algebraic(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_rem`], [`f32::algebraic_rem`], [`f64::algebraic_rem`] and [`f128::algebraic_rem`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn frem_algebraic(a: T, b: T) -> T; +pub const fn frem_algebraic(a: T, b: T) -> T; /// Returns the number of bits set in an integer type `T` /// diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 330b409876453..16c0c11804049 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1138,6 +1138,10 @@ pub(crate) mod builtin { issue = "29599", reason = "`concat_idents` is not stable enough for use and is subject to change" )] + #[deprecated( + since = "1.88.0", + note = "use `${concat(...)}` with the `macro_metavar_expr_concat` feature instead" + )] #[rustc_builtin_macro] #[macro_export] macro_rules! concat_idents { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d3d1eebc22753..b1119d4899bab 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -145,6 +145,9 @@ impl f128 { pub const RADIX: u32 = 2; /// Number of significant digits in base 2. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[unstable(feature = "f128", issue = "116909")] pub const MANTISSA_DIGITS: u32 = 113; @@ -194,16 +197,22 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const MAX: f128 = 1.18973149535723176508575932662800702e+4932_f128; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[unstable(feature = "f128", issue = "116909")] pub const MIN_EXP: i32 = -16_381; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[unstable(feature = "f128", issue = "116909")] pub const MAX_EXP: i32 = 16_384; @@ -801,7 +810,7 @@ impl f128 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -818,6 +827,7 @@ impl f128 { /// # } /// ``` #[inline] + #[doc(alias = "average")] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn midpoint(self, other: f128) -> f128 { @@ -900,6 +910,7 @@ impl f128 { #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn to_bits(self) -> u128 { // SAFETY: `u128` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -947,6 +958,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u128) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u128` is a plain old datatype so we can always transmute from it. @@ -1370,8 +1382,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f128) -> f128 { + pub const fn algebraic_add(self, rhs: f128) -> f128 { intrinsics::fadd_algebraic(self, rhs) } @@ -1380,8 +1393,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f128) -> f128 { + pub const fn algebraic_sub(self, rhs: f128) -> f128 { intrinsics::fsub_algebraic(self, rhs) } @@ -1390,8 +1404,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f128) -> f128 { + pub const fn algebraic_mul(self, rhs: f128) -> f128 { intrinsics::fmul_algebraic(self, rhs) } @@ -1400,8 +1415,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f128) -> f128 { + pub const fn algebraic_div(self, rhs: f128) -> f128 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1410,8 +1426,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f128) -> f128 { + pub const fn algebraic_rem(self, rhs: f128) -> f128 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index dceb30177e668..54e38d9e1a6f1 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -140,6 +140,9 @@ impl f16 { pub const RADIX: u32 = 2; /// Number of significant digits in base 2. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[unstable(feature = "f16", issue = "116909")] pub const MANTISSA_DIGITS: u32 = 11; @@ -189,16 +192,22 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const MAX: f16 = 6.5504e+4_f16; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[unstable(feature = "f16", issue = "116909")] pub const MIN_EXP: i32 = -13; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[unstable(feature = "f16", issue = "116909")] pub const MAX_EXP: i32 = 16; @@ -789,7 +798,7 @@ impl f16 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -805,6 +814,7 @@ impl f16 { /// # } /// ``` #[inline] + #[doc(alias = "average")] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn midpoint(self, other: f16) -> f16 { @@ -888,6 +898,7 @@ impl f16 { #[inline] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn to_bits(self) -> u16 { // SAFETY: `u16` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -934,6 +945,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u16) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u16` is a plain old datatype so we can always transmute from it. @@ -1346,8 +1358,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f16) -> f16 { + pub const fn algebraic_add(self, rhs: f16) -> f16 { intrinsics::fadd_algebraic(self, rhs) } @@ -1356,8 +1369,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f16) -> f16 { + pub const fn algebraic_sub(self, rhs: f16) -> f16 { intrinsics::fsub_algebraic(self, rhs) } @@ -1366,8 +1380,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f16) -> f16 { + pub const fn algebraic_mul(self, rhs: f16) -> f16 { intrinsics::fmul_algebraic(self, rhs) } @@ -1376,8 +1391,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f16) -> f16 { + pub const fn algebraic_div(self, rhs: f16) -> f16 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1386,8 +1402,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f16) -> f16 { + pub const fn algebraic_rem(self, rhs: f16) -> f16 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index c97dbfb63ae17..e66fd3bb52b86 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -390,6 +390,9 @@ impl f32 { pub const RADIX: u32 = 2; /// Number of significant digits in base 2. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MANTISSA_DIGITS: u32 = 24; @@ -440,16 +443,22 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX: f32 = 3.40282347e+38_f32; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MIN_EXP: i32 = -125; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX_EXP: i32 = 128; @@ -707,8 +716,7 @@ impl f32 { pub const fn is_sign_negative(self) -> bool { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & 0x8000_0000 != 0 } + self.to_bits() & 0x8000_0000 != 0 } /// Returns the least number greater than `self`. @@ -983,7 +991,7 @@ impl f32 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -995,6 +1003,7 @@ impl f32 { /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] + #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f32) -> f32 { @@ -1093,6 +1102,7 @@ impl f32 { #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[inline] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn to_bits(self) -> u32 { // SAFETY: `u32` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -1138,6 +1148,7 @@ impl f32 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u32) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u32` is a plain old datatype so we can always transmute from it. @@ -1512,8 +1523,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f32) -> f32 { + pub const fn algebraic_add(self, rhs: f32) -> f32 { intrinsics::fadd_algebraic(self, rhs) } @@ -1522,8 +1534,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f32) -> f32 { + pub const fn algebraic_sub(self, rhs: f32) -> f32 { intrinsics::fsub_algebraic(self, rhs) } @@ -1532,8 +1545,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f32) -> f32 { + pub const fn algebraic_mul(self, rhs: f32) -> f32 { intrinsics::fmul_algebraic(self, rhs) } @@ -1542,8 +1556,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f32) -> f32 { + pub const fn algebraic_div(self, rhs: f32) -> f32 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1552,8 +1567,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f32) -> f32 { + pub const fn algebraic_rem(self, rhs: f32) -> f32 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 91affdb3794b0..2d791437b2825 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -390,6 +390,9 @@ impl f64 { pub const RADIX: u32 = 2; /// Number of significant digits in base 2. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MANTISSA_DIGITS: u32 = 53; /// Approximate number of significant digits in base 10. @@ -439,16 +442,22 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX: f64 = 1.7976931348623157e+308_f64; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MIN_EXP: i32 = -1021; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX_EXP: i32 = 1024; @@ -715,8 +724,7 @@ impl f64 { pub const fn is_sign_negative(self) -> bool { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & Self::SIGN_MASK != 0 } + self.to_bits() & Self::SIGN_MASK != 0 } #[must_use] @@ -1001,7 +1009,7 @@ impl f64 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -1013,6 +1021,7 @@ impl f64 { /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] + #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f64) -> f64 { @@ -1091,6 +1100,7 @@ impl f64 { without modifying the original"] #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] #[inline] pub const fn to_bits(self) -> u64 { // SAFETY: `u64` is a plain old datatype so we can always transmute to it. @@ -1137,6 +1147,7 @@ impl f64 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u64) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u64` is a plain old datatype so we can always transmute from it. @@ -1511,8 +1522,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f64) -> f64 { + pub const fn algebraic_add(self, rhs: f64) -> f64 { intrinsics::fadd_algebraic(self, rhs) } @@ -1521,8 +1533,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f64) -> f64 { + pub const fn algebraic_sub(self, rhs: f64) -> f64 { intrinsics::fsub_algebraic(self, rhs) } @@ -1531,8 +1544,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f64) -> f64 { + pub const fn algebraic_mul(self, rhs: f64) -> f64 { intrinsics::fmul_algebraic(self, rhs) } @@ -1541,8 +1555,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f64) -> f64 { + pub const fn algebraic_div(self, rhs: f64) -> f64 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1551,8 +1566,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f64) -> f64 { + pub const fn algebraic_rem(self, rhs: f64) -> f64 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 05d8216ac27eb..8d31a7b697a80 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3675,6 +3675,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[must_use = "this returns the result of the operation, \ @@ -3778,6 +3779,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 348457e2c00f6..ecc1c7bf9021d 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -130,7 +130,7 @@ depending on the target pointer size. macro_rules! midpoint_impl { ($SelfT:ty, unsigned) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large unsigned integral type. This implies that the result is @@ -146,6 +146,8 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { // Use the well known branchless algorithm from Hacker's Delight to compute @@ -154,7 +156,7 @@ macro_rules! midpoint_impl { } }; ($SelfT:ty, signed) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -173,6 +175,9 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint_signed", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average_ceil")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: Self) -> Self { // Use the well known branchless algorithm from Hacker's Delight to compute @@ -184,7 +189,7 @@ macro_rules! midpoint_impl { } }; ($SelfT:ty, $WideT:ty, unsigned) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large unsigned integral type. This implies that the result is @@ -200,13 +205,15 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { ((self as $WideT + rhs as $WideT) / 2) as $SelfT } }; ($SelfT:ty, $WideT:ty, signed) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -225,6 +232,9 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint_signed", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average_ceil")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { ((self as $WideT + rhs as $WideT) / 2) as $SelfT diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 1b79112aec1fe..8a8b2733d5e88 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1589,7 +1589,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { super::int_log10::$Int(self.get()) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -1615,6 +1615,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: Self) -> Self { // SAFETY: The only way to get `0` with midpoint is to have two opposite or diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 3678bb091e7d0..bc6cb9508167d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -3523,6 +3523,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[inline] @@ -3624,6 +3625,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 7ec0ac7127142..aed5a043c11a3 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -162,8 +162,14 @@ //! The [`is_some`] and [`is_none`] methods return [`true`] if the [`Option`] //! is [`Some`] or [`None`], respectively. //! +//! The [`is_some_and`] and [`is_none_or`] methods apply the provided function +//! to the contents of the [`Option`] to produce a boolean value. +//! If this is [`None`] then a default result is returned instead without executing the function. +//! //! [`is_none`]: Option::is_none //! [`is_some`]: Option::is_some +//! [`is_some_and`]: Option::is_some_and +//! [`is_none_or`]: Option::is_none_or //! //! ## Adapters for working with references //! @@ -177,6 +183,10 @@ //! [Option]<[Pin]<[&]T>> //! * [`as_pin_mut`] converts from [Pin]<[&mut] [Option]\> to //! [Option]<[Pin]<[&mut] T>> +//! * [`as_slice`] returns a one-element slice of the contained value, if any. +//! If this is [`None`], an empty slice is returned. +//! * [`as_mut_slice`] returns a mutable one-element slice of the contained value, if any. +//! If this is [`None`], an empty slice is returned. //! //! [&]: reference "shared reference" //! [&mut]: reference "mutable reference" @@ -187,6 +197,8 @@ //! [`as_pin_mut`]: Option::as_pin_mut //! [`as_pin_ref`]: Option::as_pin_ref //! [`as_ref`]: Option::as_ref +//! [`as_slice`]: Option::as_slice +//! [`as_mut_slice`]: Option::as_mut_slice //! //! ## Extracting the contained value //! @@ -200,12 +212,15 @@ //! (which must implement the [`Default`] trait) //! * [`unwrap_or_else`] returns the result of evaluating the provided //! function +//! * [`unwrap_unchecked`] produces *[undefined behavior]* //! //! [`expect`]: Option::expect //! [`unwrap`]: Option::unwrap //! [`unwrap_or`]: Option::unwrap_or //! [`unwrap_or_default`]: Option::unwrap_or_default //! [`unwrap_or_else`]: Option::unwrap_or_else +//! [`unwrap_unchecked`]: Option::unwrap_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! ## Transforming contained values //! @@ -230,8 +245,9 @@ //! * [`filter`] calls the provided predicate function on the contained //! value `t` if the [`Option`] is [`Some(t)`], and returns [`Some(t)`] //! if the function returns `true`; otherwise, returns [`None`] -//! * [`flatten`] removes one level of nesting from an -//! [`Option>`] +//! * [`flatten`] removes one level of nesting from an [`Option>`] +//! * [`inspect`] method takes ownership of the [`Option`] and applies +//! the provided function to the contained value by reference if [`Some`] //! * [`map`] transforms [`Option`] to [`Option`] by applying the //! provided function to the contained value of [`Some`] and leaving //! [`None`] values unchanged @@ -239,6 +255,7 @@ //! [`Some(t)`]: Some //! [`filter`]: Option::filter //! [`flatten`]: Option::flatten +//! [`inspect`]: Option::inspect //! [`map`]: Option::map //! //! These methods transform [`Option`] to a value of a possibly @@ -621,6 +638,10 @@ impl Option { /// /// let x: Option = None; /// assert_eq!(x.is_some_and(|x| x > 1), false); + /// + /// let x: Option = Some("ownership".to_string()); + /// assert_eq!(x.as_ref().is_some_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -665,6 +686,10 @@ impl Option { /// /// let x: Option = None; /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// + /// let x: Option = Some("ownership".to_string()); + /// assert_eq!(x.as_ref().is_none_or(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 8f1b5275871e6..9737d0baec7ca 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -59,6 +59,7 @@ pub use crate::hash::macros::Hash; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] #[doc(no_inline)] pub use crate::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 48ab9267f216c..736ffb7d0caf3 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -259,8 +259,14 @@ //! The [`is_ok`] and [`is_err`] methods return [`true`] if the [`Result`] //! is [`Ok`] or [`Err`], respectively. //! +//! The [`is_ok_and`] and [`is_err_and`] methods apply the provided function +//! to the contents of the [`Result`] to produce a boolean value. If the [`Result`] does not have the expected variant +//! then [`false`] is returned instead without executing the function. +//! //! [`is_err`]: Result::is_err //! [`is_ok`]: Result::is_ok +//! [`is_ok_and`]: Result::is_ok_and +//! [`is_err_and`]: Result::is_err_and //! //! ## Adapters for working with references //! @@ -287,6 +293,7 @@ //! (which must implement the [`Default`] trait) //! * [`unwrap_or_else`] returns the result of evaluating the provided //! function +//! * [`unwrap_unchecked`] produces *[undefined behavior]* //! //! The panicking methods [`expect`] and [`unwrap`] require `E` to //! implement the [`Debug`] trait. @@ -297,6 +304,8 @@ //! [`unwrap_or`]: Result::unwrap_or //! [`unwrap_or_default`]: Result::unwrap_or_default //! [`unwrap_or_else`]: Result::unwrap_or_else +//! [`unwrap_unchecked`]: Result::unwrap_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! These methods extract the contained value in a [`Result`] when it //! is the [`Err`] variant. They require `T` to implement the [`Debug`] @@ -304,10 +313,13 @@ //! //! * [`expect_err`] panics with a provided custom message //! * [`unwrap_err`] panics with a generic message +//! * [`unwrap_err_unchecked`] produces *[undefined behavior]* //! //! [`Debug`]: crate::fmt::Debug //! [`expect_err`]: Result::expect_err //! [`unwrap_err`]: Result::unwrap_err +//! [`unwrap_err_unchecked`]: Result::unwrap_err_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! ## Transforming contained values //! @@ -330,21 +342,29 @@ //! [`Some(v)`]: Option::Some //! [`transpose`]: Result::transpose //! -//! This method transforms the contained value of the [`Ok`] variant: +//! These methods transform the contained value of the [`Ok`] variant: //! //! * [`map`] transforms [`Result`] into [`Result`] by applying //! the provided function to the contained value of [`Ok`] and leaving //! [`Err`] values unchanged +//! * [`inspect`] takes ownership of the [`Result`], applies the +//! provided function to the contained value by reference, +//! and then returns the [`Result`] //! //! [`map`]: Result::map +//! [`inspect`]: Result::inspect //! -//! This method transforms the contained value of the [`Err`] variant: +//! These methods transform the contained value of the [`Err`] variant: //! //! * [`map_err`] transforms [`Result`] into [`Result`] by //! applying the provided function to the contained value of [`Err`] and //! leaving [`Ok`] values unchanged +//! * [`inspect_err`] takes ownership of the [`Result`], applies the +//! provided function to the contained value of [`Err`] by reference, +//! and then returns the [`Result`] //! //! [`map_err`]: Result::map_err +//! [`inspect_err`]: Result::inspect_err //! //! These methods transform a [`Result`] into a value of a possibly //! different type `U`: @@ -578,6 +598,10 @@ impl Result { /// /// let x: Result = Err("hey"); /// assert_eq!(x.is_ok_and(|x| x > 1), false); + /// + /// let x: Result = Ok("ownership".to_string()); + /// assert_eq!(x.as_ref().is_ok_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -623,6 +647,10 @@ impl Result { /// /// let x: Result = Ok(123); /// assert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false); + /// + /// let x: Result = Err("ownership".to_string()); + /// assert_eq!(x.as_ref().is_err_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 18c03b4a6f89c..42b8bdbbeae0f 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -1,5 +1,4 @@ #![stable(feature = "duration_core", since = "1.25.0")] - //! Temporal quantification. //! //! # Examples: @@ -308,6 +307,39 @@ impl Duration { Duration { secs, nanos: subsec_nanos } } + /// Creates a new Duration from the specified number of nanoseconds. + /// + /// # Panics + /// + /// Panics if the given number of nanoseconds is greater than what Duration can handle, + /// which is `(u64::MAX * NANOS_PER_SEC) + NANOS_PER_SEC - 1` + /// Use this function if you need to specify time greater than what can fit in u64 + /// (around 584 years). + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_from_nanos_u128)] + /// use std::time::Duration; + /// let time_in_nanos = 2u128.pow(64); + /// let duration = Duration::from_nanos_u128(time_in_nanos); + /// ``` + #[unstable(feature = "duration_from_nanos_u128", issue = "139201")] + #[must_use] + #[inline] + pub const fn from_nanos_u128(nanos: u128) -> Duration { + const NANOS_PER_SEC: u128 = self::NANOS_PER_SEC as u128; + let secs: u128 = nanos / NANOS_PER_SEC; + if secs > u64::MAX as u128 { + panic!("overflow in duration in Duration::from_nanos_u128"); + } + let subsec_nanos = (nanos % NANOS_PER_SEC) as u32; + // SAFETY: x % 1_000_000_000 < 1_000_000_000 also, subsec_nanos >= 0 since u128 >=0 and u32 >=0 + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) }; + + Duration { secs: secs as u64, nanos: subsec_nanos } + } + /// Creates a new `Duration` from the specified number of weeks. /// /// # Panics diff --git a/library/coretests/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs index 9bf4c21a9ab97..0d85b22c585a1 100644 --- a/library/coretests/tests/ffi/cstr.rs +++ b/library/coretests/tests/ffi/cstr.rs @@ -13,3 +13,9 @@ fn compares_as_u8s() { assert_eq!(Ord::cmp(a, b), Ord::cmp(a_bytes, b_bytes)); assert_eq!(PartialOrd::partial_cmp(a, b), PartialOrd::partial_cmp(a_bytes, b_bytes)); } + +#[test] +fn debug() { + let s = c"abc\x01\x02\n\xE2\x80\xA6\xFF"; + assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index ac11157593832..ef548971aafa1 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -12,7 +12,6 @@ #![feature(async_iterator)] #![feature(bigint_helper_methods)] #![feature(bstr)] -#![feature(cell_update)] #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_eval_select)] diff --git a/library/coretests/tests/time.rs b/library/coretests/tests/time.rs index fe7bb11c67589..6040c0bbd2a9e 100644 --- a/library/coretests/tests/time.rs +++ b/library/coretests/tests/time.rs @@ -45,6 +45,13 @@ fn from_weeks_overflow() { let _ = Duration::from_weeks(overflow); } +#[test] +#[should_panic] +fn from_nanos_u128_overflow() { + let overflow = (u64::MAX * NANOS_PER_SEC) + (NANOS_PER_SEC - 1) + 1 ; + let _ = Duration::from_nanos_u128(overflow); +} + #[test] fn constructors() { assert_eq!(Duration::from_weeks(1), Duration::from_secs(7 * 24 * 60 * 60)); @@ -72,6 +79,8 @@ fn secs() { assert_eq!(Duration::from_micros(1_000_001).as_secs(), 1); assert_eq!(Duration::from_nanos(999_999_999).as_secs(), 0); assert_eq!(Duration::from_nanos(1_000_000_001).as_secs(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).as_secs(),0); + assert_eq!(Duration::from_nanos_u128(1_000_000_001).as_secs(),1); } #[test] @@ -86,6 +95,8 @@ fn millis() { assert_eq!(Duration::from_micros(1_001_000).subsec_millis(), 1); assert_eq!(Duration::from_nanos(999_999_999).subsec_millis(), 999); assert_eq!(Duration::from_nanos(1_001_000_000).subsec_millis(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_millis(),999); + assert_eq!(Duration::from_nanos_u128(1_001_000_001).subsec_millis(),1); } #[test] @@ -100,6 +111,9 @@ fn micros() { assert_eq!(Duration::from_micros(1_000_001).subsec_micros(), 1); assert_eq!(Duration::from_nanos(999_999_999).subsec_micros(), 999_999); assert_eq!(Duration::from_nanos(1_000_001_000).subsec_micros(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_micros(),999_999); + assert_eq!(Duration::from_nanos_u128(1_000_001_000).subsec_micros(),1); + } #[test] @@ -114,6 +128,8 @@ fn nanos() { assert_eq!(Duration::from_micros(1_000_001).subsec_nanos(), 1000); assert_eq!(Duration::from_nanos(999_999_999).subsec_nanos(), 999_999_999); assert_eq!(Duration::from_nanos(1_000_000_001).subsec_nanos(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_nanos(),999_999_999); + assert_eq!(Duration::from_nanos_u128(1_000_000_001).subsec_nanos(),1); } #[test] diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c46dcebedcab8..36a1c57b02012 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -513,13 +513,13 @@ impl Span { } /// Creates an empty span pointing to directly before this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn start(&self) -> Span { Span(self.0.start()) } /// Creates an empty span pointing to directly after this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn end(&self) -> Span { Span(self.0.end()) } @@ -527,7 +527,7 @@ impl Span { /// The one-indexed line of the source file where the span starts. /// /// To obtain the line of the span's end, use `span.end().line()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn line(&self) -> usize { self.0.line() } @@ -535,7 +535,7 @@ impl Span { /// The one-indexed column of the source file where the span starts. /// /// To obtain the column of the span's end, use `span.end().column()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn column(&self) -> usize { self.0.column() } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 917a470842ca9..3536e84d58bed 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.155" } +compiler_builtins = { version = "=0.1.156" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -35,7 +35,7 @@ miniz_oxide = { version = "0.8.0", optional = true, default-features = false } addr2line = { version = "0.24.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.171", default-features = false, features = [ +libc = { version = "0.2.172", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 0eef2bd225c06..9ad26e5d28ec2 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -973,6 +973,9 @@ where /// Returns an array of length `N` with the results of each query. For soundness, at most one /// mutable reference will be returned to any value. `None` will be used if the key is missing. /// + /// This method performs a check to ensure there are no duplicate keys, which currently has a time-complexity of O(n^2), + /// so be careful when passing many keys. + /// /// # Panics /// /// Panics if any keys are overlapping. diff --git a/library/std/src/env.rs b/library/std/src/env.rs index c84a72c4fad02..1593969e114aa 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -13,7 +13,7 @@ use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf}; -use crate::sys::os as os_imp; +use crate::sys::{env as env_imp, os as os_imp}; use crate::{fmt, io, sys}; /// Returns the current working directory as a [`PathBuf`]. @@ -96,7 +96,7 @@ pub struct Vars { /// [`env::vars_os()`]: vars_os #[stable(feature = "env", since = "1.0.0")] pub struct VarsOs { - inner: os_imp::Env, + inner: env_imp::Env, } /// Returns an iterator of (variable, value) pairs of strings, for all the @@ -150,7 +150,7 @@ pub fn vars() -> Vars { #[must_use] #[stable(feature = "env", since = "1.0.0")] pub fn vars_os() -> VarsOs { - VarsOs { inner: os_imp::env() } + VarsOs { inner: env_imp::env() } } #[stable(feature = "env", since = "1.0.0")] @@ -259,7 +259,7 @@ pub fn var_os>(key: K) -> Option { } fn _var_os(key: &OsStr) -> Option { - os_imp::getenv(key) + env_imp::getenv(key) } /// The error type for operations interacting with environment variables. @@ -363,7 +363,7 @@ impl Error for VarError { #[stable(feature = "env", since = "1.0.0")] pub unsafe fn set_var, V: AsRef>(key: K, value: V) { let (key, value) = (key.as_ref(), value.as_ref()); - unsafe { os_imp::setenv(key, value) }.unwrap_or_else(|e| { + unsafe { env_imp::setenv(key, value) }.unwrap_or_else(|e| { panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") }) } @@ -434,7 +434,7 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { #[stable(feature = "env", since = "1.0.0")] pub unsafe fn remove_var>(key: K) { let key = key.as_ref(); - unsafe { os_imp::unsetenv(key) } + unsafe { env_imp::unsetenv(key) } .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) } diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index c07c391892d80..c9595b051e207 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -119,7 +119,7 @@ mod break_keyword {} #[doc(keyword = "const")] // -/// Compile-time constants, compile-time evaluable functions, and raw pointers. +/// Compile-time constants, compile-time blocks, compile-time evaluable functions, and raw pointers. /// /// ## Compile-time constants /// @@ -166,6 +166,12 @@ mod break_keyword {} /// /// For more detail on `const`, see the [Rust Book] or the [Reference]. /// +/// ## Compile-time blocks +/// +/// The `const` keyword can also be used to define a block of code that is evaluated at compile time. +/// This is useful for ensuring certain computations are completed before optimizations happen, as well as +/// before runtime. For more details, see the [Reference][const-blocks]. +/// /// ## Compile-time evaluable functions /// /// The other main use of the `const` keyword is in `const fn`. This marks a function as being @@ -184,6 +190,7 @@ mod break_keyword {} /// [pointer primitive]: pointer /// [Rust Book]: ../book/ch03-01-variables-and-mutability.html#constants /// [Reference]: ../reference/items/constant-items.html +/// [const-blocks]: ../reference/expressions/block-expr.html#const-blocks /// [const-eval]: ../reference/const_eval.html mod const_keyword {} @@ -381,11 +388,15 @@ mod enum_keyword {} /// lazy_static;`. The other use is in foreign function interfaces (FFI). /// /// `extern` is used in two different contexts within FFI. The first is in the form of external -/// blocks, for declaring function interfaces that Rust code can call foreign code by. +/// blocks, for declaring function interfaces that Rust code can call foreign code by. This use +/// of `extern` is unsafe, since we are asserting to the compiler that all function declarations +/// are correct. If they are not, using these items may lead to undefined behavior. /// /// ```rust ignore +/// // SAFETY: The function declarations given below are in +/// // line with the header files of `my_c_library`. /// #[link(name = "my_c_library")] -/// extern "C" { +/// unsafe extern "C" { /// fn my_c_function(x: i32) -> bool; /// } /// ``` diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3a52b7790376c..f77bf92a806e3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -709,6 +709,7 @@ pub use core::primitive; // Re-export built-in macros defined through core. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] pub use core::{ assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 0427feb29550f..4f9259f39c1ab 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -1100,3 +1100,39 @@ pub fn lchown>(dir: P, uid: Option, gid: Option) -> io: pub fn chroot>(dir: P) -> io::Result<()> { sys::fs::chroot(dir.as_ref()) } + +/// Create a FIFO special file at the specified path with the specified mode. +/// +/// # Examples +/// +/// ```no_run +/// # #![feature(unix_mkfifo)] +/// # #[cfg(not(unix))] +/// # fn main() {} +/// # #[cfg(unix)] +/// # fn main() -> std::io::Result<()> { +/// # use std::{ +/// # os::unix::fs::{mkfifo, PermissionsExt}, +/// # fs::{File, Permissions, remove_file}, +/// # io::{Write, Read}, +/// # }; +/// # let _ = remove_file("/tmp/fifo"); +/// mkfifo("/tmp/fifo", Permissions::from_mode(0o774))?; +/// +/// let mut wx = File::options().read(true).write(true).open("/tmp/fifo")?; +/// let mut rx = File::open("/tmp/fifo")?; +/// +/// wx.write_all(b"hello, world!")?; +/// drop(wx); +/// +/// let mut s = String::new(); +/// rx.read_to_string(&mut s)?; +/// +/// assert_eq!(s, "hello, world!"); +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "unix_mkfifo", issue = "139324")] +pub fn mkfifo>(path: P, permissions: Permissions) -> io::Result<()> { + sys::fs::mkfifo(path.as_ref(), permissions.mode()) +} diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs index db9621c8c205c..1840bb38c17c8 100644 --- a/library/std/src/os/unix/fs/tests.rs +++ b/library/std/src/os/unix/fs/tests.rs @@ -55,3 +55,23 @@ fn write_vectored_at() { let content = fs::read(&filename).unwrap(); assert_eq!(&content, expected); } + +#[test] +fn test_mkfifo() { + let tmp_dir = crate::test_helpers::tmpdir(); + + let fifo = tmp_dir.path().join("fifo"); + + mkfifo(&fifo, Permissions::from_mode(0o774)).unwrap(); + + let mut wx = fs::File::options().read(true).write(true).open(&fifo).unwrap(); + let mut rx = fs::File::open(fifo).unwrap(); + + wx.write_all(b"hello, world!").unwrap(); + drop(wx); + + let mut s = String::new(); + rx.read_to_string(&mut s).unwrap(); + + assert_eq!(s, "hello, world!"); +} diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 980213be7ea90..7cd20c48d8939 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -353,6 +353,15 @@ fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { } } +/// Checks whether the string is valid as a file extension, or panics otherwise. +fn validate_extension(extension: &OsStr) { + for &b in extension.as_encoded_bytes() { + if is_sep_byte(b) { + panic!("extension cannot contain path separators: {extension:?}"); + } + } +} + //////////////////////////////////////////////////////////////////////////////// // The core iterators //////////////////////////////////////////////////////////////////////////////// @@ -1507,13 +1516,7 @@ impl PathBuf { } fn _set_extension(&mut self, extension: &OsStr) -> bool { - for &b in extension.as_encoded_bytes() { - if b < 128 { - if is_separator(b as char) { - panic!("extension cannot contain path separators: {:?}", extension); - } - } - } + validate_extension(extension); let file_stem = match self.file_stem() { None => return false, @@ -1541,6 +1544,11 @@ impl PathBuf { /// Returns `false` and does nothing if [`self.file_name`] is [`None`], /// returns `true` and updates the extension otherwise. /// + /// # Panics + /// + /// Panics if the passed extension contains a path separator (see + /// [`is_separator`]). + /// /// # Caveats /// /// The appended `extension` may contain dots and will be used in its entirety, @@ -1582,6 +1590,8 @@ impl PathBuf { } fn _add_extension(&mut self, extension: &OsStr) -> bool { + validate_extension(extension); + let file_name = match self.file_name() { None => return false, Some(f) => f.as_encoded_bytes(), @@ -3265,7 +3275,7 @@ impl Hash for Path { if !verbatim { component_start += match tail { [b'.'] => 1, - [b'.', sep @ _, ..] if is_sep_byte(*sep) => 1, + [b'.', sep, ..] if is_sep_byte(*sep) => 1, _ => 0, }; } diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index c15d8c40085a5..68c9ac1e41463 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -46,6 +46,7 @@ pub use crate::result::Result::{self, Err, Ok}; // Re-exported built-in macros #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] #[doc(no_inline)] pub use core::prelude::v1::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 3a22a16cb165e..9737b2f5bfe60 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -46,7 +46,7 @@ macro_rules! rtprintpanic { macro_rules! rtabort { ($($t:tt)*) => { { - rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*)); + rtprintpanic!("fatal runtime error: {}, aborting\n", format_args!($($t)*)); crate::sys::abort_internal(); } } diff --git a/library/std/src/sys/alloc/sgx.rs b/library/std/src/sys/alloc/sgx.rs index f5c27688fbc8f..7a846e2376b9b 100644 --- a/library/std/src/sys/alloc/sgx.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -10,8 +10,10 @@ use crate::sys::pal::waitqueue::SpinMutex; // The current allocator here is the `dlmalloc` crate which we've got included // in the rust-lang/rust repository as a submodule. The crate is a port of // dlmalloc.c from C to Rust. +// +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys5alloc3sgx8DLMALLOCE")] static DLMALLOC: SpinMutex> = SpinMutex::new(dlmalloc::Dlmalloc::new_with_allocator(Sgx {})); diff --git a/library/std/src/sys/args/hermit.rs b/library/std/src/sys/args/hermit.rs deleted file mode 100644 index ddd644a554044..0000000000000 --- a/library/std/src/sys/args/hermit.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::ffi::{CStr, OsString, c_char}; -use crate::os::hermit::ffi::OsStringExt; -use crate::ptr; -use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicIsize, AtomicPtr}; - -#[path = "common.rs"] -mod common; -pub use common::Args; - -static ARGC: AtomicIsize = AtomicIsize::new(0); -static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { - ARGC.store(argc, Relaxed); - // Use release ordering here to broadcast writes by the OS. - ARGV.store(argv as *mut *const u8, Release); -} - -/// Returns the command line arguments -pub fn args() -> Args { - // Synchronize with the store above. - let argv = ARGV.load(Acquire); - // If argv has not been initialized yet, do not return any arguments. - let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) }; - let args: Vec = (0..argc) - .map(|i| unsafe { - let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect(); - - Args::new(args) -} diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index f24d6eb123e03..0011f55dc14ee 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -2,16 +2,26 @@ #![forbid(unsafe_op_in_unsafe_fn)] +#[cfg(any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_family = "windows", + target_os = "hermit", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + cfg_if::cfg_if! { - if #[cfg(all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))))] { + if #[cfg(any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_os = "hermit", + ))] { mod unix; pub use unix::*; } else if #[cfg(target_family = "windows")] { mod windows; pub use windows::*; - } else if #[cfg(target_os = "hermit")] { - mod hermit; - pub use hermit::*; } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use sgx::*; diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index efc4b79185227..0185a8a600094 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -8,6 +8,7 @@ use crate::sys::pal::abi::usercalls::raw::ByteBuffer; use crate::sys_common::FromInner; use crate::{fmt, slice}; +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE")] static ARGS: AtomicUsize = AtomicUsize::new(0); diff --git a/library/std/src/sys/args/uefi.rs b/library/std/src/sys/args/uefi.rs index 84406c7f69df2..02dada382eff0 100644 --- a/library/std/src/sys/args/uefi.rs +++ b/library/std/src/sys/args/uefi.rs @@ -1,14 +1,11 @@ use r_efi::protocols::loaded_image; +pub use super::common::Args; use crate::env::current_exe; use crate::ffi::OsString; use crate::iter::Iterator; use crate::sys::pal::helpers; -#[path = "common.rs"] -mod common; -pub use common::Args; - pub fn args() -> Args { let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs index 7d7815c6dff23..a7b79ad396e79 100644 --- a/library/std/src/sys/args/unix.rs +++ b/library/std/src/sys/args/unix.rs @@ -5,13 +5,13 @@ #![allow(dead_code)] // runtime init functions not used during testing +pub use super::common::Args; use crate::ffi::CStr; +#[cfg(target_os = "hermit")] +use crate::os::hermit::ffi::OsStringExt; +#[cfg(not(target_os = "hermit"))] use crate::os::unix::ffi::OsStringExt; -#[path = "common.rs"] -mod common; -pub use common::Args; - /// One-time global initialization. pub unsafe fn init(argc: isize, argv: *const *const u8) { unsafe { imp::init(argc, argv) } @@ -73,6 +73,7 @@ pub fn args() -> Args { target_os = "illumos", target_os = "emscripten", target_os = "haiku", + target_os = "hermit", target_os = "l4re", target_os = "fuchsia", target_os = "redox", @@ -100,7 +101,7 @@ mod imp { unsafe fn really_init(argc: isize, argv: *const *const u8) { // These don't need to be ordered with each other or other stores, - // because they only hold the unmodified system-provide argv/argc. + // because they only hold the unmodified system-provided argv/argc. ARGC.store(argc, Ordering::Relaxed); ARGV.store(argv as *mut _, Ordering::Relaxed); } diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasi.rs index 4795789e4c717..72063a87dc9f5 100644 --- a/library/std/src/sys/args/wasi.rs +++ b/library/std/src/sys/args/wasi.rs @@ -1,12 +1,9 @@ #![forbid(unsafe_op_in_unsafe_fn)] +pub use super::common::Args; use crate::ffi::{CStr, OsStr, OsString}; use crate::os::wasi::ffi::OsStrExt; -#[path = "common.rs"] -mod common; -pub use common::Args; - /// Returns the command line arguments pub fn args() -> Args { Args::new(maybe_args().unwrap_or(Vec::new())) diff --git a/library/std/src/sys/args/windows.rs b/library/std/src/sys/args/windows.rs index 47f0e5f2d05f8..81c44fabdcc67 100644 --- a/library/std/src/sys/args/windows.rs +++ b/library/std/src/sys/args/windows.rs @@ -6,6 +6,7 @@ #[cfg(test)] mod tests; +pub use super::common::Args; use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::os::windows::prelude::*; @@ -18,10 +19,6 @@ use crate::sys_common::AsInner; use crate::sys_common::wstr::WStrUnits; use crate::{io, iter, ptr}; -#[path = "common.rs"] -mod common; -pub use common::Args; - pub fn args() -> Args { // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 // string so it's safe for `WStrUnits` to use. diff --git a/library/std/src/sys/args/xous.rs b/library/std/src/sys/args/xous.rs index 09a47283d6573..2010bad14d1fb 100644 --- a/library/std/src/sys/args/xous.rs +++ b/library/std/src/sys/args/xous.rs @@ -1,10 +1,7 @@ +pub use super::common::Args; use crate::sys::pal::os::get_application_parameters; use crate::sys::pal::os::params::ArgumentList; -#[path = "common.rs"] -mod common; -pub use common::Args; - pub fn args() -> Args { let Some(params) = get_application_parameters() else { return Args::new(vec![]); diff --git a/library/std/src/sys/env/common.rs b/library/std/src/sys/env/common.rs new file mode 100644 index 0000000000000..f161ff073f3d5 --- /dev/null +++ b/library/std/src/sys/env/common.rs @@ -0,0 +1,48 @@ +use crate::ffi::OsString; +use crate::{fmt, vec}; + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries(self.slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self { + Env { iter: env.into_iter() } + } + + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + EnvStrDebug { slice: self.iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.as_slice()).finish() + } +} + +impl !Send for Env {} +impl !Sync for Env {} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} diff --git a/library/std/src/sys/env/hermit.rs b/library/std/src/sys/env/hermit.rs new file mode 100644 index 0000000000000..445ecdeb6a39f --- /dev/null +++ b/library/std/src/sys/env/hermit.rs @@ -0,0 +1,72 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::io; +use crate::os::hermit::ffi::OsStringExt; +use crate::sync::Mutex; + +static ENV: Mutex>> = Mutex::new(None); + +pub fn init(env: *const *const c_char) { + let mut guard = ENV.lock().unwrap(); + let map = guard.insert(HashMap::new()); + + if env.is_null() { + return; + } + + unsafe { + let mut environ = env; + while !(*environ).is_null() { + if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { + map.insert(key, value); + } + environ = environ.add(1); + } + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + let guard = ENV.lock().unwrap(); + let env = guard.as_ref().unwrap(); + + let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect(); + + Env::new(result) +} + +pub fn getenv(k: &OsStr) -> Option { + ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + ENV.lock().unwrap().as_mut().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + ENV.lock().unwrap().as_mut().unwrap().remove(k); + Ok(()) +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs new file mode 100644 index 0000000000000..d81ff875c830f --- /dev/null +++ b/library/std/src/sys/env/mod.rs @@ -0,0 +1,48 @@ +//! Platform-dependent environment variables abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(any( + target_family = "unix", + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(target_family = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use xous::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::*; + } else { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs new file mode 100644 index 0000000000000..85be9cd6ad418 --- /dev/null +++ b/library/std/src/sys/env/sgx.rs @@ -0,0 +1,55 @@ +#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers + +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; + +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests +#[cfg_attr(test, linkage = "available_externally")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")] +static ENV: AtomicUsize = AtomicUsize::new(0); +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests +#[cfg_attr(test, linkage = "available_externally")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")] +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex>; + +fn get_env_store() -> Option<&'static EnvStore> { + unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } +} + +fn create_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) + }); + unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let env = get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default(); + Env::new(env) +} + +pub fn getenv(k: &OsStr) -> Option { + get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + create_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + if let Some(env) = get_env_store() { + env.lock().unwrap().remove(k); + } + Ok(()) +} diff --git a/library/std/src/sys/env/solid.rs b/library/std/src/sys/env/solid.rs new file mode 100644 index 0000000000000..ea77fc3c11930 --- /dev/null +++ b/library/std/src/sys/env/solid.rs @@ -0,0 +1,96 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::raw::{c_char, c_int}; +use crate::os::solid::ffi::{OsStrExt, OsStringExt}; +use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe extern "C" { + static mut environ: *const *const c_char; + } + + unsafe { + let _guard = env_read_lock(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env::new(result); + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) +} + +/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this +/// function just returns a generic error. +fn cvt_env(t: c_int) -> io::Result { + if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } +} diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs new file mode 100644 index 0000000000000..1561df41cac3f --- /dev/null +++ b/library/std/src/sys/env/uefi.rs @@ -0,0 +1,102 @@ +pub use super::common::Env; +use crate::ffi::{OsStr, OsString}; +use crate::io; + +pub fn env() -> Env { + let env = uefi_env::get_all().expect("not supported on this platform"); + Env::new(env) +} + +pub fn getenv(key: &OsStr) -> Option { + uefi_env::get(key) +} + +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + uefi_env::set(key, val) +} + +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + uefi_env::unset(key) +} + +mod uefi_env { + use crate::ffi::{OsStr, OsString}; + use crate::io; + use crate::os::uefi::ffi::OsStringExt; + use crate::ptr::NonNull; + use crate::sys::{helpers, unsupported_err}; + + pub(crate) fn get(key: &OsStr) -> Option { + let shell = helpers::open_shell()?; + let mut key_ptr = helpers::os_string_to_raw(key)?; + unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } + } + + pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; + let mut val_ptr = helpers::os_string_to_raw(val) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } + } + + pub(crate) fn unset(key: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + } + + pub(crate) fn get_all() -> io::Result> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + + let mut vars = Vec::new(); + let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; + + if val.is_null() { + return Ok(vars); + } + + let mut start = 0; + + // UEFI Shell returns all keys separated by NULL. + // End of string is denoted by two NULLs + for i in 0.. { + if unsafe { *val.add(i) } == 0 { + // Two NULL signal end of string + if i == start { + break; + } + + let key = OsString::from_wide(unsafe { + crate::slice::from_raw_parts(val.add(start), i - start) + }); + // SAFETY: val.add(start) is always NULL terminated + let val = unsafe { get_raw(shell, val.add(start)) } + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; + + vars.push((key, val)); + start = i + 1; + } + } + + Ok(vars) + } + + unsafe fn get_raw( + shell: NonNull, + key_ptr: *mut r_efi::efi::Char16, + ) -> Option { + let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; + helpers::os_string_from_raw(val) + } + + unsafe fn set_raw( + key_ptr: *mut r_efi::efi::Char16, + val_ptr: *mut r_efi::efi::Char16, + ) -> io::Result<()> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + let r = + unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } +} diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs new file mode 100644 index 0000000000000..78c7af65f9e38 --- /dev/null +++ b/library/std/src/sys/env/unix.rs @@ -0,0 +1,126 @@ +use core::slice::memchr; + +use libc::c_char; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::unix::prelude::*; +use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::cvt; + +// Use `_NSGetEnviron` on Apple platforms. +// +// `_NSGetEnviron` is the documented alternative (see `man environ`), and has +// been available since the first versions of both macOS and iOS. +// +// Nowadays, specifically since macOS 10.8, `environ` has been exposed through +// `libdyld.dylib`, which is linked via. `libSystem.dylib`: +// +// +// So in the end, it likely doesn't really matter which option we use, but the +// performance cost of using `_NSGetEnviron` is extremely miniscule, and it +// might be ever so slightly more supported, so let's just use that. +// +// NOTE: The header where this is defined (`crt_externs.h`) was added to the +// iOS 13.0 SDK, which has been the source of a great deal of confusion in the +// past about the availability of this API. +// +// NOTE(madsmtm): Neither this nor using `environ` has been verified to not +// cause App Store rejections; if this is found to be the case, an alternative +// implementation of this is possible using `[NSProcessInfo environment]` +// - which internally uses `_NSGetEnviron` and a system-wide lock on the +// environment variables to protect against `setenv`, so using that might be +// desirable anyhow? Though it also means that we have to link to Foundation. +#[cfg(target_vendor = "apple")] +pub unsafe fn environ() -> *mut *const *const c_char { + unsafe { libc::_NSGetEnviron() as *mut *const *const c_char } +} + +// Use the `environ` static which is part of POSIX. +#[cfg(not(target_vendor = "apple"))] +pub unsafe fn environ() -> *mut *const *const c_char { + unsafe extern "C" { + static mut environ: *const *const c_char; + } + &raw mut environ +} + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + let mut environ = *environ(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env::new(result); + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) +} diff --git a/library/std/src/sys/env/unsupported.rs b/library/std/src/sys/env/unsupported.rs new file mode 100644 index 0000000000000..98905e6482747 --- /dev/null +++ b/library/std/src/sys/env/unsupported.rs @@ -0,0 +1,40 @@ +use crate::ffi::{OsStr, OsString}; +use crate::{fmt, io}; + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + self.0 + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs new file mode 100644 index 0000000000000..3719f9db51eb3 --- /dev/null +++ b/library/std/src/sys/env/wasi.rs @@ -0,0 +1,102 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::wasi::prelude::*; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::pal::os::{cvt, libc}; + +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + // Access to the environment must be protected by a lock in multi-threaded scenarios. + use crate::sync::{PoisonError, RwLock}; + static ENV_LOCK: RwLock<()> = RwLock::new(()); + pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) + } + pub fn env_write_lock() -> impl Drop { + ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) + } + } else { + // No need for a lock if we are single-threaded. + pub fn env_read_lock() -> impl Drop { + Box::new(()) + } + pub fn env_write_lock() -> impl Drop { + Box::new(()) + } + } +} + +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_environ(); + + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env::new(result); + } + + // See src/libstd/sys/pal/unix/os.rs, same as that + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| unsafe { + let _guard = env_write_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| unsafe { + let _guard = env_write_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + }) +} diff --git a/library/std/src/sys/env/windows.rs b/library/std/src/sys/env/windows.rs new file mode 100644 index 0000000000000..3c4d4a84cfd6b --- /dev/null +++ b/library/std/src/sys/env/windows.rs @@ -0,0 +1,133 @@ +use crate::ffi::{OsStr, OsString}; +use crate::os::windows::prelude::*; +use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s}; +use crate::{fmt, io, ptr, slice}; + +pub struct Env { + base: *mut c::WCHAR, + iter: EnvIterator, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + iter: &'a EnvIterator, +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + let iter: EnvIterator = (*iter).clone(); + let mut list = f.debug_list(); + for (a, b) in iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { base: _, iter } = self; + EnvStrDebug { iter } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { base: _, iter } = self; + f.debug_list().entries(iter.clone()).finish() + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self { base: _, iter } = self; + iter.next() + } +} + +#[derive(Clone)] +struct EnvIterator(*mut c::WCHAR); + +impl Iterator for EnvIterator { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(cur) = self; + loop { + unsafe { + if **cur == 0 { + return None; + } + let p = *cur as *const u16; + let mut len = 0; + while *p.add(len) != 0 { + len += 1; + } + let s = slice::from_raw_parts(p, len); + *cur = cur.add(len + 1); + + // Windows allows environment variables to start with an equals + // symbol (in any other position, this is the separator between + // variable name and value). Since`s` has at least length 1 at + // this point (because the empty string terminates the array of + // environment variables), we can safely slice. + let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { + Some(p) => p, + None => continue, + }; + return Some(( + OsStringExt::from_wide(&s[..pos]), + OsStringExt::from_wide(&s[pos + 1..]), + )); + } + } + } +} + +impl Drop for Env { + fn drop(&mut self) { + unsafe { + c::FreeEnvironmentStringsW(self.base); + } + } +} + +pub fn env() -> Env { + unsafe { + let ch = c::GetEnvironmentStringsW(); + if ch.is_null() { + panic!("failure getting env string from OS: {}", io::Error::last_os_error()); + } + Env { base: ch, iter: EnvIterator(ch) } + } +} + +pub fn getenv(k: &OsStr) -> Option { + let k = to_u16s(k).ok()?; + fill_utf16_buf( + |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, + OsStringExt::from_wide, + ) + .ok() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that k and v are null-terminated wide strings. + unsafe { + let k = to_u16s(k)?; + let v = to_u16s(v)?; + + cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) + } +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that v is a null-terminated wide strings. + unsafe { + let v = to_u16s(n)?; + cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) + } +} diff --git a/library/std/src/sys/env/xous.rs b/library/std/src/sys/env/xous.rs new file mode 100644 index 0000000000000..232a3dafb0be5 --- /dev/null +++ b/library/std/src/sys/env/xous.rs @@ -0,0 +1,54 @@ +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; +use crate::sys::pal::os::{get_application_parameters, params}; + +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex>; + +fn get_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + let env_store = EnvStore::default(); + if let Some(params) = get_application_parameters() { + for param in params { + if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { + let mut env_store = env_store.lock().unwrap(); + for env in envs { + env_store.insert(env.key.into(), env.value.into()); + } + break; + } + } + } + ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) + }); + unsafe { &*core::ptr::with_exposed_provenance::(ENV.load(Ordering::Relaxed)) } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let env = clone_to_vec(&*get_env_store().lock().unwrap()); + Env::new(env) +} + +pub fn getenv(k: &OsStr) -> Option { + get_env_store().lock().unwrap().get(k).cloned() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + get_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + get_env_store().lock().unwrap().remove(k); + Ok(()) +} diff --git a/library/std/src/sys/env/zkvm.rs b/library/std/src/sys/env/zkvm.rs new file mode 100644 index 0000000000000..2eb7005ba1289 --- /dev/null +++ b/library/std/src/sys/env/zkvm.rs @@ -0,0 +1,32 @@ +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_env; +pub use unsupported_env::{Env, env, setenv, unsetenv}; + +use crate::ffi::{OsStr, OsString}; +use crate::sys::os_str; +use crate::sys::pal::{WORD_SIZE, abi}; +use crate::sys_common::FromInner; + +pub fn getenv(varname: &OsStr) -> Option { + let varname = varname.as_encoded_bytes(); + let nbytes = + unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; + if nbytes == usize::MAX { + return None; + } + + let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; + let words = unsafe { abi::sys_alloc_words(nwords) }; + + let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; + debug_assert_eq!(nbytes, nbytes2); + + // Convert to OsString. + // + // FIXME: We can probably get rid of the extra copy here if we + // reimplement "os_str" instead of just using the generic unix + // "os_str". + let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; + Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) +} diff --git a/library/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs index 018d7954db26a..9683fd47cf96b 100644 --- a/library/std/src/sys/env_consts.rs +++ b/library/std/src/sys/env_consts.rs @@ -389,6 +389,17 @@ pub mod os { pub const EXE_EXTENSION: &str = "exe"; } +#[cfg(target_os = "zkvm")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".elf"; + pub const DLL_EXTENSION: &str = "elf"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + // The fallback when none of the other gates match. #[else] pub mod os { diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 4c5e36ce67a3b..d55e28074fe8c 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -9,7 +9,7 @@ cfg_if::cfg_if! { if #[cfg(target_family = "unix")] { mod unix; use unix as imp; - pub use unix::{chown, fchown, lchown}; + pub use unix::{chown, fchown, lchown, mkfifo}; #[cfg(not(target_os = "fuchsia"))] pub use unix::chroot; pub(crate) use unix::debug_assert_fd_is_open; diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index bc8817bac7044..351a9f9413f68 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2137,6 +2137,12 @@ pub fn chroot(dir: &Path) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) } +pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ()) + }) +} + pub use remove_dir_impl::remove_dir_all; // Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e7b631999e0da..f9a02b522e5e1 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,7 @@ pub mod anonymous_pipe; pub mod args; pub mod backtrace; pub mod cmath; +pub mod env; pub mod env_consts; pub mod exit_guard; pub mod fd; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 70636760a83b6..ea636938d703f 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -16,7 +16,10 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(missing_docs, nonstandard_style)] +use crate::io::ErrorKind; +use crate::os::hermit::hermit_abi; use crate::os::raw::c_char; +use crate::sys::env; pub mod futex; pub mod os; @@ -25,9 +28,6 @@ pub mod pipe; pub mod thread; pub mod time; -use crate::io::ErrorKind; -use crate::os::hermit::hermit_abi; - pub fn unsupported() -> crate::io::Result { Err(unsupported_err()) } @@ -76,7 +76,7 @@ pub unsafe extern "C" fn runtime_entry( } // initialize environment - os::init_environment(env); + env::init(env); let result = unsafe { main(argc as isize, argv) }; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 791cdb1e57e7d..a998c3165e52f 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,15 +1,10 @@ -use core::slice::memchr; - use super::hermit_abi; -use crate::collections::HashMap; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; -use crate::sync::Mutex; use crate::sys::unsupported; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } @@ -68,115 +63,6 @@ pub fn current_exe() -> io::Result { unsupported() } -static ENV: Mutex>> = Mutex::new(None); - -pub fn init_environment(env: *const *const c_char) { - let mut guard = ENV.lock().unwrap(); - let map = guard.insert(HashMap::new()); - - if env.is_null() { - return; - } - - unsafe { - let mut environ = env; - while !(*environ).is_null() { - if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { - map.insert(key, value); - } - environ = environ.add(1); - } - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - let guard = ENV.lock().unwrap(); - let env = guard.as_ref().unwrap(); - - let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::>(); - - Env { iter: result.into_iter() } -} - -pub fn getenv(k: &OsStr) -> Option { - ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - ENV.lock().unwrap().as_mut().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - ENV.lock().unwrap().as_mut().unwrap().remove(k); - Ok(()) -} - pub fn temp_dir() -> PathBuf { PathBuf::from("/tmp") } diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 8e2b271f1c970..f082d94614b4d 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -11,15 +11,17 @@ const USIZE_BITS: usize = 64; const TLS_KEYS: usize = 128; // Same as POSIX minimum const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx3abi3tls14TLS_KEY_IN_USEE")] static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; macro_rules! dup { ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); (() $($val:tt)*) => ([$($val),*]) } +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx3abi3tls14TLS_DESTRUCTORE")] static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); unsafe extern "C" { diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index b1ec2afd764e6..70f838679c9ca 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -1,14 +1,11 @@ use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; -use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; pub fn errno() -> i32 { RESULT_SUCCESS @@ -73,99 +70,6 @@ pub fn current_exe() -> io::Result { unsupported() } -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx2os3ENVE")] -static ENV: AtomicUsize = AtomicUsize::new(0); -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx2os8ENV_INITE")] -static ENV_INIT: Once = Once::new(); -type EnvStore = Mutex>; - -fn get_env_store() -> Option<&'static EnvStore> { - unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } -} - -fn create_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) - }); - unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = get_env_store() - .map(|env| clone_to_vec(&env.lock().unwrap())) - .unwrap_or_default() - .into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option { - get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - create_env_store().lock().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - if let Some(env) = get_env_store() { - env.lock().unwrap().remove(k); - } - Ok(()) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem in SGX") } diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index b6932df431f42..219ef1b7a9897 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -45,8 +45,9 @@ mod task_queue { } } + // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] - #[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE")] + #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx6thread10TASK_QUEUEE")] static TASK_QUEUE: Mutex> = Mutex::new(Vec::new()); pub(super) fn lock() -> MutexGuard<'static, Vec> { diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index e3b2e0aa50f4a..8f5976b0592ec 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,14 +1,8 @@ -use core::slice::memchr; - use super::{error, itron, unsupported}; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString}; -use crate::os::raw::{c_char, c_int}; -use crate::os::solid::ffi::{OsStrExt, OsStringExt}; +use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; -use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::run_with_cstr; -use crate::{fmt, io, vec}; +use crate::{fmt, io}; // `solid` directly maps `errno`s to μITRON error codes. impl itron::error::ItronError { @@ -75,138 +69,6 @@ pub fn current_exe() -> io::Result { unsupported() } -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - unsafe extern "C" { - static mut environ: *const *const c_char; - } - - unsafe { - let _guard = env_read_lock(); - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter() }; - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> Option { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) - }) -} - -/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this -/// function just returns a generic error. -fn cvt_env(t: c_int) -> io::Result { - if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } -} - pub fn temp_dir() -> PathBuf { panic!("no standard temporary directory on this platform") } diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index bf6945811ab0e..03f3c72b0229a 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -73,47 +73,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - None -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index d26d61890c19e..bfd4dc81cb44f 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -131,60 +131,6 @@ pub fn current_exe() -> io::Result { helpers::device_path_to_text(protocol).map(PathBuf::from) } -pub struct EnvStrDebug<'a> { - iter: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut list = f.debug_list(); - for (a, b) in self.iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -pub struct Env(crate::vec::IntoIter<(OsString, OsString)>); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - EnvStrDebug { iter: self.0.as_slice() } - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0.next() - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -pub fn env() -> Env { - let env = uefi_env::get_all().expect("not supported on this platform"); - Env(env.into_iter()) -} - -pub fn getenv(key: &OsStr) -> Option { - uefi_env::get(key) -} - -pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { - uefi_env::set(key, val) -} - -pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { - uefi_env::unset(key) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } @@ -213,85 +159,3 @@ pub fn exit(code: i32) -> ! { pub fn getpid() -> u32 { panic!("no pids on this platform") } - -mod uefi_env { - use crate::ffi::{OsStr, OsString}; - use crate::io; - use crate::os::uefi::ffi::OsStringExt; - use crate::ptr::NonNull; - use crate::sys::{helpers, unsupported_err}; - - pub(crate) fn get(key: &OsStr) -> Option { - let shell = helpers::open_shell()?; - let mut key_ptr = helpers::os_string_to_raw(key)?; - unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } - } - - pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { - let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - let mut val_ptr = helpers::os_string_to_raw(val) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } - } - - pub(crate) fn unset(key: &OsStr) -> io::Result<()> { - let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } - } - - pub(crate) fn get_all() -> io::Result> { - let shell = helpers::open_shell().ok_or(unsupported_err())?; - - let mut vars = Vec::new(); - let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; - - if val.is_null() { - return Ok(vars); - } - - let mut start = 0; - - // UEFI Shell returns all keys separated by NULL. - // End of string is denoted by two NULLs - for i in 0.. { - if unsafe { *val.add(i) } == 0 { - // Two NULL signal end of string - if i == start { - break; - } - - let key = OsString::from_wide(unsafe { - crate::slice::from_raw_parts(val.add(start), i - start) - }); - // SAFETY: val.add(start) is always NULL terminated - let val = unsafe { get_raw(shell, val.add(start)) } - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; - - vars.push((key, val)); - start = i + 1; - } - } - - Ok(vars) - } - - unsafe fn get_raw( - shell: NonNull, - key_ptr: *mut r_efi::efi::Char16, - ) -> Option { - let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; - helpers::os_string_from_raw(val) - } - - unsafe fn set_raw( - key_ptr: *mut r_efi::efi::Char16, - val_ptr: *mut r_efi::efi::Char16, - ) -> io::Result<()> { - let shell = helpers::open_shell().ok_or(unsupported_err())?; - let r = - unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; - if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } - } -} diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index f47421c67051b..4883303b88e27 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -5,20 +5,15 @@ #[cfg(test)] mod tests; -use core::slice::memchr; - use libc::{c_char, c_int, c_void}; use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::{self, PathBuf}; -use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] -use crate::sys::weak::weak; -use crate::sys::{cvt, fd}; -use crate::{fmt, io, iter, mem, ptr, slice, str, vec}; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::cvt; +use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; @@ -552,166 +547,6 @@ pub fn current_exe() -> io::Result { if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) } } -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -// Use `_NSGetEnviron` on Apple platforms. -// -// `_NSGetEnviron` is the documented alternative (see `man environ`), and has -// been available since the first versions of both macOS and iOS. -// -// Nowadays, specifically since macOS 10.8, `environ` has been exposed through -// `libdyld.dylib`, which is linked via. `libSystem.dylib`: -// -// -// So in the end, it likely doesn't really matter which option we use, but the -// performance cost of using `_NSGetEnviron` is extremely miniscule, and it -// might be ever so slightly more supported, so let's just use that. -// -// NOTE: The header where this is defined (`crt_externs.h`) was added to the -// iOS 13.0 SDK, which has been the source of a great deal of confusion in the -// past about the availability of this API. -// -// NOTE(madsmtm): Neither this nor using `environ` has been verified to not -// cause App Store rejections; if this is found to be the case, an alternative -// implementation of this is possible using `[NSProcessInfo environment]` -// - which internally uses `_NSGetEnviron` and a system-wide lock on the -// environment variables to protect against `setenv`, so using that might be -// desirable anyhow? Though it also means that we have to link to Foundation. -#[cfg(target_vendor = "apple")] -pub unsafe fn environ() -> *mut *const *const c_char { - libc::_NSGetEnviron() as *mut *const *const c_char -} - -// Use the `environ` static which is part of POSIX. -#[cfg(not(target_vendor = "apple"))] -pub unsafe fn environ() -> *mut *const *const c_char { - unsafe extern "C" { - static mut environ: *const *const c_char; - } - &raw mut environ -} - -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - unsafe { - let _guard = env_read_lock(); - let mut environ = *environ(); - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter() }; - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> Option { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| { - let _guard = ENV_LOCK.write(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| { - let _guard = ENV_LOCK.write(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - #[cfg(not(target_os = "espidf"))] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 73631053e9f47..efa6f8d776559 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -64,7 +64,10 @@ impl Condvar { // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c // // To work around this issue, the timeout is clamped to 1000 years. - #[cfg(target_vendor = "apple")] + // + // Cygwin implementation is based on NT API and a super large timeout + // makes the syscall block forever. + #[cfg(any(target_vendor = "apple", target_os = "cygwin"))] let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 48de4312885fe..a8ef97ecf67ac 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -62,47 +62,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - None -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index ba2b65a1f40dc..672cf70d1a5b2 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -1,19 +1,16 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use core::slice::memchr; - use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::marker::PhantomData; -use crate::ops::Drop; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; +use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::unsupported; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; // Add a few symbols not in upstream `libc` just yet. -mod libc { +pub mod libc { pub use libc::*; unsafe extern "C" { @@ -23,28 +20,6 @@ mod libc { } } -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - // Access to the environment must be protected by a lock in multi-threaded scenarios. - use crate::sync::{PoisonError, RwLock}; - static ENV_LOCK: RwLock<()> = RwLock::new(()); - pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) - } - pub fn env_write_lock() -> impl Drop { - ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) - } - } else { - // No need for a lock if we are single-threaded. - pub fn env_read_lock() -> impl Drop { - Box::new(()) - } - pub fn env_write_lock() -> impl Drop { - Box::new(()) - } - } -} - pub fn errno() -> i32 { unsafe extern "C" { #[thread_local] @@ -141,123 +116,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - unsafe { - let _guard = env_read_lock(); - - // Use `__wasilibc_get_environ` instead of `environ` here so that we - // don't require wasi-libc to eagerly initialize the environment - // variables. - let mut environ = libc::__wasilibc_get_environ(); - - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter() }; - } - - // See src/libstd/sys/pal/unix/os.rs, same as that - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> Option { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| unsafe { - let _guard = env_write_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| unsafe { - let _guard = env_write_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - #[allow(dead_code)] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } @@ -294,6 +152,6 @@ macro_rules! impl_is_minus_one { impl_is_minus_one! { i8 i16 i32 i64 isize } -fn cvt(t: T) -> io::Result { +pub fn cvt(t: T) -> io::Result { if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 044dc2e8cd8fa..f331282d2d72a 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,16 +5,16 @@ #[cfg(test)] mod tests; +use super::api; #[cfg(not(target_vendor = "uwp"))] use super::api::WinError; -use super::{api, to_u16s}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; -use crate::sys::{c, cvt}; -use crate::{fmt, io, ptr, slice}; +use crate::sys::pal::{c, cvt}; +use crate::{fmt, io, ptr}; pub fn errno() -> i32 { api::get_last_error().code as i32 @@ -76,108 +76,6 @@ pub fn error_string(mut errnum: i32) -> String { } } -pub struct Env { - base: *mut c::WCHAR, - iter: EnvIterator, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - iter: &'a EnvIterator, -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - let iter: EnvIterator = (*iter).clone(); - let mut list = f.debug_list(); - for (a, b) in iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { base: _, iter } = self; - EnvStrDebug { iter } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { base: _, iter } = self; - f.debug_list().entries(iter.clone()).finish() - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self { base: _, iter } = self; - iter.next() - } -} - -#[derive(Clone)] -struct EnvIterator(*mut c::WCHAR); - -impl Iterator for EnvIterator { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(cur) = self; - loop { - unsafe { - if **cur == 0 { - return None; - } - let p = *cur as *const u16; - let mut len = 0; - while *p.add(len) != 0 { - len += 1; - } - let s = slice::from_raw_parts(p, len); - *cur = cur.add(len + 1); - - // Windows allows environment variables to start with an equals - // symbol (in any other position, this is the separator between - // variable name and value). Since`s` has at least length 1 at - // this point (because the empty string terminates the array of - // environment variables), we can safely slice. - let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { - Some(p) => p, - None => continue, - }; - return Some(( - OsStringExt::from_wide(&s[..pos]), - OsStringExt::from_wide(&s[pos + 1..]), - )); - } - } - } -} - -impl Drop for Env { - fn drop(&mut self) { - unsafe { - c::FreeEnvironmentStringsW(self.base); - } - } -} - -pub fn env() -> Env { - unsafe { - let ch = c::GetEnvironmentStringsW(); - if ch.is_null() { - panic!("failure getting env string from OS: {}", io::Error::last_os_error()); - } - Env { base: ch, iter: EnvIterator(ch) } - } -} - pub struct SplitPaths<'a> { data: EncodeWide<'a>, must_yield: bool, @@ -290,33 +188,6 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) } -pub fn getenv(k: &OsStr) -> Option { - let k = to_u16s(k).ok()?; - super::fill_utf16_buf( - |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, - OsStringExt::from_wide, - ) - .ok() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - // SAFETY: We ensure that k and v are null-terminated wide strings. - unsafe { - let k = to_u16s(k)?; - let v = to_u16s(v)?; - - cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) - } -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - // SAFETY: We ensure that v is a null-terminated wide strings. - unsafe { - let v = to_u16s(n)?; - cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) - } -} - pub fn temp_dir() -> PathBuf { super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap() } diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 2c87e7d91f27d..1b41575358f1e 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,13 +1,11 @@ use super::unsupported; -use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; -use crate::{fmt, io, vec}; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::{fmt, io}; pub(crate) mod params; @@ -136,100 +134,6 @@ pub(crate) fn get_application_parameters() -> Option>; - -fn get_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - let env_store = EnvStore::default(); - if let Some(params) = get_application_parameters() { - for param in params { - if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { - let mut env_store = env_store.lock().unwrap(); - for env in envs { - env_store.insert(env.key.into(), env.value.into()); - } - break; - } - } - } - ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) - }); - unsafe { &*core::ptr::with_exposed_provenance::(ENV.load(Ordering::Relaxed)) } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option { - get_env_store().lock().unwrap().get(k).cloned() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - get_env_store().lock().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - get_env_store().lock().unwrap().remove(k); - Ok(()) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/zkvm/env.rs b/library/std/src/sys/pal/zkvm/env.rs deleted file mode 100644 index b85153642b1c9..0000000000000 --- a/library/std/src/sys/pal/zkvm/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".elf"; - pub const DLL_EXTENSION: &str = "elf"; - pub const EXE_SUFFIX: &str = ".elf"; - pub const EXE_EXTENSION: &str = "elf"; -} diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 868b19e33b672..a8ef97ecf67ac 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -1,10 +1,8 @@ -use super::{WORD_SIZE, abi, unsupported}; +use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sys::os_str; -use crate::sys_common::FromInner; use crate::{fmt, io}; pub fn errno() -> i32 { @@ -64,64 +62,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -pub fn getenv(varname: &OsStr) -> Option { - let varname = varname.as_encoded_bytes(); - let nbytes = - unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; - if nbytes == usize::MAX { - return None; - } - - let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; - let words = unsafe { abi::sys_alloc_words(nwords) }; - - let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; - debug_assert_eq!(nbytes, nbytes2); - - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; - Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 3b04ec50db30e..478f6583b1f22 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -88,7 +88,7 @@ impl Command { // in its own process. Thus the parent drops the lock guard immediately. // The child calls `mem::forget` to leak the lock, which is crucial because // releasing a lock is not async-signal-safe. - let env_lock = sys::os::env_read_lock(); + let env_lock = sys::env::env_read_lock(); let pid = unsafe { self.do_fork()? }; if pid == 0 { @@ -237,7 +237,7 @@ impl Command { // Similar to when forking, we want to ensure that access to // the environment is synchronized, so make sure to grab the // environment lock before we try to exec. - let _lock = sys::os::env_read_lock(); + let _lock = sys::env::env_read_lock(); let Err(e) = self.do_exec(theirs, envp.as_ref()); e @@ -386,13 +386,13 @@ impl Command { impl Drop for Reset { fn drop(&mut self) { unsafe { - *sys::os::environ() = self.0; + *sys::env::environ() = self.0; } } } - _reset = Some(Reset(*sys::os::environ())); - *sys::os::environ() = envp.as_ptr(); + _reset = Some(Reset(*sys::env::environ())); + *sys::env::environ() = envp.as_ptr(); } libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()); @@ -415,6 +415,7 @@ impl Command { all(target_os = "linux", target_env = "musl"), target_os = "nto", target_vendor = "apple", + target_os = "cygwin", )))] fn posix_spawn( &mut self, @@ -433,6 +434,7 @@ impl Command { all(target_os = "linux", target_env = "musl"), target_os = "nto", target_vendor = "apple", + target_os = "cygwin", ))] fn posix_spawn( &mut self, @@ -584,7 +586,7 @@ impl Command { /// Some platforms can set a new working directory for a spawned process in the /// `posix_spawn` path. This function looks up the function pointer for adding /// such an action to a `posix_spawn_file_actions_t` struct. - #[cfg(not(all(target_os = "linux", target_env = "musl")))] + #[cfg(not(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin")))] fn get_posix_spawn_addchdir() -> Option { use crate::sys::weak::weak; @@ -618,7 +620,9 @@ impl Command { /// Weak symbol lookup doesn't work with statically linked libcs, so in cases /// where static linking is possible we need to either check for the presence /// of the symbol at compile time or know about it upfront. - #[cfg(all(target_os = "linux", target_env = "musl"))] + /// + /// Cygwin doesn't support weak symbol, so just link it. + #[cfg(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin"))] fn get_posix_spawn_addchdir() -> Option { // Our minimum required musl supports this function, so we can just use it. Some(libc::posix_spawn_file_actions_addchdir_np) @@ -735,8 +739,8 @@ impl Command { cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_read_lock(); - let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); + let _env_lock = sys::env::env_read_lock(); + let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::env::environ() as *const _); #[cfg(not(target_os = "nto"))] let spawn_fn = libc::posix_spawnp; diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index 5f1727789a1bc..b92446f0cf673 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -67,7 +67,7 @@ impl Command { let c_envp = envp .as_ref() .map(|c| c.as_ptr()) - .unwrap_or_else(|| *sys::os::environ() as *const _); + .unwrap_or_else(|| *sys::env::environ() as *const _); let stack_size = crate::cmp::max( crate::env::var_os("RUST_MIN_STACK") .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) @@ -76,7 +76,7 @@ impl Command { ); // ensure that access to the environment is synchronized - let _lock = sys::os::env_read_lock(); + let _lock = sys::env::env_read_lock(); let ret = libc::rtpSpawn( self.get_program_cstr().as_ptr(), diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index d5a5d10205dd8..7cd448733130d 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -22,12 +22,16 @@ use crate::fmt; /// /// Initialization is dynamically performed on the first call to a setter (e.g. /// [`with`]) within a thread, and values that implement [`Drop`] get -/// destructed when a thread exits. Some caveats apply, which are explained below. +/// destructed when a thread exits. Some platform-specific caveats apply, which +/// are explained below. +/// Note that, should the destructor panics, the whole process will be [aborted]. /// /// A `LocalKey`'s initializer cannot recursively depend on itself. Using a /// `LocalKey` in this way may cause panics, aborts or infinite recursion on /// the first call to `with`. /// +/// [aborted]: crate::process::abort +/// /// # Single-thread Synchronization /// /// Though there is no potential race with other threads, it is still possible to diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 5ab71413586dc..03af35e809c91 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -205,8 +205,8 @@ pub struct Instant(time::Instant); /// println!("{}", elapsed.as_secs()); /// } /// Err(e) => { -/// // an error occurred! -/// println!("Error: {e:?}"); +/// // the system clock went backwards! +/// println!("Great Scott! {e:?}"); /// } /// } /// } @@ -245,6 +245,7 @@ pub struct Instant(time::Instant); /// > structure cannot represent the new point in time. /// /// [`add`]: SystemTime::add +/// [`UNIX_EPOCH`]: SystemTime::UNIX_EPOCH #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] pub struct SystemTime(time::SystemTime); diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs index df28e8129ddd9..677738bac8f98 100644 --- a/library/std/tests/floats/f128.rs +++ b/library/std/tests/floats/f128.rs @@ -112,6 +112,8 @@ fn test_nan() { assert!(!nan.is_sign_negative()); assert!(!nan.is_normal()); assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f128::MANTISSA_DIGITS - 2)) != 0); } #[test] diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs index 1a90f00aecceb..0fc4df8115a24 100644 --- a/library/std/tests/floats/f16.rs +++ b/library/std/tests/floats/f16.rs @@ -95,6 +95,8 @@ fn test_nan() { assert!(!nan.is_sign_negative()); assert!(!nan.is_normal()); assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f16::MANTISSA_DIGITS - 2)) != 0); } #[test] diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs index d99b03cb255f7..9af23afc5bbfc 100644 --- a/library/std/tests/floats/f32.rs +++ b/library/std/tests/floats/f32.rs @@ -72,6 +72,8 @@ fn test_nan() { assert!(nan.is_sign_positive()); assert!(!nan.is_sign_negative()); assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f32::MANTISSA_DIGITS - 2)) != 0); } #[test] diff --git a/library/std/tests/floats/f64.rs b/library/std/tests/floats/f64.rs index 611670751bb52..de9c27eb33d39 100644 --- a/library/std/tests/floats/f64.rs +++ b/library/std/tests/floats/f64.rs @@ -60,6 +60,8 @@ fn test_nan() { assert!(nan.is_sign_positive()); assert!(!nan.is_sign_negative()); assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f64::MANTISSA_DIGITS - 2)) != 0); } #[test] diff --git a/library/stdarch b/library/stdarch index 4666c7376f25a..1245618ccf5b2 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 4666c7376f25a265c74535585d622da3da6dfeb1 +Subproject commit 1245618ccf5b2df7ab1ebb0279b9f3f726670161 diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 17ee4d610f958..d415668f54a70 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -56,6 +56,7 @@ dependencies = [ "sha2", "sysinfo", "tar", + "tempfile", "termcolor", "toml", "tracing", @@ -225,14 +226,20 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fd-lock" version = "4.0.2" @@ -240,7 +247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", - "rustix", + "rustix 0.38.40", "windows-sys 0.52.0", ] @@ -266,6 +273,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "globset" version = "0.4.15" @@ -334,9 +353,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libredox" @@ -355,6 +374,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" + [[package]] name = "log" version = "0.4.22" @@ -486,6 +511,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "redox_syscall" version = "0.5.7" @@ -548,10 +579,23 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.3", + "windows-sys 0.59.0", +] + [[package]] name = "ryu" version = "1.0.18" @@ -678,6 +722,19 @@ dependencies = [ "xattr", ] +[[package]] +name = "tempfile" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.0.2", + "windows-sys 0.59.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -824,6 +881,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "winapi" version = "0.3.9" @@ -990,6 +1056,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "xattr" version = "1.3.1" @@ -997,8 +1072,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys", - "rustix", + "linux-raw-sys 0.4.14", + "rustix 0.38.40", ] [[package]] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 23aa87a74075b..712a9b04c1a19 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -83,6 +83,7 @@ features = [ [dev-dependencies] pretty_assertions = "1.4" +tempfile = "3.15.0" # We care a lot about bootstrap's compile times, so don't include debuginfo for # dependencies, only bootstrap itself. diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 68fa42ee9e655..2e5865e509695 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -111,14 +111,10 @@ impl Step for Std { // the `rust.download-rustc=true` option. let force_recompile = builder.rust_info().is_managed_git_subrepository() && builder.download_rustc() - && builder.config.last_modified_commit(&["library"], "download-rustc", true).is_none(); + && builder.config.has_changes_from_upstream(&["library"]); trace!("is managed git repo: {}", builder.rust_info().is_managed_git_subrepository()); trace!("download_rustc: {}", builder.download_rustc()); - trace!( - "last modified commit: {:?}", - builder.config.last_modified_commit(&["library"], "download-rustc", true) - ); trace!(force_recompile); run.builder.ensure(Std { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index ed90ede793622..3c412683b9492 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -421,13 +421,13 @@ impl Step for Rustc { builder.install(&rustdoc, &image.join("bin"), FileType::Executable); } + let ra_proc_macro_srv_compiler = + builder.compiler_for(compiler.stage, builder.config.build, compiler.host); + builder.ensure(compile::Rustc::new(ra_proc_macro_srv_compiler, compiler.host)); + if let Some(ra_proc_macro_srv) = builder.ensure_if_default( tool::RustAnalyzerProcMacroSrv { - compiler: builder.compiler_for( - compiler.stage, - builder.config.build, - compiler.host, - ), + compiler: ra_proc_macro_srv_compiler, target: compiler.host, }, builder.kind, @@ -1178,6 +1178,8 @@ impl Step for Cargo { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let cargo = builder.ensure(tool::Cargo { compiler, target }); let src = builder.src.join("src/tools/cargo"); let etc = src.join("src/etc"); @@ -1232,6 +1234,8 @@ impl Step for RustAnalyzer { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target }); let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); @@ -1274,6 +1278,8 @@ impl Step for Clippy { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + // Prepare the image directory // We expect clippy to build, because we've exited this step above if tool // state for clippy isn't testing. @@ -1324,9 +1330,12 @@ impl Step for Miri { if !builder.build.unstable_features() { return None; } + let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let miri = builder.ensure(tool::Miri { compiler, target }); let cargomiri = builder.ensure(tool::CargoMiri { compiler, target }); @@ -1463,6 +1472,8 @@ impl Step for Rustfmt { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let rustfmt = builder.ensure(tool::Rustfmt { compiler, target }); let cargofmt = builder.ensure(tool::Cargofmt { compiler, target }); let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); @@ -2328,6 +2339,8 @@ impl Step for LlvmBitcodeLinker { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let llbc_linker = builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] }); diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 48bb5cb8e8761..ee8bd8286daec 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -96,6 +96,8 @@ pub enum GccBuildStatus { /// Returns a path to the libgccjit.so file. #[cfg(not(test))] fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option { + use build_helper::git::PathFreshness; + // Try to download GCC from CI if configured and available if !matches!(builder.config.gcc_ci_mode, crate::core::config::GccCiMode::DownloadFromCi) { return None; @@ -104,18 +106,40 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option { + // Download from upstream CI + let root = ci_gcc_root(&builder.config); + let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&upstream); + if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() { + builder.config.download_ci_gcc(&upstream, &root); + t!(gcc_stamp.write()); + } + + let libgccjit = root.join("lib").join("libgccjit.so"); + create_lib_alias(builder, &libgccjit); + Some(libgccjit) + } + PathFreshness::HasLocalModifications { .. } => { + // We have local modifications, rebuild GCC. + eprintln!("Found local GCC modifications, GCC will *not* be downloaded"); + None + } + PathFreshness::MissingUpstream => { + eprintln!("error: could not find commit hash for downloading GCC"); + eprintln!("HELP: maybe your repository history is too shallow?"); + eprintln!("HELP: consider disabling `download-ci-gcc`"); + eprintln!("HELP: or fetch enough history to include one upstream commit"); + None + } } - - let libgccjit = root.join("lib").join("libgccjit.so"); - create_lib_alias(builder, &libgccjit); - Some(libgccjit) } #[cfg(test)] @@ -264,31 +288,16 @@ fn ci_gcc_root(config: &crate::Config) -> PathBuf { config.out.join(config.build).join("ci-gcc") } -/// This retrieves the GCC sha we *want* to use, according to git history. +/// Detect whether GCC sources have been modified locally or not. #[cfg(not(test))] -fn detect_gcc_sha(config: &crate::Config, is_git: bool) -> String { - use build_helper::git::get_closest_merge_commit; - - let gcc_sha = if is_git { - get_closest_merge_commit( - Some(&config.src), - &config.git_config(), - &["src/gcc", "src/bootstrap/download-ci-gcc-stamp"], - ) - .unwrap() +fn detect_gcc_freshness(config: &crate::Config, is_git: bool) -> build_helper::git::PathFreshness { + use build_helper::git::PathFreshness; + + if is_git { + config.check_path_modifications(&["src/gcc", "src/bootstrap/download-ci-gcc-stamp"]) } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) { - info.sha.trim().to_owned() + PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() } } else { - "".to_owned() - }; - - if gcc_sha.is_empty() { - eprintln!("error: could not find commit hash for downloading GCC"); - eprintln!("HELP: maybe your repository history is too shallow?"); - eprintln!("HELP: consider disabling `download-ci-gcc`"); - eprintln!("HELP: or fetch enough history to include one upstream commit"); - panic!(); + PathFreshness::MissingUpstream } - - gcc_sha } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 6f6839ad15b0b..86af956535e5e 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use std::sync::OnceLock; use std::{env, fs}; -use build_helper::git::get_closest_merge_commit; +use build_helper::git::PathFreshness; #[cfg(feature = "tracing")] use tracing::instrument; @@ -181,26 +181,15 @@ pub const LLVM_INVALIDATION_PATHS: &[&str] = &[ "src/version", ]; -/// This retrieves the LLVM sha we *want* to use, according to git history. -pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { - let llvm_sha = if is_git { - get_closest_merge_commit(Some(&config.src), &config.git_config(), LLVM_INVALIDATION_PATHS) - .unwrap() +/// Detect whether LLVM sources have been modified locally or not. +pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshness { + if is_git { + config.check_path_modifications(LLVM_INVALIDATION_PATHS) } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) { - info.sha.trim().to_owned() + PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() } } else { - "".to_owned() - }; - - if llvm_sha.is_empty() { - eprintln!("error: could not find commit hash for downloading LLVM"); - eprintln!("HELP: maybe your repository history is too shallow?"); - eprintln!("HELP: consider disabling `download-ci-llvm`"); - eprintln!("HELP: or fetch enough history to include one upstream commit"); - panic!(); + PathFreshness::MissingUpstream } - - llvm_sha } /// Returns whether the CI-found LLVM is currently usable. diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 83083e12ef1fc..9d07babe5196b 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -683,7 +683,7 @@ impl Step for Editor { match EditorKind::prompt_user() { Ok(editor_kind) => { if let Some(editor_kind) = editor_kind { - while !t!(create_editor_settings_maybe(config, editor_kind.clone())) {} + while !t!(create_editor_settings_maybe(config, &editor_kind)) {} } else { println!("Ok, skipping editor setup!"); } @@ -695,7 +695,7 @@ impl Step for Editor { /// Create the recommended editor LSP config file for rustc development, or just print it /// If this method should be re-called, it returns `false`. -fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result { +fn create_editor_settings_maybe(config: &Config, editor: &EditorKind) -> io::Result { let hashes = editor.hashes(); let (current_hash, historical_hashes) = hashes.split_last().unwrap(); let settings_path = editor.settings_path(config); @@ -752,7 +752,7 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu // exists but user modified, back it up Some(false) => { // exists and is not current version or outdated, so back it up - let backup = settings_path.clone().with_extension(editor.backup_extension()); + let backup = settings_path.with_extension(editor.backup_extension()); eprintln!( "WARNING: copying `{}` to `{}`", settings_path.file_name().unwrap().to_str().unwrap(), diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs index 6a6731cafc54a..fd4918961adba 100644 --- a/src/bootstrap/src/core/build_steps/suggest.rs +++ b/src/bootstrap/src/core/build_steps/suggest.rs @@ -13,7 +13,6 @@ pub fn suggest(builder: &Builder<'_>, run: bool) { let git_config = builder.config.git_config(); let suggestions = builder .tool_cmd(Tool::SuggestTests) - .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository) .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch) .env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email) .run_capture_stdout(builder) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 096f7de65975a..d4fccc535a6b0 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2064,7 +2064,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } let git_config = builder.config.git_config(); - cmd.arg("--git-repository").arg(git_config.git_repository); cmd.arg("--nightly-branch").arg(git_config.nightly_branch); cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email); cmd.force_coloring_in_ci(); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 3426da51a808c..1549d4713af2c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -696,8 +696,7 @@ impl Step for Rustdoc { let files_to_track = &["src/librustdoc", "src/tools/rustdoc"]; // Check if unchanged - if builder.config.last_modified_commit(files_to_track, "download-rustc", true).is_some() - { + if !builder.config.has_changes_from_upstream(files_to_track) { let precompiled_rustdoc = builder .config .ci_rustc_dir() diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5de824ebab238..51852099dc398 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1,11 +1,14 @@ +use std::env::VarError; use std::{panic, thread}; +use build_helper::stage0_parser::parse_stage0_file; use llvm::prebuilt_llvm_config; use super::*; use crate::Flags; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::config::Config; +use crate::utils::tests::git::{GitCtx, git_test}; static TEST_TRIPLE_1: &str = "i686-unknown-haiku"; static TEST_TRIPLE_2: &str = "i686-unknown-hurd-gnu"; @@ -239,42 +242,80 @@ fn alias_and_path_for_library() { } #[test] -fn ci_rustc_if_unchanged_logic() { - let config = Config::parse_inner( - Flags::parse(&[ - "build".to_owned(), - "--dry-run".to_owned(), - "--set=rust.download-rustc='if-unchanged'".to_owned(), - ]), - |&_| Ok(Default::default()), - ); - - let build = Build::new(config.clone()); - let builder = Builder::new(&build); - - if config.out.exists() { - fs::remove_dir_all(&config.out).unwrap(); - } +fn ci_rustc_if_unchanged_invalidate_on_compiler_changes() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + ctx.create_upstream_merge(&["compiler/bar"]); + // This change should invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["compiler/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); + assert_eq!(config.download_rustc_commit, None); + }); +} - builder.run_step_descriptions(&Builder::get_step_descriptions(config.cmd.kind()), &[]); +#[test] +fn ci_rustc_if_unchanged_invalidate_on_library_changes_in_ci() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + ctx.create_upstream_merge(&["compiler/bar"]); + // This change should invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["library/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); + assert_eq!(config.download_rustc_commit, None); + }); +} - // Make sure "if-unchanged" logic doesn't try to use CI rustc while there are changes - // in compiler and/or library. - if config.download_rustc_commit.is_some() { - let mut paths = vec!["compiler"]; +#[test] +fn ci_rustc_if_unchanged_do_not_invalidate_on_library_changes_outside_ci() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + let sha = ctx.create_upstream_merge(&["compiler/bar"]); + // This change should not invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["library/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", false); + assert_eq!(config.download_rustc_commit, Some(sha)); + }); +} - // Handle library tree the same way as in `Config::download_ci_rustc_commit`. - if builder.config.is_running_on_ci { - paths.push("library"); - } +#[test] +fn ci_rustc_if_unchanged_do_not_invalidate_on_tool_changes() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + let sha = ctx.create_upstream_merge(&["compiler/bar"]); + // This change should not invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["src/tools/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); + assert_eq!(config.download_rustc_commit, Some(sha)); + }); +} - let has_changes = config.last_modified_commit(&paths, "download-rustc", true).is_none(); +/// Prepares the given directory so that it looks like a rustc checkout. +/// Also configures `GitCtx` to use the correct merge bot e-mail for upstream merge commits. +fn prepare_rustc_checkout(ctx: &mut GitCtx) { + ctx.merge_bot_email = + format!("Merge bot <{}>", parse_stage0_file().config.git_merge_commit_email); + ctx.write("src/ci/channel", "nightly"); + ctx.commit(); +} - assert!( - !has_changes, - "CI-rustc can't be used with 'if-unchanged' while there are changes in compiler and/or library." - ); - } +/// Parses a Config directory from `path`, with the given value of `download_rustc`. +fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) -> Config { + Config::parse_inner( + Flags::parse(&[ + "build".to_owned(), + "--dry-run".to_owned(), + "--ci".to_owned(), + if ci { "true" } else { "false" }.to_owned(), + format!("--set=rust.download-rustc='{download_rustc}'"), + "--src".to_owned(), + path.to_str().unwrap().to_owned(), + ]), + |&_| Ok(Default::default()), + ) } mod defaults { @@ -408,6 +449,7 @@ mod dist { use pretty_assertions::assert_eq; use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, first, run_build}; + use crate::Flags; use crate::core::builder::*; fn configure(host: &[&str], target: &[&str]) -> Config { @@ -646,6 +688,37 @@ mod dist { ); } + /// This also serves as an important regression test for + /// and . + #[test] + fn dist_all_cross() { + let cmd_args = + &["dist", "--stage", "2", "--dry-run", "--config=/does/not/exist"].map(str::to_owned); + let config_str = r#" + [rust] + channel = "nightly" + + [build] + extended = true + + build = "i686-unknown-haiku" + host = ["i686-unknown-netbsd"] + target = ["i686-unknown-netbsd"] + "#; + let config = Config::parse_inner(Flags::parse(cmd_args), |&_| toml::from_str(config_str)); + let mut cache = run_build(&[], config); + + // Stage 2 `compile::Rustc` should **NEVER** be cached here. + assert_eq!( + first(cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 1), + ] + ); + } + #[test] fn build_all() { let build = Build::new(configure( diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 43b62789536d4..23b623d9bab23 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -11,12 +11,12 @@ use std::io::IsTerminal; use std::path::{Path, PathBuf, absolute}; use std::process::Command; use std::str::FromStr; -use std::sync::OnceLock; +use std::sync::{Arc, Mutex, OnceLock}; use std::{cmp, env, fs}; use build_helper::ci::CiEnv; use build_helper::exit; -use build_helper::git::{GitConfig, get_closest_merge_commit, output_result}; +use build_helper::git::{GitConfig, PathFreshness, check_path_modifications, output_result}; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; #[cfg(feature = "tracing")] @@ -422,6 +422,9 @@ pub struct Config { pub compiletest_use_stage0_libtest: bool, pub is_running_on_ci: bool, + + /// Cache for determining path modifications + pub path_modification_cache: Arc, PathFreshness>>>, } #[derive(Clone, Debug, Default)] @@ -2960,7 +2963,6 @@ impl Config { pub fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.stage0_metadata.config.git_repository, nightly_branch: &self.stage0_metadata.config.nightly_branch, git_merge_commit_email: &self.stage0_metadata.config.git_merge_commit_email, } @@ -3193,19 +3195,30 @@ impl Config { let commit = if self.rust_info.is_managed_git_subrepository() { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - match self.last_modified_commit(&allowed_paths, "download-rustc", if_unchanged) { - Some(commit) => commit, - None => { + let freshness = self.check_path_modifications(&allowed_paths); + self.verbose(|| { + eprintln!("rustc freshness: {freshness:?}"); + }); + match freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream } => { if if_unchanged { return None; } - println!("ERROR: could not find commit hash for downloading rustc"); - println!("HELP: maybe your repository history is too shallow?"); - println!( - "HELP: consider setting `rust.download-rustc=false` in bootstrap.toml" - ); - println!("HELP: or fetch enough history to include one upstream commit"); - crate::exit!(1); + + if self.is_running_on_ci { + eprintln!("CI rustc commit matches with HEAD and we are in CI."); + eprintln!( + "`rustc.download-ci` functionality will be skipped as artifacts are not available." + ); + return None; + } + + upstream + } + PathFreshness::MissingUpstream => { + eprintln!("No upstream commit found"); + return None; } } } else { @@ -3214,19 +3227,6 @@ impl Config { .expect("git-commit-info is missing in the project root") }; - if self.is_running_on_ci && { - let head_sha = - output(helpers::git(Some(&self.src)).arg("rev-parse").arg("HEAD").as_command_mut()); - let head_sha = head_sha.trim(); - commit == head_sha - } { - eprintln!("CI rustc commit matches with HEAD and we are in CI."); - eprintln!( - "`rustc.download-ci` functionality will be skipped as artifacts are not available." - ); - return None; - } - if debug_assertions_requested { eprintln!( "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ @@ -3264,9 +3264,7 @@ impl Config { self.update_submodule("src/llvm-project"); // Check for untracked changes in `src/llvm-project` and other important places. - let has_changes = self - .last_modified_commit(LLVM_INVALIDATION_PATHS, "download-ci-llvm", true) - .is_none(); + let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); // Return false if there are untracked changes, otherwise check if CI LLVM is available. if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) } @@ -3297,51 +3295,30 @@ impl Config { } } - /// Returns the last commit in which any of `modified_paths` were changed, - /// or `None` if there are untracked changes in the working directory and `if_unchanged` is true. - pub fn last_modified_commit( - &self, - modified_paths: &[&str], - option_name: &str, - if_unchanged: bool, - ) -> Option { - assert!( - self.rust_info.is_managed_git_subrepository(), - "Can't run `Config::last_modified_commit` on a non-git source." - ); - - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap(); - if commit.is_empty() { - println!("error: could not find commit hash for downloading components from CI"); - println!("help: maybe your repository history is too shallow?"); - println!("help: consider disabling `{option_name}`"); - println!("help: or fetch enough history to include one upstream commit"); - crate::exit!(1); - } - - // Warn if there were changes to the compiler or standard library since the ancestor commit. - let mut git = helpers::git(Some(&self.src)); - git.args(["diff-index", "--quiet", &commit, "--"]).args(modified_paths); - - let has_changes = !t!(git.as_command_mut().status()).success(); - if has_changes { - if if_unchanged { - if self.is_verbose() { - println!( - "warning: saw changes to one of {modified_paths:?} since {commit}; \ - ignoring `{option_name}`" - ); - } - return None; - } - println!( - "warning: `{option_name}` is enabled, but there are changes to one of {modified_paths:?}" - ); + /// Returns true if any of the `paths` have been modified locally. + pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool { + match self.check_path_modifications(paths) { + PathFreshness::LastModifiedUpstream { .. } => false, + PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, } + } - Some(commit.to_string()) + /// Checks whether any of the given paths have been modified w.r.t. upstream. + pub fn check_path_modifications(&self, paths: &[&'static str]) -> PathFreshness { + // Checking path modifications through git can be relatively expensive (>100ms). + // We do not assume that the sources would change during bootstrap's execution, + // so we can cache the results here. + // Note that we do not use a static variable for the cache, because it would cause problems + // in tests that create separate `Config` instsances. + self.path_modification_cache + .lock() + .unwrap() + .entry(paths.to_vec()) + .or_insert_with(|| { + check_path_modifications(&self.src, &self.git_config(), paths, CiEnv::current()) + .unwrap() + }) + .clone() } /// Checks if the given target is the same as the host target. @@ -3379,6 +3356,10 @@ impl Config { _ => !self.is_system_llvm(target), } } + + pub fn ci_env(&self) -> CiEnv { + if self.is_running_on_ci { CiEnv::GitHubActions } else { CiEnv::None } + } } /// Compares the current `Llvm` options against those in the CI LLVM builder and detects any incompatible options. diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index c8a12c9072c91..96ac8a6d52fab 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use std::{env, fs}; use build_helper::ci::CiEnv; +use build_helper::git::PathFreshness; use clap::CommandFactory; use serde::Deserialize; @@ -15,6 +16,7 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig}; +use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { Config::parse_inner( @@ -51,9 +53,7 @@ fn download_ci_llvm() { let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\""); if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { - let has_changes = if_unchanged_config - .last_modified_commit(LLVM_INVALIDATION_PATHS, "download-ci-llvm", true) - .is_none(); + let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); assert!( !has_changes, @@ -746,3 +746,242 @@ fn test_include_precedence_over_profile() { // override profile settings, so we expect this to be "dev" here. assert_eq!(config.channel, "dev"); } + +#[test] +fn test_pr_ci_unchanged_anywhere() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_nonupstream_merge(&["b"]); + let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha }); + }); +} + +#[test] +fn test_pr_ci_changed_in_pr() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_nonupstream_merge(&["b"]); + let src = ctx.check_modifications(&["b"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha }); + }); +} + +#[test] +fn test_auto_ci_unchanged_anywhere_select_parent() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_upstream_merge(&["b"]); + let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha }); + }); +} + +#[test] +fn test_auto_ci_changed_in_pr() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_upstream_merge(&["b", "c"]); + let src = ctx.check_modifications(&["c", "d"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha }); + }); +} + +#[test] +fn test_local_uncommitted_modifications() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_branch("feature"); + ctx.modify("a"); + + assert_eq!( + ctx.check_modifications(&["a", "d"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: sha } + ); + }); +} + +#[test] +fn test_local_committed_modifications() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_upstream_merge(&["b", "c"]); + ctx.create_branch("feature"); + ctx.modify("x"); + ctx.commit(); + ctx.modify("a"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&["a", "d"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: sha } + ); + }); +} + +#[test] +fn test_local_committed_modifications_subdirectory() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a/b/c"]); + ctx.create_upstream_merge(&["b", "c"]); + ctx.create_branch("feature"); + ctx.modify("a/b/d"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&["a/b"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: sha } + ); + }); +} + +#[test] +fn test_local_changes_in_head_upstream() { + git_test(|ctx| { + // We want to resolve to the upstream commit that made modifications to a, + // even if it is currently HEAD + let sha = ctx.create_upstream_merge(&["a"]); + assert_eq!( + ctx.check_modifications(&["a", "d"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: sha } + ); + }); +} + +#[test] +fn test_local_changes_in_previous_upstream() { + git_test(|ctx| { + // We want to resolve to this commit, which modified a + let sha = ctx.create_upstream_merge(&["a", "e"]); + // Not to this commit, which is the latest upstream commit + ctx.create_upstream_merge(&["b", "c"]); + ctx.create_branch("feature"); + ctx.modify("d"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&["a"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: sha } + ); + }); +} + +#[test] +fn test_local_no_upstream_commit_with_changes() { + git_test(|ctx| { + ctx.create_upstream_merge(&["a", "e"]); + ctx.create_upstream_merge(&["a", "e"]); + // We want to fall back to this commit, because there are no commits + // that modified `x`. + let sha = ctx.create_upstream_merge(&["a", "e"]); + ctx.create_branch("feature"); + ctx.modify("d"); + ctx.commit(); + assert_eq!( + ctx.check_modifications(&["x"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: sha } + ); + }); +} + +#[test] +fn test_local_no_upstream_commit() { + git_test(|ctx| { + let src = ctx.check_modifications(&["c", "d"], CiEnv::None); + assert_eq!(src, PathFreshness::MissingUpstream); + }); +} + +#[test] +fn test_local_changes_negative_path() { + git_test(|ctx| { + let upstream = ctx.create_upstream_merge(&["a"]); + ctx.create_branch("feature"); + ctx.modify("b"); + ctx.modify("d"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&[":!b", ":!d"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: upstream.clone() } + ); + assert_eq!( + ctx.check_modifications(&[":!c"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: upstream.clone() } + ); + assert_eq!( + ctx.check_modifications(&[":!d", ":!x"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream } + ); + }); +} + +#[test] +fn test_local_changes_subtree_that_used_bors() { + // Here we simulate a very specific situation related to subtrees. + // When you have merge commits locally, we should ignore them w.r.t. the artifact download + // logic. + // The upstream search code currently uses a simple heuristic: + // - Find commits by bors (or in general an author with the merge commit e-mail) + // - Find the newest such commit + // This should make it work even for subtrees that: + // - Used bors in the past (so they have bors merge commits in their history). + // - Use Josh to merge rustc into the subtree, in a way that the rustc history is the second + // parent, not the first one. + // + // In addition, when searching for modified files, we cannot simply start from HEAD, because + // in this situation git wouldn't find the right commit. + // + // This test checks that this specific scenario will resolve to the right rustc commit, both + // when finding a modified file and when finding a non-existent file (which essentially means + // that we just lookup the most recent upstream commit). + // + // See https://github.com/rust-lang/rust/issues/101907#issuecomment-2697671282 for more details. + git_test(|ctx| { + ctx.create_upstream_merge(&["a"]); + + // Start unrelated subtree history + ctx.run_git(&["switch", "--orphan", "subtree"]); + ctx.modify("bar"); + ctx.commit(); + // Now we need to emulate old bors commits in the subtree. + // Git only has a resolution of one second, which is a problem, since our git logic orders + // merge commits by their date. + // To avoid sleeping in the test, we modify the commit date to be forcefully in the past. + ctx.create_upstream_merge(&["subtree/a"]); + ctx.run_git(&["commit", "--amend", "--date", "Wed Feb 16 14:00 2011 +0100", "--no-edit"]); + + // Merge the subtree history into rustc + ctx.switch_to_branch("main"); + ctx.run_git(&["merge", "subtree", "--allow-unrelated"]); + + // Create a rustc commit that modifies a path that we're interested in (`x`) + let upstream_1 = ctx.create_upstream_merge(&["x"]); + // Create another bors commit + let upstream_2 = ctx.create_upstream_merge(&["a"]); + + ctx.switch_to_branch("subtree"); + + // Create a subtree branch + ctx.create_branch("subtree-pr"); + ctx.modify("baz"); + ctx.commit(); + // We merge rustc into this branch (simulating a "subtree pull") + ctx.merge("main", "committer "); + + // And then merge that branch into the subtree (simulating a situation right before a + // "subtree push") + ctx.switch_to_branch("subtree"); + ctx.merge("subtree-pr", "committer "); + + // And we want to check that we resolve to the right commits. + assert_eq!( + ctx.check_modifications(&["x"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: upstream_1 } + ); + assert_eq!( + ctx.check_modifications(&["nonexistent"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: upstream_2 } + ); + }); +} diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 4e4b948a8fd6a..b95d07356c1b9 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -720,8 +720,9 @@ download-rustc = false #[cfg(not(test))] pub(crate) fn maybe_download_ci_llvm(&self) { use build_helper::exit; + use build_helper::git::PathFreshness; - use crate::core::build_steps::llvm::detect_llvm_sha; + use crate::core::build_steps::llvm::detect_llvm_freshness; use crate::core::config::check_incompatible_options_for_ci_llvm; if !self.llvm_from_ci { @@ -729,7 +730,22 @@ download-rustc = false } let llvm_root = self.ci_llvm_root(); - let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository()); + let llvm_freshness = + detect_llvm_freshness(self, self.rust_info.is_managed_git_subrepository()); + self.verbose(|| { + eprintln!("LLVM freshness: {llvm_freshness:?}"); + }); + let llvm_sha = match llvm_freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream } => upstream, + PathFreshness::MissingUpstream => { + eprintln!("error: could not find commit hash for downloading LLVM"); + eprintln!("HELP: maybe your repository history is too shallow?"); + eprintln!("HELP: consider disabling `download-ci-llvm`"); + eprintln!("HELP: or fetch enough history to include one upstream commit"); + crate::exit!(1); + } + }; let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); if !llvm_stamp.is_up_to_date() && !self.dry_run() { diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index caef8ce3088a7..169fcec303e90 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -20,4 +20,4 @@ pub(crate) mod tracing; pub(crate) mod metrics; #[cfg(test)] -mod tests; +pub(crate) mod tests; diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 7cf1af1c3e2dc..b61fa3bb8d690 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -28,8 +28,6 @@ pub static CRATES: &[&str] = &[ "libc", "log", "memchr", - "mime", - "mime_guess", "minimal-lexical", "nom", "once_cell", @@ -38,7 +36,6 @@ pub static CRATES: &[&str] = &[ "pest_meta", "proc-macro2", "quote", - "rinja_parser", "rustc-hash", "self_cell", "serde", @@ -56,7 +53,6 @@ pub static CRATES: &[&str] = &[ "unic-langid", "unic-langid-impl", "unic-langid-macros", - "unicase", "unicode-ident", "unicode-width", "version_check", diff --git a/src/bootstrap/src/utils/tests/git.rs b/src/bootstrap/src/utils/tests/git.rs new file mode 100644 index 0000000000000..bd40f398c7b10 --- /dev/null +++ b/src/bootstrap/src/utils/tests/git.rs @@ -0,0 +1,151 @@ +use std::ffi::OsStr; +use std::fs::OpenOptions; +use std::path::Path; +use std::process::Command; + +use build_helper::ci::CiEnv; +use build_helper::git::{GitConfig, PathFreshness, check_path_modifications}; + +pub struct GitCtx { + dir: tempfile::TempDir, + pub git_repo: String, + pub nightly_branch: String, + pub merge_bot_email: String, +} + +impl GitCtx { + fn new() -> Self { + let dir = tempfile::TempDir::new().unwrap(); + let ctx = Self { + dir, + git_repo: "rust-lang/rust".to_string(), + nightly_branch: "nightly".to_string(), + merge_bot_email: "Merge bot ".to_string(), + }; + ctx.run_git(&["init"]); + ctx.run_git(&["config", "user.name", "Tester"]); + ctx.run_git(&["config", "user.email", "tester@rust-lang.org"]); + ctx.modify("README.md"); + ctx.commit(); + ctx.run_git(&["branch", "-m", "main"]); + ctx + } + + pub fn get_path(&self) -> &Path { + self.dir.path() + } + + pub fn check_modifications(&self, target_paths: &[&str], ci_env: CiEnv) -> PathFreshness { + check_path_modifications(self.dir.path(), &self.git_config(), target_paths, ci_env).unwrap() + } + + pub fn create_upstream_merge(&self, modified_files: &[&str]) -> String { + self.create_branch_and_merge("previous-pr", modified_files, &self.merge_bot_email) + } + + pub fn create_nonupstream_merge(&self, modified_files: &[&str]) -> String { + self.create_branch_and_merge("pr", modified_files, "Tester ") + } + + pub fn create_branch_and_merge( + &self, + branch: &str, + modified_files: &[&str], + author: &str, + ) -> String { + let current_branch = self.get_current_branch(); + + self.create_branch(branch); + for file in modified_files { + self.modify(file); + } + self.commit(); + self.switch_to_branch(¤t_branch); + self.merge(branch, author); + self.run_git(&["branch", "-d", branch]); + self.get_current_commit() + } + + pub fn get_current_commit(&self) -> String { + self.run_git(&["rev-parse", "HEAD"]) + } + + pub fn get_current_branch(&self) -> String { + self.run_git(&["rev-parse", "--abbrev-ref", "HEAD"]) + } + + pub fn merge(&self, branch: &str, author: &str) { + self.run_git(&["merge", "--no-commit", "--no-ff", branch]); + self.run_git(&[ + "commit".to_string(), + "-m".to_string(), + format!("Merge of {branch} into {}", self.get_current_branch()), + "--author".to_string(), + author.to_string(), + ]); + } + + pub fn modify(&self, path: &str) { + self.write(path, "line"); + } + + pub fn write(&self, path: &str, data: &str) { + use std::io::Write; + + let path = self.dir.path().join(path); + std::fs::create_dir_all(&path.parent().unwrap()).unwrap(); + + let mut file = OpenOptions::new().create(true).append(true).open(path).unwrap(); + writeln!(file, "{data}").unwrap(); + } + + pub fn commit(&self) -> String { + self.run_git(&["add", "."]); + self.run_git(&["commit", "-m", "commit message"]); + self.get_current_commit() + } + + pub fn switch_to_branch(&self, name: &str) { + self.run_git(&["switch", name]); + } + + /// Creates a branch and switches to it. + pub fn create_branch(&self, name: &str) { + self.run_git(&["checkout", "-b", name]); + } + + pub fn run_git>(&self, args: &[S]) -> String { + let mut cmd = self.git_cmd(); + cmd.args(args); + eprintln!("Running {cmd:?}"); + let output = cmd.output().unwrap(); + let stdout = String::from_utf8(output.stdout).unwrap().trim().to_string(); + let stderr = String::from_utf8(output.stderr).unwrap().trim().to_string(); + if !output.status.success() { + panic!("Git command `{cmd:?}` failed\nStdout\n{stdout}\nStderr\n{stderr}"); + } + stdout + } + + fn git_cmd(&self) -> Command { + let mut cmd = Command::new("git"); + cmd.current_dir(&self.dir); + cmd + } + + fn git_config(&self) -> GitConfig<'_> { + GitConfig { + nightly_branch: &self.nightly_branch, + git_merge_commit_email: &self.merge_bot_email, + } + } +} + +/// Run an end-to-end test that allows testing git logic. +pub fn git_test(test_fn: F) +where + F: FnOnce(&mut GitCtx), +{ + let mut ctx = GitCtx::new(); + test_fn(&mut ctx); +} diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 0791f7a6e2074..73d55db994cc0 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -1 +1,2 @@ +pub mod git; mod shared_helpers_tests; diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index f5347c3082410..438cd14389c1c 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -3,8 +3,8 @@ use std::process::{Command, Stdio}; use crate::ci::CiEnv; +#[derive(Debug)] pub struct GitConfig<'a> { - pub git_repository: &'a str, pub nightly_branch: &'a str, pub git_merge_commit_email: &'a str, } @@ -27,147 +27,234 @@ pub fn output_result(cmd: &mut Command) -> Result { String::from_utf8(output.stdout).map_err(|err| format!("{err:?}")) } -/// Finds the remote for rust-lang/rust. -/// For example for these remotes it will return `upstream`. -/// ```text -/// origin https://github.com/pietroalbani/rust.git (fetch) -/// origin https://github.com/pietroalbani/rust.git (push) -/// upstream https://github.com/rust-lang/rust (fetch) -/// upstream https://github.com/rust-lang/rust (push) -/// ``` -pub fn get_rust_lang_rust_remote( +/// Represents the result of checking whether a set of paths +/// have been modified locally or not. +#[derive(PartialEq, Debug, Clone)] +pub enum PathFreshness { + /// Artifacts should be downloaded from this upstream commit, + /// there are no local modifications. + LastModifiedUpstream { upstream: String }, + /// There are local modifications to a certain set of paths. + /// "Local" essentially means "not-upstream" here. + /// `upstream` is the latest upstream merge commit that made modifications to the + /// set of paths. + HasLocalModifications { upstream: String }, + /// No upstream commit was found. + /// This should not happen in most reasonable circumstances, but one never knows. + MissingUpstream, +} + +/// This function figures out if a set of paths was last modified upstream or +/// if there are some local modifications made to them. +/// It can be used to figure out if we should download artifacts from CI or rather +/// build them locally. +/// +/// The function assumes that at least a single upstream bors merge commit is in the +/// local git history. +/// +/// `target_paths` should be a non-empty slice of paths (git `pathspec`s) relative to `git_dir` +/// whose modifications would invalidate the artifact. +/// Each pathspec can also be a negative match, i.e. `:!foo`. This matches changes outside +/// the `foo` directory. +/// See +/// for how git `pathspec` works. +/// +/// The function behaves differently in CI and outside CI. +/// +/// - Outside CI, we want to find out if `target_paths` were modified in some local commit on +/// top of the latest upstream commit that is available in local git history. +/// If not, we try to find the most recent upstream commit (which we assume are commits +/// made by bors) that modified `target_paths`. +/// We don't want to simply take the latest master commit to avoid changing the output of +/// this function frequently after rebasing on the latest master branch even if `target_paths` +/// were not modified upstream in the meantime. In that case we would be redownloading CI +/// artifacts unnecessarily. +/// +/// - In CI, we use a shallow clone of depth 2, i.e., we fetch only a single parent commit +/// (which will be the most recent bors merge commit) and do not have access +/// to the full git history. Luckily, we only need to distinguish between two situations: +/// 1) The current PR made modifications to `target_paths`. +/// In that case, a build is typically necessary. +/// 2) The current PR did not make modifications to `target_paths`. +/// In that case we simply take the latest upstream commit, because on CI there is no need to avoid +/// redownloading. +pub fn check_path_modifications( + git_dir: &Path, config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result { - let mut git = Command::new("git"); - if let Some(git_dir) = git_dir { - git.current_dir(git_dir); + target_paths: &[&str], + ci_env: CiEnv, +) -> Result { + assert!(!target_paths.is_empty()); + for path in target_paths { + assert!(Path::new(path.trim_start_matches(":!")).is_relative()); } - git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]); - let stdout = output_result(&mut git)?; - let rust_lang_remote = stdout - .lines() - .find(|remote| remote.contains(config.git_repository)) - .ok_or_else(|| format!("{} remote not found", config.git_repository))?; + let upstream_sha = if matches!(ci_env, CiEnv::GitHubActions) { + // Here the situation is different for PR CI and try/auto CI. + // For PR CI, we have the following history: + // + // 1-N PR commits + // upstream merge commit made by bors + // + // For try/auto CI, we have the following history: + // <**non-upstream** merge commit made by bors> + // 1-N PR commits + // upstream merge commit made by bors + // + // But on both cases, HEAD should be a merge commit. + // So if HEAD contains modifications of `target_paths`, our PR has modified + // them. If not, we can use the only available upstream commit for downloading + // artifacts. - let remote_name = - rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?; - Ok(remote_name.into()) -} + // Do not include HEAD, as it is never an upstream commit + // If we do not find an upstream commit in CI, something is seriously wrong. + Some( + get_closest_upstream_commit(Some(git_dir), config, ci_env)? + .expect("No upstream commit was found on CI"), + ) + } else { + // Outside CI, we want to find the most recent upstream commit that + // modified the set of paths, to have an upstream reference that does not change + // unnecessarily often. + // However, if such commit is not found, we can fall back to the latest upstream commit + let upstream_with_modifications = + get_latest_upstream_commit_that_modified_files(git_dir, config, target_paths)?; + match upstream_with_modifications { + Some(sha) => Some(sha), + None => get_closest_upstream_commit(Some(git_dir), config, ci_env)?, + } + }; -pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result { - let mut git = Command::new("git"); - if let Some(git_dir) = git_dir { - git.current_dir(git_dir); - } - git.args(["rev-parse", rev]); - let output = git.output().map_err(|err| format!("{err:?}"))?; - - match output.status.code() { - Some(0) => Ok(true), - Some(128) => Ok(false), - None => Err(format!( - "git didn't exit properly: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )), - Some(code) => Err(format!( - "git command exited with status code: {code}: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )), + let Some(upstream_sha) = upstream_sha else { + return Ok(PathFreshness::MissingUpstream); + }; + + // For local environments, we want to find out if something has changed + // from the latest upstream commit. + // However, that should be equivalent to checking if something has changed + // from the latest upstream commit *that modified `target_paths`*, and + // with this approach we do not need to invoke git an additional time. + if has_changed_since(git_dir, &upstream_sha, target_paths) { + Ok(PathFreshness::HasLocalModifications { upstream: upstream_sha }) + } else { + Ok(PathFreshness::LastModifiedUpstream { upstream: upstream_sha }) } } -/// Returns the master branch from which we can take diffs to see changes. -/// This will usually be rust-lang/rust master, but sometimes this might not exist. -/// This could be because the user is updating their forked master branch using the GitHub UI -/// and therefore doesn't need an upstream master branch checked out. -/// We will then fall back to origin/master in the hope that at least this exists. -pub fn updated_master_branch( - config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result { - let upstream_remote = get_rust_lang_rust_remote(config, git_dir)?; - let branch = config.nightly_branch; - for upstream_master in [format!("{upstream_remote}/{branch}"), format!("origin/{branch}")] { - if rev_exists(&upstream_master, git_dir)? { - return Ok(upstream_master); - } - } +/// Returns true if any of the passed `paths` have changed since the `base` commit. +pub fn has_changed_since(git_dir: &Path, base: &str, paths: &[&str]) -> bool { + let mut git = Command::new("git"); + git.current_dir(git_dir); + + git.args(["diff-index", "--quiet", base, "--"]).args(paths); - Err("Cannot find any suitable upstream master branch".to_owned()) + // Exit code 0 => no changes + // Exit code 1 => some changes were detected + !git.status().expect("cannot run git diff-index").success() } -/// Finds the nearest merge commit by comparing the local `HEAD` with the upstream branch's state. -/// To work correctly, the upstream remote must be properly configured using `git remote add `. -/// In most cases `get_closest_merge_commit` is the function you are looking for as it doesn't require remote -/// to be configured. -fn git_upstream_merge_base( - config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result { - let updated_master = updated_master_branch(config, git_dir)?; +/// Returns the latest upstream commit that modified `target_paths`, or `None` if no such commit +/// was found. +fn get_latest_upstream_commit_that_modified_files( + git_dir: &Path, + git_config: &GitConfig<'_>, + target_paths: &[&str], +) -> Result, String> { let mut git = Command::new("git"); - if let Some(git_dir) = git_dir { - git.current_dir(git_dir); + git.current_dir(git_dir); + + // In theory, we could just use + // `git rev-list --first-parent HEAD --author= -- ` + // to find the latest upstream commit that modified ``. + // However, this does not work if you are in a subtree sync branch that contains merge commits + // which have the subtree history as their first parent, and the rustc history as second parent: + // `--first-parent` will just walk up the subtree history and never see a single rustc commit. + // We thus have to take a two-pronged approach. First lookup the most recent upstream commit + // by *date* (this should work even in a subtree sync branch), and then start the lookup for + // modified paths starting from that commit. + // + // See https://github.com/rust-lang/rust/pull/138591#discussion_r2037081858 for more details. + let upstream = get_closest_upstream_commit(Some(git_dir), git_config, CiEnv::None)? + .unwrap_or_else(|| "HEAD".to_string()); + + git.args([ + "rev-list", + "--first-parent", + "-n1", + &upstream, + "--author", + git_config.git_merge_commit_email, + ]); + + if !target_paths.is_empty() { + git.arg("--").args(target_paths); } - Ok(output_result(git.arg("merge-base").arg(&updated_master).arg("HEAD"))?.trim().to_owned()) + let output = output_result(&mut git)?.trim().to_owned(); + if output.is_empty() { Ok(None) } else { Ok(Some(output)) } } -/// Searches for the nearest merge commit in the repository. +/// Returns the most recent (ordered chronologically) commit found in the local history that +/// should exist upstream. We identify upstream commits by the e-mail of the commit +/// author. /// -/// **In CI** finds the nearest merge commit that *also exists upstream*. -/// -/// It looks for the most recent commit made by the merge bot by matching the author's email -/// address with the merge bot's email. -pub fn get_closest_merge_commit( +/// If we are in CI, we simply return our first parent. +fn get_closest_upstream_commit( git_dir: Option<&Path>, config: &GitConfig<'_>, - target_paths: &[&str], -) -> Result { + env: CiEnv, +) -> Result, String> { + let base = match env { + CiEnv::None => "HEAD", + CiEnv::GitHubActions => { + // On CI, we should always have a non-upstream merge commit at the tip, + // and our first parent should be the most recently merged upstream commit. + // We thus simply return our first parent. + return resolve_commit_sha(git_dir, "HEAD^1").map(Some); + } + }; + let mut git = Command::new("git"); if let Some(git_dir) = git_dir { git.current_dir(git_dir); } - let channel = include_str!("../../ci/channel").trim(); - - let merge_base = { - if CiEnv::is_ci() && - // FIXME: When running on rust-lang managed CI and it's not a nightly build, - // `git_upstream_merge_base` fails with an error message similar to this: - // ``` - // called `Result::unwrap()` on an `Err` value: "command did not execute successfully: - // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" - // ``` - // Investigate and resolve this issue instead of skipping it like this. - (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) - { - git_upstream_merge_base(config, git_dir).unwrap() - } else { - // For non-CI environments, ignore rust-lang/rust upstream as it usually gets - // outdated very quickly. - "HEAD".to_string() - } - }; - + // We do not use `--first-parent`, because we can be in a situation (outside CI) where we have + // a subtree merge that actually has the main rustc history as its second parent. + // Using `--first-parent` would recurse into the history of the subtree, which could have some + // old bors commits that are not relevant to us. + // With `--author-date-order`, git recurses into all parent subtrees, and returns the most + // chronologically recent bors commit. + // Here we assume that none of our subtrees use bors anymore, and that all their old bors + // commits are way older than recent rustc bors commits! git.args([ "rev-list", + "--author-date-order", &format!("--author={}", config.git_merge_commit_email), "-n1", - "--first-parent", - &merge_base, + &base, ]); - if !target_paths.is_empty() { - git.arg("--").args(target_paths); + let output = output_result(&mut git)?.trim().to_owned(); + if output.is_empty() { Ok(None) } else { Ok(Some(output)) } +} + +/// Resolve the commit SHA of `commit_ref`. +fn resolve_commit_sha(git_dir: Option<&Path>, commit_ref: &str) -> Result { + let mut git = Command::new("git"); + + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); } + git.args(["rev-parse", commit_ref]); + Ok(output_result(&mut git)?.trim().to_owned()) } -/// Returns the files that have been modified in the current branch compared to the last merge. +/// Returns the files that have been modified in the current branch compared to the master branch. +/// This includes committed changes, uncommitted changes, and changes that are not even staged. +/// /// The `extensions` parameter can be used to filter the files by their extension. /// Does not include removed files. /// If `extensions` is empty, all files will be returned. @@ -176,7 +263,9 @@ pub fn get_git_modified_files( git_dir: Option<&Path>, extensions: &[&str], ) -> Result, String> { - let merge_base = get_closest_merge_commit(git_dir, config, &[])?; + let Some(merge_base) = get_closest_upstream_commit(git_dir, config, CiEnv::None)? else { + return Err("No upstream commit was found".to_string()); + }; let mut git = Command::new("git"); if let Some(git_dir) = git_dir { @@ -204,13 +293,7 @@ pub fn get_git_modified_files( } /// Returns the files that haven't been added to git yet. -pub fn get_git_untracked_files( - config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result>, String> { - let Ok(_updated_master) = updated_master_branch(config, git_dir) else { - return Ok(None); - }; +pub fn get_git_untracked_files(git_dir: Option<&Path>) -> Result>, String> { let mut git = Command::new("git"); if let Some(git_dir) = git_dir { git.current_dir(git_dir); diff --git a/src/build_helper/src/stage0_parser.rs b/src/build_helper/src/stage0_parser.rs index 2a0c12a1c91c7..2723f4aa7b914 100644 --- a/src/build_helper/src/stage0_parser.rs +++ b/src/build_helper/src/stage0_parser.rs @@ -20,7 +20,6 @@ pub struct Stage0Config { pub artifacts_server: String, pub artifacts_with_llvm_assertions_server: String, pub git_merge_commit_email: String, - pub git_repository: String, pub nightly_branch: String, } @@ -49,7 +48,6 @@ pub fn parse_stage0_file() -> Stage0 { stage0.config.artifacts_with_llvm_assertions_server = value.to_owned() } "git_merge_commit_email" => stage0.config.git_merge_commit_email = value.to_owned(), - "git_repository" => stage0.config.git_repository = value.to_owned(), "nightly_branch" => stage0.config.nightly_branch = value.to_owned(), "compiler_date" => stage0.compiler.date = value.to_owned(), diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 13880ad466a6b..5600d7b4db59b 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -3,12 +3,15 @@ mod tests; use std::collections::BTreeMap; +use anyhow::Context as _; use serde_yaml::Value; use crate::GitHubContext; +use crate::utils::load_env_var; /// Representation of a job loaded from the `src/ci/github-actions/jobs.yml` file. #[derive(serde::Deserialize, Debug, Clone)] +#[serde(deny_unknown_fields)] pub struct Job { /// Name of the job, e.g. mingw-check pub name: String, @@ -26,6 +29,8 @@ pub struct Job { pub free_disk: Option, /// Documentation link to a resource that could help people debug this CI job. pub doc_url: Option, + /// Whether the job is executed on AWS CodeBuild. + pub codebuild: Option, } impl Job { @@ -80,7 +85,7 @@ impl JobDatabase { } pub fn load_job_db(db: &str) -> anyhow::Result { - let mut db: Value = serde_yaml::from_str(&db)?; + let mut db: Value = serde_yaml::from_str(db)?; // We need to expand merge keys (<<), because serde_yaml can't deal with them // `apply_merge` only applies the merge once, so do it a few times to unwrap nested merges. @@ -107,6 +112,29 @@ struct GithubActionsJob { free_disk: Option, #[serde(skip_serializing_if = "Option::is_none")] doc_url: Option, + #[serde(skip_serializing_if = "Option::is_none")] + codebuild: Option, +} + +/// Replace GitHub context variables with environment variables in job configs. +/// Used for codebuild jobs like +/// `codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt` +fn substitute_github_vars(jobs: Vec) -> anyhow::Result> { + let run_id = load_env_var("GITHUB_RUN_ID")?; + let run_attempt = load_env_var("GITHUB_RUN_ATTEMPT")?; + + let jobs = jobs + .into_iter() + .map(|mut job| { + job.os = job + .os + .replace("$github.run_id", &run_id) + .replace("$github.run_attempt", &run_attempt); + job + }) + .collect(); + + Ok(jobs) } /// Skip CI jobs that are not supposed to be executed on the given `channel`. @@ -177,6 +205,8 @@ fn calculate_jobs( } RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env), }; + let jobs = substitute_github_vars(jobs.clone()) + .context("Failed to substitute GitHub context variables in jobs")?; let jobs = skip_jobs(jobs, channel); let jobs = jobs .into_iter() @@ -207,6 +237,7 @@ fn calculate_jobs( continue_on_error: job.continue_on_error, free_disk: job.free_disk, doc_url: job.doc_url, + codebuild: job.codebuild, } }) .collect(); diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile index 420c42bc9d807..3795859f308e6 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ghcr.io/rust-lang/ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index be235f648b527..c09be047c6a80 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -59,11 +59,10 @@ COPY scripts/shared.sh /scripts/ ARG SCRIPT_ARG -COPY scripts/add_dummy_commit.sh /tmp/ COPY scripts/x86_64-gnu-llvm.sh /tmp/ COPY scripts/x86_64-gnu-llvm2.sh /tmp/ COPY scripts/x86_64-gnu-llvm3.sh /tmp/ COPY scripts/stage_2_test_set1.sh /tmp/ COPY scripts/stage_2_test_set2.sh /tmp/ -ENV SCRIPT "/tmp/add_dummy_commit.sh && /tmp/${SCRIPT_ARG}" +ENV SCRIPT "/tmp/${SCRIPT_ARG}" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile index 408b87125e0c6..83a3bfb37a54b 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile @@ -59,11 +59,10 @@ COPY scripts/shared.sh /scripts/ ARG SCRIPT_ARG -COPY scripts/add_dummy_commit.sh /tmp/ COPY scripts/x86_64-gnu-llvm.sh /tmp/ COPY scripts/x86_64-gnu-llvm2.sh /tmp/ COPY scripts/x86_64-gnu-llvm3.sh /tmp/ COPY scripts/stage_2_test_set1.sh /tmp/ COPY scripts/stage_2_test_set2.sh /tmp/ -ENV SCRIPT "/tmp/add_dummy_commit.sh && /tmp/${SCRIPT_ARG}" +ENV SCRIPT "/tmp/${SCRIPT_ARG}" diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 00d791eeb6b38..36f7df2b06907 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -288,7 +288,7 @@ args="$args --privileged" # `LOCAL_USER_ID` (recognized in `src/ci/run.sh`) to ensure that files are all # read/written as the same user as the bare-metal user. if [ -f /.dockerenv ]; then - docker create -v /checkout --name checkout alpine:3.4 /bin/true + docker create -v /checkout --name checkout ghcr.io/rust-lang/alpine:3.4 /bin/true docker cp . checkout:/checkout args="$args --volumes-from checkout" else diff --git a/src/ci/docker/scripts/add_dummy_commit.sh b/src/ci/docker/scripts/add_dummy_commit.sh deleted file mode 100755 index 029e4ae141f8f..0000000000000 --- a/src/ci/docker/scripts/add_dummy_commit.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -ex - -if [ "$READ_ONLY_SRC" = "0" ]; then - # `core::builder::tests::ci_rustc_if_unchanged_logic` bootstrap test ensures that - # "download-rustc=if-unchanged" logic don't use CI rustc while there are changes on - # compiler and/or library. Here we are adding a dummy commit on compiler and running - # that test to make sure we never download CI rustc with a change on the compiler tree. - echo "" >> ../compiler/rustc/src/main.rs - git config --global user.email "dummy@dummy.com" - git config --global user.name "dummy" - git add ../compiler/rustc/src/main.rs - git commit -m "test commit for rust.download-rustc=if-unchanged logic" - DISABLE_CI_RUSTC_IF_INCOMPATIBLE=0 ../x.py test bootstrap \ - -- core::builder::tests::ci_rustc_if_unchanged_logic - # Revert the dummy commit - git reset --hard HEAD~1 -fi diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh index d1bf2dab1e2d7..17eb2cea59ac1 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh @@ -2,8 +2,6 @@ set -ex -/tmp/add_dummy_commit.sh - ##### Test stage 1 ##### ../x.py --stage 1 test --skip src/tools/tidy diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index cb2bec5a9dfa6..88b29d2df56ae 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -56,6 +56,15 @@ runners: - &job-aarch64-linux-8c os: ubuntu-24.04-arm64-8core-32gb <<: *base-job + + # Codebuild runners are provisioned in + # https://github.com/rust-lang/simpleinfra/blob/b7ddd5e6bec8a93ec30510cdddec02c5666fefe9/terragrunt/accounts/ci-prod/ci-runners/terragrunt.hcl#L2 + - &job-linux-36c-codebuild + free_disk: true + codebuild: true + os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt + <<: *base-job + envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact @@ -108,8 +117,6 @@ pr: - name: x86_64-gnu-llvm-19 env: ENABLE_GCC_CODEGEN: "1" - # We are adding (temporarily) a dummy commit on the compiler - READ_ONLY_SRC: "0" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - name: x86_64-gnu-tools @@ -153,7 +160,7 @@ auto: <<: *job-linux-4c - name: dist-arm-linux - <<: *job-linux-8c + <<: *job-linux-36c-codebuild - name: dist-armhf-linux <<: *job-linux-4c diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index 055a6ac2211e3..ad7ee136e9c27 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -14,6 +14,17 @@ isX86() { fi } +# Check if we're on a GitHub hosted runner. +# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". +isGitHubRunner() { + # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". + if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then + return 0 + else + return 1 + fi +} + # print a line of the specified character printSeparationLine() { for ((i = 0; i < 80; i++)); do @@ -32,7 +43,7 @@ getAvailableSpace() { # make Kb human readable (assume the input is Kb) # REF: https://unix.stackexchange.com/a/44087/60849 formatByteCount() { - numfmt --to=iec-i --suffix=B --padding=7 "$1"'000' + numfmt --to=iec-i --suffix=B --padding=7 "${1}000" } # macro to output saved space @@ -45,6 +56,11 @@ printSavedSpace() { after=$(getAvailableSpace) local saved=$((after - before)) + if [ "$saved" -lt 0 ]; then + echo "::warning::Saved space is negative: $saved. Using '0' as saved space." + saved=0 + fi + echo "" printSeparationLine "*" if [ -n "${title}" ]; then @@ -118,10 +134,14 @@ removeUnusedFilesAndDirs() { # Azure "/opt/az" "/usr/share/az_"* + ) + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then # Environment variable set by GitHub Actions - "$AGENT_TOOLSDIRECTORY" - ) + to_remove+=( + "${AGENT_TOOLSDIRECTORY}" + ) + fi for element in "${to_remove[@]}"; do if [ ! -e "$element" ]; then @@ -155,20 +175,25 @@ cleanPackages() { '^dotnet-.*' '^llvm-.*' '^mongodb-.*' - 'azure-cli' 'firefox' 'libgl1-mesa-dri' 'mono-devel' 'php.*' ) - if isX86; then + if isGitHubRunner; then packages+=( - 'google-chrome-stable' - 'google-cloud-cli' - 'google-cloud-sdk' - 'powershell' + azure-cli ) + + if isX86; then + packages+=( + 'google-chrome-stable' + 'google-cloud-cli' + 'google-cloud-sdk' + 'powershell' + ) + fi fi sudo apt-get -qq remove -y --fix-missing "${packages[@]}" diff --git a/src/ci/scripts/setup-upstream-remote.sh b/src/ci/scripts/setup-upstream-remote.sh deleted file mode 100755 index 52b4c98a89016..0000000000000 --- a/src/ci/scripts/setup-upstream-remote.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# In CI environments, bootstrap is forced to use the remote upstream based -# on "git_repository" and "nightly_branch" values from src/stage0 file. -# This script configures the remote as it may not exist by default. - -set -euo pipefail -IFS=$'\n\t' - -ci_dir=$(cd $(dirname $0) && pwd)/.. -source "$ci_dir/shared.sh" - -git_repository=$(parse_stage0_file_by_key "git_repository") -nightly_branch=$(parse_stage0_file_by_key "nightly_branch") - -# Configure "rust-lang/rust" upstream remote only when it's not origin. -if [ -z "$(git config remote.origin.url | grep $git_repository)" ]; then - echo "Configuring https://github.com/$git_repository remote as upstream." - git remote add upstream "https://github.com/$git_repository" - REMOTE_NAME="upstream" -else - REMOTE_NAME="origin" -fi - -git fetch $REMOTE_NAME $nightly_branch diff --git a/src/doc/nomicon b/src/doc/nomicon index 0c10c30cc5473..c76a20f0d9871 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 0c10c30cc54736c5c194ce98c50e2de84eeb6e79 +Subproject commit c76a20f0d987145dcedf05c5c073ce8d91f2e82a diff --git a/src/doc/reference b/src/doc/reference index 3340922df189b..3bf3402aea982 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 3340922df189bddcbaad17dc3927d51a76bcd5ed +Subproject commit 3bf3402aea982b876eb56c87da17b0685c6461d5 diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 67de4a9816552..dc52e0928cc4c 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -a7c39b68616668a45f0afd62849a1da7c8ad2516 +b8005bff3248cfc6e327faf4fa631ac49bb49ba9 diff --git a/src/doc/rustc-dev-guide/src/tests/minicore.md b/src/doc/rustc-dev-guide/src/tests/minicore.md index e4853b6d40e35..507b259e0275d 100644 --- a/src/doc/rustc-dev-guide/src/tests/minicore.md +++ b/src/doc/rustc-dev-guide/src/tests/minicore.md @@ -6,25 +6,37 @@ ui/codegen/assembly test suites. It provides `core` stubs for tests that need to build for cross-compiled targets but do not need/want to run. +
+Please note that [`minicore`] is only intended for `core` items, and explicitly +**not** `std` or `alloc` items because `core` items are applicable to a wider +range of tests. +
+ A test can use [`minicore`] by specifying the `//@ add-core-stubs` directive. Then, mark the test with `#![feature(no_core)]` + `#![no_std]` + `#![no_core]`. Due to Edition 2015 extern prelude rules, you will probably need to declare `minicore` as an extern crate. +## Implied compiler flags + Due to the `no_std` + `no_core` nature of these tests, `//@ add-core-stubs` implies and requires that the test will be built with `-C panic=abort`. -Unwinding panics are not supported. +**Unwinding panics are not supported.** + +Tests will also be built with `-C force-unwind-tables=yes` to preserve CFI +directives in assembly tests. + +TL;DR: `//@ add-core-stubs` implies two compiler flags: + +1. `-C panic=abort` +2. `-C force-unwind-tables=yes` + +## Adding more `core` stubs If you find a `core` item to be missing from the [`minicore`] stub, consider adding it to the test auxiliary if it's likely to be used or is already needed by more than one test. -
-Please note that [`minicore`] is only intended for `core` items, and explicitly -**not** `std` or `alloc` items because `core` items are applicable to a wider -range of tests. -
- ## Example codegen test that uses `minicore` ```rust,no_run diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 7c4e2a0fdae74..6232c8bcc0a61 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -303,7 +303,7 @@ It should be preferred to using `error-pattern`, which is imprecise and non-exha ### `error-pattern` The `error-pattern` [directive](directives.md) can be used for runtime messages, which don't -have a specific span, or in exceptional cases for compile time messages. +have a specific span, or in exceptional cases, for compile time messages. Let's think about this test: @@ -316,7 +316,7 @@ fn main() { } ``` -We want to ensure this shows "index out of bounds" but we cannot use the `ERROR` +We want to ensure this shows "index out of bounds", but we cannot use the `ERROR` annotation since the runtime error doesn't have any span. Then it's time to use the `error-pattern` directive: @@ -333,18 +333,19 @@ fn main() { Use of `error-pattern` is not recommended in general. For strict testing of compile time output, try to use the line annotations `//~` as much as -possible, including `//~?` annotations for diagnostics without span. +possible, including `//~?` annotations for diagnostics without spans. If the compile time output is target dependent or too verbose, use directive `//@ dont-require-annotations: ` to make the line annotation checking -non-exhaustive, some of the compiler messages can stay uncovered by annotations in this mode. +non-exhaustive. +Some of the compiler messages can stay uncovered by annotations in this mode. -For checking runtime output `//@ check-run-results` may be preferable. +For checking runtime output, `//@ check-run-results` may be preferable. Only use `error-pattern` if none of the above works. Line annotations `//~` are still checked in tests using `error-pattern`. -In exceptional cases use `//@ compile-flags: --error-format=human` to opt out of these checks. +In exceptional cases, use `//@ compile-flags: --error-format=human` to opt out of these checks. ### Diagnostic kinds (error levels) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 9870e5011ebf6..50c7ae3ef8db2 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,7 +34,7 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) @@ -43,6 +43,8 @@ target | notes [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue]. +[^win32-msvc-alignment]: Due to non-standard behavior of MSVC, native C code on this target can cause types with an alignment of more than 4 bytes to be incorrectly aligned to only 4 bytes (this affects, e.g., `u64` and `i64`). Rust applies some mitigations to reduce the impact of this issue, but this can still cause unsoundness due to unsafe code that (correctly) assumes that references are always properly aligned. See [issue #112480](https://github.com/rust-lang/rust/issues/112480). + [77071]: https://github.com/rust-lang/rust/issues/77071 [x86-32-float-return-issue]: https://github.com/rust-lang/rust/issues/115567 @@ -95,7 +97,7 @@ target | notes [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) -[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) [`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10, glibc 2.17) @@ -169,7 +171,7 @@ target | std | notes [`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment] [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] @@ -317,9 +319,9 @@ target | std | host | notes [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] -[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] -[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] +[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux diff --git a/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md index 60aaa371bbc1f..e98aa996a6e5e 100644 --- a/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md +++ b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md @@ -20,8 +20,9 @@ The `target_os` of the target is `cygwin`, and it is `unix`. ## Building the target -For cross-compilation you want LLVM with [llvm/llvm-project#121439 (merged)](https://github.com/llvm/llvm-project/pull/121439) applied to fix the LLVM codegen on importing external global variables from DLLs. -No native builds on Cygwin now. It should be possible theoretically though, but might need a lot of patches. +For cross-compilation you want LLVM at least 20.1.0-rc1. +No native builds on Cygwin now. +The tracking issue for host tools on Cygwin is [#137819](https://github.com/rust-lang/rust/issues/137819). ## Building Rust programs diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 872592d669d6d..b55ddf6e0e165 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -222,6 +222,28 @@ For more, see [the chapter on documentation tests](write-documentation/documenta See also `--test`. +## `--test-runtool`, `--test-runtool-arg`: program to run tests with; args to pass to it + +A doctest wrapper program can be specified with the `--test-runtool` flag. +Rustdoc will execute that wrapper instead of the doctest executable when +running tests. The first arguments to the wrapper will be any arguments +specified with the `--test-runtool-arg` flag, followed by the path to the +doctest executable to run. + +Using these options looks like this: + +```bash +$ rustdoc src/lib.rs --test-runtool path/to/runner --test-runtool-arg --do-thing --test-runtool-arg --do-other-thing +``` + +For example, if you want to run your doctests under valgrind you might run: + +```bash +$ rustdoc src/lib.rs --test-runtool valgrind +``` + +Another use case would be to run a test inside an emulator, or through a Virtual Machine. + ## `--target`: generate documentation for the specified target triple Using this flag looks like this: diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index bace2f5f95390..5635f68b1b398 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -89,7 +89,7 @@ the standard library and functions that are included in the results list: ### Non-functions in type-based search Certain items that are not functions are treated as though they -were a semantically equivelent function. +were a semantically equivalent function. For example, struct fields are treated as though they were getter methods. This means that a search for `CpuidResult -> u32` will show diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index d4ff69a99338b..69e5a5adbec29 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -631,60 +631,6 @@ The generated output (formatted) will look like this: `--output-format html` has no effect, as the default output is HTML. This is accepted on stable, even though the other options for this flag aren't. -## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests - - * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options --enable-per-target-ignores -``` - -This flag allows you to tag doctests with compiletest style `ignore-foo` filters that prevent -rustdoc from running that test if the target triple string contains foo. For example: - -```rust -///```ignore-foo,ignore-bar -///assert!(2 == 2); -///``` -struct Foo; -``` - -This will not be run when the build target is `super-awesome-foo` or `less-bar-awesome`. -If the flag is not enabled, then rustdoc will consume the filter, but do nothing with it, and -the above example will be run for all targets. -If you want to preserve backwards compatibility for older versions of rustdoc, you can use - -```rust -///```ignore,ignore-foo -///assert!(2 == 2); -///``` -struct Foo; -``` - -In older versions, this will be ignored on all targets, but on newer versions `ignore-gnu` will -override `ignore`. - -## `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it - - * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) - -Using these options looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options --runtool runner --runtool-arg --do-thing --runtool-arg --do-other-thing -``` - -These options can be used to run the doctest under a program, and also pass arguments to -that program. For example, if you want to run your doctests under valgrind you might run - -```bash -$ rustdoc src/lib.rs -Z unstable-options --runtool valgrind -``` - -Another use case would be to run a test inside an emulator, or through a Virtual Machine. - ## `--with-examples`: include examples of uses of items as documentation * Tracking issue: [#88791](https://github.com/rust-lang/rust/issues/88791) diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md index b921f67785754..077b02d603d0f 100644 --- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -427,6 +427,43 @@ should not be merged with the others. So the previous code should use it: In this case, it means that the line information will not change if you add/remove other doctests. +### Ignoring targets + +Attributes starting with `ignore-` can be used to ignore doctests for specific +targets. For example, `ignore-x86_64` will avoid building doctests when the +target name contains `x86_64`. + +```rust +/// ```ignore-x86_64 +/// assert!(2 == 2); +/// ``` +struct Foo; +``` + +This doctest will not be built for targets such as `x86_64-unknown-linux-gnu`. + +Multiple ignore attributes can be specified to ignore multiple targets: + +```rust +/// ```ignore-x86_64,ignore-windows +/// assert!(2 == 2); +/// ``` +struct Foo; +``` + +If you want to preserve backwards compatibility for older versions of rustdoc, +you can specify both `ignore` and `ignore-`, such as: + +```rust +/// ```ignore,ignore-x86_64 +/// assert!(2 == 2); +/// ``` +struct Foo; +``` + +In older versions, this will be ignored on all targets, but starting with +version CURRENT_RUSTC_VERSION, `ignore-x86_64` will override `ignore`. + ### Custom CSS classes for code blocks ```rust diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index f2e1bb80cb263..2f9d4d22e5a85 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -245,8 +245,6 @@ See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details. ## Example 1: Redirecting control flow using an indirect branch/call to an invalid destination ```rust,ignore (making doc tests pass cross-platform is hard) -#![feature(naked_functions)] - use std::arch::naked_asm; use std::mem; diff --git a/src/doc/unstable-book/src/language-features/deref-patterns.md b/src/doc/unstable-book/src/language-features/deref-patterns.md index d0102a665b0b0..d0a64538e8ccc 100644 --- a/src/doc/unstable-book/src/language-features/deref-patterns.md +++ b/src/doc/unstable-book/src/language-features/deref-patterns.md @@ -54,4 +54,25 @@ if let [b] = &mut *v { assert_eq!(v, [Box::new(Some(2))]); ``` +Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str` +is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is +expected. This lets them be used in `deref!(_)` patterns: + +```rust +# #![feature(deref_patterns)] +# #![allow(incomplete_features)] +match ("test".to_string(), b"test".to_vec()) { + (deref!("test"), deref!(b"test")) => {} + _ => panic!(), +} + +// Matching on slices and arrays using literals is possible elsewhere as well: +match *"test" { + "test" => {} + _ => panic!(), +} +``` + +Implicit deref pattern syntax is not yet supported for string or byte string literals. + [smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors diff --git a/src/doc/unstable-book/src/library-features/concat-idents.md b/src/doc/unstable-book/src/library-features/concat-idents.md index 4366172fb9965..8a38d155e3dbc 100644 --- a/src/doc/unstable-book/src/library-features/concat-idents.md +++ b/src/doc/unstable-book/src/library-features/concat-idents.md @@ -2,7 +2,10 @@ The tracking issue for this feature is: [#29599] +This feature is deprecated, to be replaced by [`macro_metavar_expr_concat`]. + [#29599]: https://github.com/rust-lang/rust/issues/29599 +[`macro_metavar_expr_concat`]: https://github.com/rust-lang/rust/issues/124225 ------------------------ diff --git a/src/gcc b/src/gcc index 13cc8243226a9..0ea98a1365b81 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit 13cc8243226a9028bb08ab6c5e1c5fe6d533bcdf +Subproject commit 0ea98a1365b81f7488073512c850e8ee951a4afd diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index 31f9c284d7dd5..fc99dd08b78a6 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -96,7 +96,7 @@ fn print_tt(printer: &mut Printer<'_>, tt: &TokenTree) { } } TokenTree::Delimited(_span, _spacing, delim, tts) => { - let open_delim = printer.token_kind_to_string(&token::OpenDelim(*delim)); + let open_delim = printer.token_kind_to_string(&delim.as_open_token_kind()); printer.word(open_delim); if !tts.is_empty() { if *delim == Delimiter::Brace { @@ -107,7 +107,7 @@ fn print_tt(printer: &mut Printer<'_>, tt: &TokenTree) { printer.space(); } } - let close_delim = printer.token_kind_to_string(&token::CloseDelim(*delim)); + let close_delim = printer.token_kind_to_string(&delim.as_close_token_kind()); printer.word(close_delim); } } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 23a2bcd9011ee..4ef73ff48edb5 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -124,13 +124,9 @@ pub(crate) struct Options { /// temporary directory if not set. pub(crate) persist_doctests: Option, /// Runtool to run doctests with - pub(crate) runtool: Option, + pub(crate) test_runtool: Option, /// Arguments to pass to the runtool - pub(crate) runtool_args: Vec, - /// Whether to allow ignoring doctests on a per-target basis - /// For example, using ignore-foo to ignore running the doctest on any target that - /// contains "foo" as a substring - pub(crate) enable_per_target_ignores: bool, + pub(crate) test_runtool_args: Vec, /// Do not run doctests, compile them if should_test is active. pub(crate) no_run: bool, /// What sources are being mapped. @@ -215,9 +211,8 @@ impl fmt::Debug for Options { .field("persist_doctests", &self.persist_doctests) .field("show_coverage", &self.show_coverage) .field("crate_version", &self.crate_version) - .field("runtool", &self.runtool) - .field("runtool_args", &self.runtool_args) - .field("enable-per-target-ignores", &self.enable_per_target_ignores) + .field("test_runtool", &self.test_runtool) + .field("test_runtool_args", &self.test_runtool_args) .field("run_check", &self.run_check) .field("no_run", &self.no_run) .field("test_builder_wrappers", &self.test_builder_wrappers) @@ -779,9 +774,8 @@ impl Options { let unstable_opts_strs = matches.opt_strs("Z"); let lib_strs = matches.opt_strs("L"); let extern_strs = matches.opt_strs("extern"); - let runtool = matches.opt_str("runtool"); - let runtool_args = matches.opt_strs("runtool-arg"); - let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores"); + let test_runtool = matches.opt_str("test-runtool"); + let test_runtool_args = matches.opt_strs("test-runtool-arg"); let document_private = matches.opt_present("document-private-items"); let document_hidden = matches.opt_present("document-hidden-items"); let run_check = matches.opt_present("check"); @@ -843,9 +837,8 @@ impl Options { crate_version, test_run_directory, persist_doctests, - runtool, - runtool_args, - enable_per_target_ignores, + test_runtool, + test_runtool_args, test_builder, run_check, no_run, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 88eaa52c6deba..829a9ca6e7ddf 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -219,11 +219,9 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); let crate_attrs = tcx.hir_attrs(CRATE_HIR_ID); let opts = scrape_test_config(crate_name, crate_attrs, args_path); - let enable_per_target_ignores = options.enable_per_target_ignores; let hir_collector = HirCollector::new( ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), - enable_per_target_ignores, tcx, ); let tests = hir_collector.collect_crate(); @@ -782,10 +780,10 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = &rustdoc_options.runtool { + if let Some(tool) = &rustdoc_options.test_runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(&rustdoc_options.runtool_args); + cmd.args(&rustdoc_options.test_runtool_args); cmd.arg(&output_file); } else { cmd = Command::new(&output_file); diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a0d39ce749d83..497a8d7c4a758 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -104,13 +104,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> { }; let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - find_testable_code( - &input_str, - &mut md_collector, - codes, - options.enable_per_target_ignores, - None, - ); + find_testable_code(&input_str, &mut md_collector, codes, None); let mut collector = CreateRunnableDocTests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index be2dea641d79e..43dcfab880b5a 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -63,19 +63,18 @@ impl DocTestVisitor for RustCollector { pub(super) struct HirCollector<'tcx> { codes: ErrorCodes, tcx: TyCtxt<'tcx>, - enable_per_target_ignores: bool, collector: RustCollector, } impl<'tcx> HirCollector<'tcx> { - pub fn new(codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>) -> Self { + pub fn new(codes: ErrorCodes, tcx: TyCtxt<'tcx>) -> Self { let collector = RustCollector { source_map: tcx.sess.psess.clone_source_map(), cur_path: vec![], position: DUMMY_SP, tests: vec![], }; - Self { codes, enable_per_target_ignores, tcx, collector } + Self { codes, tcx, collector } } pub fn collect_crate(mut self) -> Vec { @@ -131,7 +130,6 @@ impl HirCollector<'_> { &doc, &mut self.collector, self.codes, - self.enable_per_target_ignores, Some(&crate::html::markdown::ExtraInfo::new(self.tcx, def_id, span)), ); } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 44134bda5ea39..fc46293e7eaac 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -246,7 +246,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { match kind { CodeBlockKind::Fenced(ref lang) => { let parse_result = - LangString::parse_without_check(lang, self.check_error_codes, false); + LangString::parse_without_check(lang, self.check_error_codes); if !parse_result.rust { let added_classes = parse_result.added_classes; let lang_string = if let Some(lang) = parse_result.unknown.first() { @@ -707,17 +707,15 @@ pub(crate) fn find_testable_code( doc: &str, tests: &mut T, error_codes: ErrorCodes, - enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, ) { - find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) + find_codes(doc, tests, error_codes, extra_info, false) } pub(crate) fn find_codes( doc: &str, tests: &mut T, error_codes: ErrorCodes, - enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, include_non_rust: bool, ) { @@ -733,12 +731,7 @@ pub(crate) fn find_codes( if lang.is_empty() { Default::default() } else { - LangString::parse( - lang, - error_codes, - enable_per_target_ignores, - extra_info, - ) + LangString::parse(lang, error_codes, extra_info) } } CodeBlockKind::Indented => Default::default(), @@ -1162,18 +1155,13 @@ impl Default for LangString { } impl LangString { - fn parse_without_check( - string: &str, - allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, - ) -> Self { - Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) + fn parse_without_check(string: &str, allow_error_code_check: ErrorCodes) -> Self { + Self::parse(string, allow_error_code_check, None) } fn parse( string: &str, allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, extra: Option<&ExtraInfo<'_>>, ) -> Self { let allow_error_code_check = allow_error_code_check.as_bool(); @@ -1203,10 +1191,8 @@ impl LangString { LangStringToken::LangToken(x) if let Some(ignore) = x.strip_prefix("ignore-") => { - if enable_per_target_ignores { - ignores.push(ignore.to_owned()); - seen_rust_tags = !seen_other_tags; - } + ignores.push(ignore.to_owned()); + seen_rust_tags = !seen_other_tags; } LangStringToken::LangToken("rust") => { data.rust = true; @@ -1968,7 +1954,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec::new(); - find_testable_code(input, &mut lines, ErrorCodes::No, false, None); + find_testable_code(input, &mut lines, ErrorCodes::No, None); assert_eq!(lines, expect); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7e17f09aecdcb..beaa6497b8caa 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2086,6 +2086,7 @@ fn render_impl( .split_summary_and_content() }) .unwrap_or((None, None)); + write!( w, "{}", @@ -2097,24 +2098,19 @@ fn render_impl( use_absolute, aliases, before_dox.as_deref(), + trait_.is_none() && impl_.items.is_empty(), ) )?; if toggled { w.write_str("")?; } - if before_dox.is_some() { - if trait_.is_none() && impl_.items.is_empty() { - w.write_str( - "
\ -
This impl block contains no items.
\ -
", - )?; - } - if let Some(after_dox) = after_dox { - write!(w, "
{after_dox}
")?; - } + if before_dox.is_some() + && let Some(after_dox) = after_dox + { + write!(w, "
{after_dox}
")?; } + if !default_impl_items.is_empty() || !impl_items.is_empty() { w.write_str("
")?; close_tags.push("
"); @@ -2182,6 +2178,7 @@ fn render_impl_summary( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], doc: Option<&str>, + impl_is_empty: bool, ) -> impl fmt::Display { fmt::from_fn(move |w| { let inner_impl = i.inner_impl(); @@ -2237,6 +2234,13 @@ fn render_impl_summary( } if let Some(doc) = doc { + if impl_is_empty { + w.write_str( + "
\ +
This impl block contains no items.
\ +
", + )?; + } write!(w, "
{doc}
")?; } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 74d23b3143f44..19ac24a5d6e3d 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -585,7 +585,7 @@ img { margin-left: -140px; border-left: none; } -.sidebar-resizer.active:before { +.sidebar-resizer.active::before { border-left: solid 2px var(--sidebar-resizer-active); display: block; height: 100%; @@ -2044,7 +2044,7 @@ button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { text-decoration: none; } -#settings-menu > a:before { +#settings-menu > a::before { /* Wheel */ content: url('data:image/svg+xml,\ @@ -2064,7 +2064,7 @@ button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { filter: var(--settings-menu-filter); } -button#toggle-all-docs:before { +button#toggle-all-docs::before { /* Custom arrow icon */ content: url('data:image/svg+xml,\ @@ -2074,14 +2074,14 @@ button#toggle-all-docs:before { filter: var(--settings-menu-filter); } -button#toggle-all-docs.will-expand:before { +button#toggle-all-docs.will-expand::before { /* Custom arrow icon */ content: url('data:image/svg+xml,\ '); } -#help-button > a:before { +#help-button > a::before { /* Question mark with circle */ content: url('data:image/svg+xml,\ @@ -2093,17 +2093,17 @@ button#toggle-all-docs.will-expand:before { filter: var(--settings-menu-filter); } -button#toggle-all-docs:before, -#help-button > a:before, -#settings-menu > a:before { +button#toggle-all-docs::before, +#help-button > a::before, +#settings-menu > a::before { filter: var(--settings-menu-filter); margin: 8px; } @media not (pointer: coarse) { - button#toggle-all-docs:hover:before, - #help-button > a:hover:before, - #settings-menu > a:hover:before { + button#toggle-all-docs:hover::before, + #help-button > a:hover::before, + #settings-menu > a:hover::before { filter: var(--settings-menu-hover-filter); } } @@ -2125,7 +2125,7 @@ rustdoc-toolbar span.label { padding-bottom: 4px; } -#sidebar-button > a:before { +#sidebar-button > a::before { /* sidebar resizer image */ content: url('data:image/svg+xml,\ @@ -2319,7 +2319,10 @@ details.toggle > summary:not(.hideme)::before { doc block while aligning it with the impl block items. */ .implementors-toggle > .docblock, /* We indent trait items as well. */ -#main-content > .methods > :not(.item-info) { +#main-content > .methods > :not(.item-info), +.impl > .item-info, +.impl > .docblock, +.impl + .docblock { margin-left: var(--impl-items-indent); } @@ -2400,21 +2403,21 @@ However, it's not needed with smaller screen width because the doc/code block is /* sidebar button opens modal use hamburger button */ -.src #sidebar-button > a:before, .sidebar-menu-toggle:before { +.src #sidebar-button > a::before, .sidebar-menu-toggle::before { /* hamburger button image */ content: url('data:image/svg+xml,\ '); opacity: 0.75; } -.sidebar-menu-toggle:hover:before, -.sidebar-menu-toggle:active:before, -.sidebar-menu-toggle:focus:before { +.sidebar-menu-toggle:hover::before, +.sidebar-menu-toggle:active::before, +.sidebar-menu-toggle:focus::before { opacity: 1; } /* src sidebar button opens a folder view */ -.src #sidebar-button > a:before { +.src #sidebar-button > a::before { /* folder image */ content: url('data:image/svg+xml,\ @@ -2599,7 +2602,7 @@ in src-script.js and main.js } /* sidebar button becomes topbar button */ - #sidebar-button > a:before { + #sidebar-button > a::before { content: url('data:image/svg+xml,\ \ @@ -2608,7 +2611,7 @@ in src-script.js and main.js width: 22px; height: 22px; } - .sidebar-menu-toggle:before { + .sidebar-menu-toggle::before { filter: var(--mobile-sidebar-menu-filter); } .sidebar-menu-toggle:hover { @@ -3287,7 +3290,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) } :root[data-theme="ayu"] #settings-menu > a img, -:root[data-theme="ayu"] #sidebar-button > a:before { +:root[data-theme="ayu"] #sidebar-button > a::before { filter: invert(100); } /* End theme: ayu */ diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 4fe5e13c3afe0..44bd96a7e4503 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -507,28 +507,20 @@ fn opts() -> Vec { "", ), opt( - Unstable, - FlagMulti, - "", - "enable-per-target-ignores", - "parse ignore-foo for ignoring doctests on a per-target basis", - "", - ), - opt( - Unstable, + Stable, Opt, "", - "runtool", + "test-runtool", "", "The tool to run tests with when building for a different target than host", ), opt( - Unstable, + Stable, Multi, "", - "runtool-arg", + "test-runtool-arg", "", - "One (of possibly many) arguments to pass to the runtool", + "One argument (of possibly many) to pass to the runtool", ), opt( Unstable, diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 761282bde7c30..c9f0baaaa4c1b 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -212,7 +212,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { let has_docs = !i.attrs.doc_strings.is_empty(); let mut tests = Tests { found_tests: 0 }; - find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None); + find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, None); let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 70dbb944d4c0a..8028afea363d5 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -122,7 +122,7 @@ pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) { let mut tests = Tests { found_tests: 0 }; - find_testable_code(dox, &mut tests, ErrorCodes::No, false, None); + find_testable_code(dox, &mut tests, ErrorCodes::No, None); if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples() { if should_have_doc_example(cx, item) { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 297597b3deacc..36f5889dcf4ea 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -59,7 +59,12 @@ fn filter_assoc_items_by_name_and_namespace( ident: Ident, ns: Namespace, ) -> impl Iterator { - tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| { + let iter: Box> = if !ident.name.is_empty() { + Box::new(tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name)) + } else { + Box::new([].iter()) + }; + iter.filter(move |item| { item.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of) }) } diff --git a/src/stage0 b/src/stage0 index 6e86501a72ab4..06080e3a8c161 100644 --- a/src/stage0 +++ b/src/stage0 @@ -2,7 +2,6 @@ dist_server=https://static.rust-lang.org artifacts_server=https://ci-artifacts.rust-lang.org/rustc-builds artifacts_with_llvm_assertions_server=https://ci-artifacts.rust-lang.org/rustc-builds-alt git_merge_commit_email=bors@rust-lang.org -git_repository=rust-lang/rust nightly_branch=master # The configuration above this comment is editable, and can be changed diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index d679084ae44bc..680437cce4faa 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -61,7 +61,6 @@ impl Tool { artifacts_server, artifacts_with_llvm_assertions_server, git_merge_commit_email, - git_repository, nightly_branch, } = &self.config; @@ -72,7 +71,6 @@ impl Tool { artifacts_with_llvm_assertions_server )); file_content.push_str(&format!("git_merge_commit_email={}\n", git_merge_commit_email)); - file_content.push_str(&format!("git_repository={}\n", git_repository)); file_content.push_str(&format!("nightly_branch={}\n", nightly_branch)); file_content.push_str("\n"); diff --git a/src/tools/clippy/.github/workflows/clippy_changelog.yml b/src/tools/clippy/.github/workflows/clippy_changelog.yml index a2657bfea490d..1e97154bf8a34 100644 --- a/src/tools/clippy/.github/workflows/clippy_changelog.yml +++ b/src/tools/clippy/.github/workflows/clippy_changelog.yml @@ -24,7 +24,7 @@ jobs: - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ + body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \ python -c "import sys, json; print(json.load(sys.stdin)['body'])") output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") if [ -z "$output" ]; then diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index 741e745733177..07d5a08304e87 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -66,7 +66,7 @@ jobs: run: cargo test --features internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features internal + run: cargo test working-directory: clippy_lints - name: Test clippy_utils diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index 80523d91f4fc8..880ebd6e5d5c7 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -42,7 +42,7 @@ jobs: run: cargo test --features internal - name: Test clippy_lints - run: cargo test --features internal + run: cargo test working-directory: clippy_lints - name: Test clippy_utils diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index b42f3e7712f10..ede19c11257ee 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -8,6 +8,10 @@ on: tags: - rust-1.** +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + env: TARGET_BRANCH: 'gh-pages' SHA: '${{ github.sha }}' diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index d487c7d949857..70c805903d36e 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -66,7 +66,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml + run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -97,7 +97,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml + run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 1bf4b51ff0fe2..2b62c9a59aa5d 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,68 @@ document. ## Unreleased / Beta / In Rust Nightly -[609cd310...master](https://github.com/rust-lang/rust-clippy/compare/609cd310...master) +[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master) + +## Rust 1.86 + +Current stable, released 2025-04-03 + +[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-12-27T15%3A11%3A38Z..2025-02-06T13%3A57%3A58Z+base%3Amaster) + +### New Lints + +* Added [`unneeded_struct_pattern`] to `style` [#13465](https://github.com/rust-lang/rust-clippy/pull/13465) +* Added [`doc_overindented_list_items`] to `style` [#13711](https://github.com/rust-lang/rust-clippy/pull/13711) +* Added [`manual_ok_err`] to `complexity` [#13740](https://github.com/rust-lang/rust-clippy/pull/13740) +* Added [`non_std_lazy_statics`] to `pedantic` [#13770](https://github.com/rust-lang/rust-clippy/pull/13770) +* Added [`manual_repeat_n`] to `style` [#13858](https://github.com/rust-lang/rust-clippy/pull/13858) +* Added [`manual_option_as_slice`] to `complexity` [#13901](https://github.com/rust-lang/rust-clippy/pull/13901) +* Added [`double_ended_iterator_last`] to `perf` [#13922](https://github.com/rust-lang/rust-clippy/pull/13922) +* Added [`useless_nonzero_new_unchecked`] to `complexity` [#13993](https://github.com/rust-lang/rust-clippy/pull/13993) +* Added [`sliced_string_as_bytes`] to `perf` [#14002](https://github.com/rust-lang/rust-clippy/pull/14002) +* Added [`unnecessary_semicolon`] to `pedantic` [#14032](https://github.com/rust-lang/rust-clippy/pull/14032) +* Added [`return_and_then`] to `restriction` [#14051](https://github.com/rust-lang/rust-clippy/pull/14051) +* Added [`manual_slice_fill`] to `style` [#14082](https://github.com/rust-lang/rust-clippy/pull/14082) +* Added [`precedence_bits`] to `restriction` [#14115](https://github.com/rust-lang/rust-clippy/pull/14115) + +### Moves and Deprecations + +* Moved [`redundant_locals`] to `suspicious` (from `correctness`, now warn-by-default) + [#13747](https://github.com/rust-lang/rust-clippy/pull/13747) +* Moved [`format_push_string`] to `pedantic` (from `restriction`) + [#13894](https://github.com/rust-lang/rust-clippy/pull/13894) +* Moved [`format_collect`] to `pedantic` (from `perf`, now allow-by-default) + [#13894](https://github.com/rust-lang/rust-clippy/pull/13894) +* Moved [`mutex_integer`] to `restriction` (from `nursery`) [#14110](https://github.com/rust-lang/rust-clippy/pull/14110) + +### Enhancements + +* Add `lint-inconsistent-struct-field-initializers` configuration option to [`inconsistent_struct_constructor`] + [#13737](https://github.com/rust-lang/rust-clippy/pull/13737) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`obfuscated_if_else`] now also triggers for the `.then(..).unwrap_or(..)` pattern + [#14021](https://github.com/rust-lang/rust-clippy/pull/14021) + +### False Positive Fixes + +* [`trailing_empty_array`] no longer triggers in tests [#13844](https://github.com/rust-lang/rust-clippy/pull/13844) +* [`missing_const_for_fn`] no longer triggers in tests [#13945](https://github.com/rust-lang/rust-clippy/pull/13945) +* [`significant_drop_in_scrutinee`]: do not falsely warn for temporaries created by `.await` expansion + [#13985](https://github.com/rust-lang/rust-clippy/pull/13985) + +### ICE Fixes + +* [`borrow_interior_mutable_const`] Fix an ICE that can occur when taking a reference to a tuple/`struct` field of an + interior mutable `const` [#13877](https://github.com/rust-lang/rust-clippy/pull/13877) + +### Others + +* Clippy now uses Rust edition 2024 [#13751](https://github.com/rust-lang/rust-clippy/pull/13751) ## Rust 1.85 -Current stable, released 2025-02-20 +Released 2025-02-20 [View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-11-15T19%3A31%3A08Z..2024-12-26T13%3A59%3A48Z+base%3Amaster) @@ -5516,6 +5573,7 @@ Released 2018-09-13 [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes [`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts [`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test +[`char_indices_as_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_indices_as_byte_indices [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp @@ -5681,6 +5739,7 @@ Released 2018-09-13 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`ignore_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignore_without_reason [`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns [`impl_hash_borrow_with_str_and_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_hash_borrow_with_str_and_bytes [`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params @@ -5783,12 +5842,14 @@ Released 2018-09-13 [`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion +[`manual_abs_diff`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains +[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr [`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map @@ -6055,6 +6116,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes +[`redundant_test_prefix`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_test_prefix [`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations [`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference @@ -6156,6 +6218,7 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref +[`swap_with_temporary`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_with_temporary [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr @@ -6346,6 +6409,7 @@ Released 2018-09-13 [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests +[`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros @@ -6362,7 +6426,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold -[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers +[`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index f5a8e3dc387dc..1cfc1c196c0b6 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" @@ -27,6 +27,7 @@ clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } clippy_utils = { path = "clippy_utils" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } +clippy_lints_internal = { path = "clippy_lints_internal", optional = true } tempfile = { version = "3.3", optional = true } termize = "0.1" color-print = "0.3.4" @@ -43,7 +44,7 @@ walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } -rinja = { version = "0.3", default-features = false, features = ["config"] } +askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies clippy_utils = { path = "clippy_utils" } @@ -58,8 +59,8 @@ tokio = { version = "1", features = ["io-util"] } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } [features] -integration = ["tempfile"] -internal = ["clippy_lints/internal", "tempfile"] +integration = ["dep:tempfile"] +internal = ["dep:clippy_lints_internal", "dep:tempfile"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/src/tools/clippy/rinja.toml b/src/tools/clippy/askama.toml similarity index 100% rename from src/tools/clippy/rinja.toml rename to src/tools/clippy/askama.toml diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md index 19328fdd3cd47..39fe7358ed87a 100644 --- a/src/tools/clippy/book/src/SUMMARY.md +++ b/src/tools/clippy/book/src/SUMMARY.md @@ -30,6 +30,7 @@ - [Updating the Changelog](development/infrastructure/changelog_update.md) - [Release a New Version](development/infrastructure/release.md) - [The Clippy Book](development/infrastructure/book.md) + - [Benchmarking Clippy](development/infrastructure/benchmarking.md) - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index 0b9010f01071f..e5e82ede4fdf9 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -99,6 +99,7 @@ struct A; impl A { pub fn fo(&self) {} pub fn foo(&self) {} + //~^ foo_functions pub fn food(&self) {} } @@ -106,12 +107,14 @@ impl A { trait B { fn fo(&self) {} fn foo(&self) {} + //~^ foo_functions fn food(&self) {} } // Plain functions fn fo() {} fn foo() {} +//~^ foo_functions fn food() {} fn main() { @@ -122,17 +125,24 @@ fn main() { } ``` -Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently -this test is meaningless though. +Note that we are adding comment annotations with the name of our lint to mark +lines where we expect an error. Except for very specific situations +(`//@check-pass`), at least one error marker must be present in a test file for +it to be accepted. + +Once we have implemented our lint we can run `TESTNAME=foo_functions cargo +uibless` to generate the `.stderr` file. If our lint makes use of structured +suggestions then this command will also generate the corresponding `.fixed` +file. While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want by checking the `.stderr` file that gets updated on every test run. -Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we -commit our lint, we need to commit the generated `.stderr` files, too. In -general, you should only commit files changed by `cargo bless` for the -specific lint you are creating/editing. +Once we have implemented our lint running `TESTNAME=foo_functions cargo uitest` +should pass on its own. When we commit our lint, we need to commit the generated + `.stderr` and if applicable `.fixed` files, too. In general, you should only + commit files changed by `cargo bless` for the specific lint you are creating/editing. > _Note:_ you can run multiple test files by specifying a comma separated list: > `TESTNAME=foo_functions,test2,test3`. diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index 931e5c3a2942a..4219724ed5df9 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -147,7 +147,7 @@ following: First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in -`/rust-toolchain`. We will use this override to install Clippy into the right +`/rust-toolchain.toml`. We will use this override to install Clippy into the right toolchain. > Tip: You can view the active toolchain for the current directory with `rustup diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 051febc2ca5da..2e39f279eae42 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -159,19 +159,21 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::return_ty; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { // Check if item is a method/function if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - && impl_item.ident.name.as_str() == "some_method" + // + // Add `some_method` to `clippy_utils::sym` if it's not already there + && impl_item.ident.name == sym::some_method // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + && is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) { // ... } diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index 169cecd7d11dc..cb6d7b740dbd1 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -9,7 +9,7 @@ lint involves some boilerplate code. A lint type is the category of items and expressions in which your lint focuses on. -As of the writing of this documentation update, there are 12 _types_ of lints +As of the writing of this documentation update, there are 11 _types_ of lints besides the numerous standalone lints living under `clippy_lints/src/`: - `cargo` @@ -23,7 +23,6 @@ besides the numerous standalone lints living under `clippy_lints/src/`: - `transmute` - `types` - `unit_types` -- `utils / internal` (Clippy internal lints) These types group together lints that share some common behaviors. For instance, `functions` groups together lints that deal with some aspects of functions in diff --git a/src/tools/clippy/book/src/development/infrastructure/benchmarking.md b/src/tools/clippy/book/src/development/infrastructure/benchmarking.md new file mode 100644 index 0000000000000..a3ebce922f6c9 --- /dev/null +++ b/src/tools/clippy/book/src/development/infrastructure/benchmarking.md @@ -0,0 +1,55 @@ +# Benchmarking Clippy + +Benchmarking Clippy is similar to using our Lintcheck tool, in fact, it even +uses the same tool! Just by adding a `--perf` flag it will transform Lintcheck +into a very simple but powerful benchmarking tool! + +It requires having the [`perf` tool][perf] installed, as `perf` is what's actually +profiling Clippy under the hood. + +The lintcheck `--perf` tool generates a series of `perf.data` in the +`target/lintcheck/sources/-` directories. Each `perf.data` +corresponds to the package which is contained. + +Lintcheck uses the `-g` flag, meaning that you can get stack traces for richer +analysis, including with tools such as [flamegraph][flamegraph-perf] +(or [`flamegraph-rs`][flamegraph-rs]). + +Currently, we only measure instruction count, as it's the most reproducible metric +and [rustc-perf][rustc-perf] also considers it the main number to focus on. + +## Benchmarking a PR + +Having a benchmarking tool directly implemented into lintcheck gives us the +ability to benchmark any given PR just by making a before and after + +Here's the way you can get into any PR, benchmark it, and then benchmark +`master`. + +The first `perf.data` will not have any numbers appended, but any subsequent +benchmark will be written to `perf.data.number` with a number growing for 0. +All benchmarks are compressed so that you can + +```bash +git fetch upstream pull//head: +git switch BRANCHNAME + +# Bench +cargo lintcheck --perf + +# Get last common commit, checkout that +LAST_COMMIT=$(git log BRANCHNAME..master --oneline | tail -1 | cut -c 1-11) +git switch -c temporary $LAST_COMMIT + +# We're now on master + +# Bench +cargo lintcheck --perf +perf diff ./target/lintcheck/sources/CRATE/perf.data ./target/lintcheck/sources/CRATE/perf.data.0 +``` + + +[perf]: https://perfwiki.github.io/main/ +[flamegraph-perf]: https://github.com/brendangregg/FlameGraph +[flamegraph-rs]: https://github.com/flamegraph-rs/flamegraph +[rustc-perf]: https://github.com/rust-lang/rustc-perf diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md index 8b080c099b81c..a429e0d953c46 100644 --- a/src/tools/clippy/book/src/development/infrastructure/release.md +++ b/src/tools/clippy/book/src/development/infrastructure/release.md @@ -88,9 +88,6 @@ git push upstream stable After updating the `stable` branch, tag the HEAD commit and push it to the Clippy repo. -> Note: Only push the tag once the Deploy GitHub action of the `beta` branch is -> finished. Otherwise the deploy for the tag might fail. - ```bash git tag rust-1.XX.0 # XX should be exchanged with the corresponding version git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md index da1ad586607f9..2bbdf47a83581 100644 --- a/src/tools/clippy/book/src/development/infrastructure/sync.md +++ b/src/tools/clippy/book/src/development/infrastructure/sync.md @@ -86,7 +86,7 @@ to be run inside the `rust` directory): 4. Bump the nightly version in the Clippy repository by running these commands: ```bash cargo dev sync update_nightly - git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain clippy_utils/README.md + git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain.toml clippy_utils/README.md ``` 5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or diff --git a/src/tools/clippy/book/src/development/writing_tests.md b/src/tools/clippy/book/src/development/writing_tests.md index d4cca2a72f0be..1da54322fcf72 100644 --- a/src/tools/clippy/book/src/development/writing_tests.md +++ b/src/tools/clippy/book/src/development/writing_tests.md @@ -41,20 +41,23 @@ Update the file with some examples to get started: struct A; impl A { pub fn fo(&self) {} - pub fn foo(&self) {} //~ ERROR: function called "foo" + pub fn foo(&self) {} + //~^ foo_functions pub fn food(&self) {} } // Default trait methods trait B { fn fo(&self) {} - fn foo(&self) {} //~ ERROR: function called "foo" + fn foo(&self) {} + //~^ foo_functions fn food(&self) {} } // Plain functions fn fo() {} -fn foo() {} //~ ERROR: function called "foo" +fn foo() {} +//~^ foo_functions fn food() {} fn main() { @@ -66,19 +69,38 @@ fn main() { ``` Without actual lint logic to emit the lint when we see a `foo` function name, -this test will just pass, because no lint will be emitted. However, we can now -run the test with the following command: +this test will fail, because we expect errors at lines marked with +`//~^ foo_functions`. However, we can now run the test with the following command: ```sh $ TESTNAME=foo_functions cargo uitest ``` -Clippy will compile and it will conclude with an `ok` for the tests: +Clippy will compile and it will fail complaining it didn't receive any errors: ``` ...Clippy warnings and test outputs... -test compile_test ... ok -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s +error: diagnostic code `clippy::foo_functions` not found on line 8 + --> tests/ui/foo_functions.rs:9:10 + | +9 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + +error: diagnostic code `clippy::foo_functions` not found on line 16 + --> tests/ui/foo_functions.rs:17:10 + | +17 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + +error: diagnostic code `clippy::foo_functions` not found on line 23 + --> tests/ui/foo_functions.rs:24:6 + | +24 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + ``` This is normal. After all, we wrote a bunch of Rust code but we haven't really diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 3726d6e8a8691..2314d1beac7e0 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -425,6 +425,33 @@ Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. * [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv) +## `check-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `check-private-items` Whether to also run the listed lints on private items. @@ -613,31 +640,15 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) -## `lint-inconsistent-struct-field-initializers` -Whether to suggest reordering constructor fields when initializers are present. - -Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the -suggested code would compile, it can change semantics if the initializer expressions have side effects. The -following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: - -```rust -struct MyStruct { - vector: Vec, - length: usize -} -fn main() { - let vector = vec![1,2,3]; - MyStruct { length: vector.len(), vector}; -} -``` - -[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 +## `lint-commented-code` +Whether collapsible `if` chains are linted if they contain comments inside the parts +that would be collapsed. **Default Value:** `false` --- **Affected lints:** -* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) +* [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if) ## `literal-representation-threshold` @@ -786,6 +797,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) * [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok) +* [`manual_abs_diff`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff) * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) * [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals) * [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) @@ -793,6 +805,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten) * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [`manual_is_power_of_two`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two) * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint) * [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) @@ -1059,7 +1072,8 @@ The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' ## `warn-on-all-wildcard-imports` -Whether to allow certain wildcard imports (prelude, super in tests). +Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, +or for `pub use` reexports. **Default Value:** `false` diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index f4789c9d03035..0a7724bbe4e61 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1,15 +1,20 @@ avoid-breaking-exported-api = false -lint-inconsistent-struct-field-initializers = true +check-inconsistent-struct-field-initializers = true + +lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +allow-invalid = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +allow-invalid = true [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" +allow-invalid = true diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 934725fccb8ec..93fd2e35d1ba5 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 798f8b3aa5a90..511cb84527d80 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -120,12 +120,7 @@ impl ConfError { Self { message: message.into(), suggestion, - span: Span::new( - file.start_pos + BytePos::from_usize(span.start), - file.start_pos + BytePos::from_usize(span.end), - SyntaxContext::root(), - None, - ), + span: span_from_toml_range(file, span), } } } @@ -176,11 +171,61 @@ macro_rules! default_text { }; } +macro_rules! deserialize { + ($map:expr, $ty:ty, $errors:expr, $file:expr) => {{ + let raw_value = $map.next_value::>()?; + let value_span = raw_value.span(); + let value = match <$ty>::deserialize(raw_value.into_inner()) { + Err(e) => { + $errors.push(ConfError::spanned( + $file, + e.to_string().replace('\n', " ").trim(), + None, + value_span, + )); + continue; + }, + Ok(value) => value, + }; + (value, value_span) + }}; + + ($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{ + let array = $map.next_value::>>()?; + let mut disallowed_paths_span = Range { + start: usize::MAX, + end: usize::MIN, + }; + let mut disallowed_paths = Vec::new(); + for raw_value in array { + let value_span = raw_value.span(); + let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner()) + { + Err(e) => { + $errors.push(ConfError::spanned( + $file, + e.to_string().replace('\n', " ").trim(), + None, + value_span, + )); + continue; + }, + Ok(disallowed_path) => disallowed_path, + }; + disallowed_paths_span = union(&disallowed_paths_span, &value_span); + disallowed_path.set_span(span_from_toml_range($file, value_span)); + disallowed_paths.push(disallowed_path); + } + (disallowed_paths, disallowed_paths_span) + }}; +} + macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? + $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -218,42 +263,46 @@ macro_rules! define_Conf { let mut value_spans = HashMap::new(); let mut errors = Vec::new(); let mut warnings = Vec::new(); + + // Declare a local variable for each field available to a configuration file. $(let mut $name = None;)* + // could get `Field` here directly, but get `String` first for diagnostics while let Some(name) = map.next_key::>()? { - match Field::deserialize(name.get_ref().as_str().into_deserializer()) { + let field = match Field::deserialize(name.get_ref().as_str().into_deserializer()) { Err(e) => { let e: FieldError = e; errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span())); + continue; } - $(Ok(Field::$name) => { + Ok(field) => field + }; + + match field { + $(Field::$name => { + // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let raw_value = map.next_value::>()?; - let value_span = raw_value.span(); - match <$ty>::deserialize(raw_value.into_inner()) { - Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)), - Ok(value) => match $name { - Some(_) => { - errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); - } - None => { - $name = Some(value); - value_spans.insert(name.get_ref().as_str().to_string(), value_span); - // $new_conf is the same as one of the defined `$name`s, so - // this variable is defined in line 2 of this function. - $(match $new_conf { - Some(_) => errors.push(ConfError::spanned(self.0, concat!( - "duplicate field `", stringify!($new_conf), - "` (provided as `", stringify!($name), "`)" - ), None, name.span())), - None => $new_conf = $name.clone(), - })? - }, - } + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); + // Was this field set previously? + if $name.is_some() { + errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); + continue; } + $name = Some(value); + value_spans.insert(name.get_ref().as_str().to_string(), value_span); + // If this is a deprecated field, was the new field (`$new_conf`) set previously? + // Note that `$new_conf` is one of the defined `$name`s. + $(match $new_conf { + Some(_) => errors.push(ConfError::spanned(self.0, concat!( + "duplicate field `", stringify!($new_conf), + "` (provided as `", stringify!($name), "`)" + ), None, name.span())), + None => $new_conf = $name.clone(), + })? })* // ignore contents of the third_party key - Ok(Field::third_party) => drop(map.next_value::()) + Field::third_party => drop(map.next_value::()) } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; @@ -275,6 +324,22 @@ macro_rules! define_Conf { }; } +fn union(x: &Range, y: &Range) -> Range { + Range { + start: cmp::min(x.start, y.start), + end: cmp::max(x.end, y.end), + } +} + +fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(span.start), + file.start_pos + BytePos::from_usize(span.end), + SyntaxContext::root(), + None, + ) +} + define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -461,6 +526,7 @@ define_Conf! { )] avoid_breaking_exported_api: bool = true, /// The list of types which may not be held across an await point. + #[disallowed_paths_allow_replacements = false] #[lints(await_holding_invalid_type)] await_holding_invalid_types: Vec = Vec::new(), /// DEPRECATED LINT: BLACKLISTED_NAME. @@ -474,6 +540,26 @@ define_Conf! { /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + check_inconsistent_struct_field_initializers: bool = false, /// Whether to also run the listed lints on private items. #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] check_private_items: bool = false, @@ -486,9 +572,11 @@ define_Conf! { #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] cyclomatic_complexity_threshold: u64 = 25, /// The list of disallowed macros, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_macros)] disallowed_macros: Vec = Vec::new(), /// The list of disallowed methods, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_methods)] disallowed_methods: Vec = Vec::new(), /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value @@ -497,6 +585,7 @@ define_Conf! { #[lints(disallowed_names)] disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), /// The list of disallowed types, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_types)] disallowed_types: Vec = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value @@ -549,25 +638,15 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether collapsible `if` chains are linted if they contain comments inside the parts + /// that would be collapsed. + #[lints(collapsible_if)] + lint_commented_code: bool = false, /// Whether to suggest reordering constructor fields when initializers are present. + /// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers /// - /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the - /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The - /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: - /// - /// ```rust - /// struct MyStruct { - /// vector: Vec, - /// length: usize - /// } - /// fn main() { - /// let vector = vec![1,2,3]; - /// MyStruct { length: vector.len(), vector}; - /// } - /// ``` - /// - /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 - #[lints(inconsistent_struct_constructor)] + /// Use the `check-inconsistent-struct-field-initializers` configuration instead. + #[conf_deprecated("Please use `check-inconsistent-struct-field-initializers` instead", check_inconsistent_struct_field_initializers)] lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] @@ -635,6 +714,7 @@ define_Conf! { iter_kv_map, legacy_numeric_constants, lines_filter_map_ok, + manual_abs_diff, manual_bits, manual_c_str_literals, manual_clamp, @@ -642,6 +722,7 @@ define_Conf! { manual_flatten, manual_hash_one, manual_is_ascii_check, + manual_is_power_of_two, manual_let_else, manual_midpoint, manual_non_exhaustive, @@ -760,7 +841,8 @@ define_Conf! { /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' #[lints(verbose_bit_mask)] verbose_bit_mask_threshold: u64 = 1, - /// Whether to allow certain wildcard imports (prelude, super in tests). + /// Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, + /// or for `pub use` reexports. #[lints(wildcard_imports)] warn_on_all_wildcard_imports: bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. @@ -981,7 +1063,23 @@ impl serde::de::Error for FieldError { // set and allows it. use fmt::Write; - let mut expected = expected.to_vec(); + let metadata = get_configuration_metadata(); + let deprecated = metadata + .iter() + .filter_map(|conf| { + if conf.deprecation_reason.is_some() { + Some(conf.name.as_str()) + } else { + None + } + }) + .collect::>(); + + let mut expected = expected + .iter() + .copied() + .filter(|name| !deprecated.contains(name)) + .collect::>(); expected.sort_unstable(); let (rows, column_widths) = calculate_dimensions(&expected); @@ -1064,7 +1162,13 @@ mod tests { fn configs_are_tested() { let mut names: HashSet = crate::get_configuration_metadata() .into_iter() - .map(|meta| meta.name.replace('_', "-")) + .filter_map(|meta| { + if meta.deprecation_reason.is_none() { + Some(meta.name.replace('_', "-")) + } else { + None + } + }) .collect(); let toml_files = WalkDir::new("../tests") diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index 5d6e8b875166c..c227b8900b74a 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -13,6 +13,7 @@ rustc::untranslatable_diagnostic )] +extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_middle; diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 8faac9ecffea4..5949eaca7bc1d 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -1,5 +1,7 @@ -use clippy_utils::def_path_def_ids; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; +use rustc_hir::PrimTy; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -21,6 +23,17 @@ pub struct DisallowedPath { path: String, reason: Option, replacement: Option, + /// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing + /// definition. + /// + /// This could be useful when conditional compilation is used, or when a clippy.toml file is + /// shared among multiple projects. + allow_invalid: bool, + /// The span of the `DisallowedPath`. + /// + /// Used for diagnostics. + #[serde(skip_serializing)] + span: Span, } impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath { @@ -36,6 +49,8 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, replacement: Option, + #[serde(rename = "allow-invalid")] + allow_invalid: Option, }, } @@ -58,7 +75,7 @@ impl DisallowedPath { &self.path } - pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> { + pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) { move |diag| { if let Some(replacement) = &self.replacement { diag.span_suggestion( @@ -72,6 +89,14 @@ impl DisallowedPath { } } } + + pub fn span(&self) -> Span { + self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } } impl DisallowedPathEnum { @@ -94,20 +119,87 @@ impl DisallowedPathEnum { Self::Simple(_) => None, } } + + fn allow_invalid(&self) -> bool { + match &self { + Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(), + Self::Simple(_) => false, + } + } } /// Creates a map of disallowed items to the reason they were disallowed. +#[allow(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, - disallowed: &'static [DisallowedPath], -) -> DefIdMap<(&'static str, &'static DisallowedPath)> { - disallowed - .iter() - .map(|x| (x.path(), x.path().split("::").collect::>(), x)) - .flat_map(|(name, path, disallowed_path)| { - def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path))) - }) - .collect() + disallowed_paths: &'static [DisallowedPath], + def_kind_predicate: impl Fn(DefKind) -> bool, + predicate_description: &str, + allow_prim_tys: bool, +) -> ( + DefIdMap<(&'static str, &'static DisallowedPath)>, + FxHashMap)>, +) { + let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath)> = DefIdMap::default(); + let mut prim_tys: FxHashMap)> = + FxHashMap::default(); + for disallowed_path in disallowed_paths { + let path = disallowed_path.path(); + let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::>()); + + let mut found_def_id = None; + let mut found_prim_ty = false; + resolutions.retain(|res| match res { + Res::Def(def_kind, def_id) => { + found_def_id = Some(*def_id); + def_kind_predicate(*def_kind) + }, + Res::PrimTy(_) => { + found_prim_ty = true; + allow_prim_tys + }, + _ => false, + }); + + if resolutions.is_empty() { + let span = disallowed_path.span(); + + if let Some(def_id) = found_def_id { + tcx.sess.dcx().span_warn( + span, + format!( + "expected a {predicate_description}, found {} {}", + tcx.def_descr_article(def_id), + tcx.def_descr(def_id) + ), + ); + } else if found_prim_ty { + tcx.sess.dcx().span_warn( + span, + format!("expected a {predicate_description}, found a primitive type",), + ); + } else if !disallowed_path.allow_invalid { + tcx.sess.dcx().span_warn( + span, + format!("`{path}` does not refer to an existing {predicate_description}"), + ); + } + } + + for res in resolutions { + match res { + Res::Def(_, def_id) => { + def_ids.insert(def_id, (path, disallowed_path)); + }, + Res::PrimTy(ty) => { + prim_tys.insert(ty, (path, disallowed_path)); + }, + _ => unreachable!(), + } + } + } + + (def_ids, prim_tys) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 9280369c23b8f..c1ffaf269c6fe 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -13,6 +13,7 @@ #[allow(unused_extern_crates)] extern crate rustc_driver; extern crate rustc_lexer; +extern crate rustc_literal_escaper; pub mod dogfood; pub mod fmt; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 074dea4ab77b6..83f8e66b33476 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -170,7 +170,6 @@ enum DevCommand { "restriction", "cargo", "nursery", - "internal", ], default_value = "nursery", )] @@ -334,7 +333,7 @@ struct SyncCommand { #[derive(Subcommand)] enum SyncSubcommand { #[command(name = "update_nightly")] - /// Update nightly version in rust-toolchain and `clippy_utils` + /// Update nightly version in `rust-toolchain.toml` and `clippy_utils` UpdateNightly, } diff --git a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs index 2966629cf70a3..ecd80215f7e8f 100644 --- a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs +++ b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs @@ -62,7 +62,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`"); if !standalone { - println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes"); + println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain.toml` changes"); } } diff --git a/src/tools/clippy/clippy_dev/src/sync.rs b/src/tools/clippy/clippy_dev/src/sync.rs index 3522d182e90ac..a6b65e561c223 100644 --- a/src/tools/clippy/clippy_dev/src/sync.rs +++ b/src/tools/clippy/clippy_dev/src/sync.rs @@ -10,7 +10,7 @@ pub fn update_nightly() { let date = Utc::now().format("%Y-%m-%d").to_string(); replace_region_in_file( UpdateMode::Change, - Path::new("rust-toolchain"), + Path::new("rust-toolchain.toml"), "# begin autogenerated nightly\n", "# end autogenerated nightly", |res| { diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index b80ee5aac7e76..d848a97f86d2f 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,7 +1,8 @@ use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file}; use aho_corasick::AhoCorasickBuilder; use itertools::Itertools; -use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape}; +use rustc_lexer::{LiteralKind, TokenKind, tokenize}; +use rustc_literal_escaper::{Mode, unescape_unicode}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; @@ -37,9 +38,8 @@ fn generate_lint_files( deprecated_lints: &[DeprecatedLint], renamed_lints: &[RenamedLint], ) { - let internal_lints = Lint::internal_lints(lints); - let mut usable_lints = Lint::usable_lints(lints); - usable_lints.sort_by_key(|lint| lint.name.clone()); + let mut lints = lints.to_owned(); + lints.sort_by_key(|lint| lint.name.clone()); replace_region_in_file( update_mode, @@ -47,7 +47,7 @@ fn generate_lint_files( "[There are over ", " lints included in this crate!]", |res| { - write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); + write!(res, "{}", round_to_fifty(lints.len())).unwrap(); }, ); @@ -57,7 +57,7 @@ fn generate_lint_files( "[There are over ", " lints included in this crate!]", |res| { - write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); + write!(res, "{}", round_to_fifty(lints.len())).unwrap(); }, ); @@ -67,7 +67,7 @@ fn generate_lint_files( "\n", "", |res| { - for lint in usable_lints + for lint in lints .iter() .map(|l| &*l.name) .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) @@ -86,7 +86,7 @@ fn generate_lint_files( "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", "// end lints modules, do not remove this comment, it’s used in `update_lints`", |res| { - for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { + for lint_mod in lints.iter().map(|l| &l.module).unique().sorted() { writeln!(res, "mod {lint_mod};").unwrap(); } }, @@ -95,7 +95,7 @@ fn generate_lint_files( process_file( "clippy_lints/src/declared_lints.rs", update_mode, - &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), + &gen_declared_lints(lints.iter()), ); let content = gen_deprecated_lints_test(deprecated_lints); @@ -106,10 +106,9 @@ fn generate_lint_files( } pub fn print_lints() { - let (lint_list, _, _) = gather_all(); - let usable_lints = Lint::usable_lints(&lint_list); - let usable_lint_count = usable_lints.len(); - let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); + let (lints, _, _) = gather_all(); + let lint_count = lints.len(); + let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { println!("\n## {lint_group}"); @@ -121,7 +120,7 @@ pub fn print_lints() { } } - println!("there are {usable_lint_count} lints"); + println!("there are {lint_count} lints"); } /// Runs the `rename_lint` command. @@ -402,53 +401,53 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io } } - if path.exists() { - if let Some(lint) = lints.iter().find(|l| l.name == name) { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration - let _ = fs::remove_file(lint_mod_path); - } + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + let _ = fs::remove_file(lint_mod_path); + } - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name - ); + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); + assert!( + content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range.clone(), ""); - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); } Ok(false) @@ -527,22 +526,6 @@ impl Lint { } } - /// Returns all non-deprecated lints and non-internal lints - #[must_use] - fn usable_lints(lints: &[Self]) -> Vec { - lints - .iter() - .filter(|l| !l.group.starts_with("internal")) - .cloned() - .collect() - } - - /// Returns all internal lints - #[must_use] - fn internal_lints(lints: &[Self]) -> Vec { - lints.iter().filter(|l| l.group == "internal").cloned().collect() - } - /// Returns the lints in a `HashMap`, grouped by the different lint groups #[must_use] fn by_lint_group(lints: impl Iterator) -> HashMap> { @@ -579,23 +562,14 @@ impl RenamedLint { /// Generates the code for registering lints #[must_use] -fn gen_declared_lints<'a>( - internal_lints: impl Iterator, - usable_lints: impl Iterator, -) -> String { - let mut details: Vec<_> = internal_lints - .map(|l| (false, &l.module, l.name.to_uppercase())) - .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase()))) - .collect(); +fn gen_declared_lints<'a>(lints: impl Iterator) -> String { + let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); details.sort_unstable(); let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); - for (is_public, module_name, lint_name) in details { - if !is_public { - output.push_str(" #[cfg(feature = \"internal\")]\n"); - } + for (module_name, lint_name) in details { let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); } output.push_str("];\n"); @@ -830,7 +804,7 @@ fn remove_line_splices(s: &str) -> String { .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - unescape::unescape_unicode(s, unescape::Mode::Str, &mut |range, ch| { + unescape_unicode(s, Mode::Str, &mut |range, ch| { if ch.is_ok() { res.push_str(&s[range]); } @@ -936,41 +910,6 @@ mod tests { assert_eq!(expected, result); } - #[test] - fn test_usable_lints() { - let lints = vec![ - Lint::new( - "should_assert_eq2", - "Not Deprecated", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new( - "should_assert_eq2", - "internal", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new( - "should_assert_eq2", - "internal_style", - "\"abc\"", - "module_name", - Range::default(), - ), - ]; - let expected = vec![Lint::new( - "should_assert_eq2", - "Not Deprecated", - "\"abc\"", - "module_name", - Range::default(), - )]; - assert_eq!(expected, Lint::usable_lints(&lints)); - } - #[test] fn test_by_lint_group() { let lints = vec![ diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index b87fcca13b1ce..206816398f50f 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -30,10 +30,10 @@ pub fn clippy_project_root() -> PathBuf { let current_dir = std::env::current_dir().unwrap(); for path in current_dir.ancestors() { let result = fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result { - if err.kind() == io::ErrorKind::NotFound { - continue; - } + if let Err(err) = &result + && err.kind() == io::ErrorKind::NotFound + { + continue; } let content = result.unwrap(); diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 54347043a13d4..20951afccbb7e 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" @@ -19,10 +19,7 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", optional = true } -tempfile = { version = "3.3.0", optional = true } toml = "0.7.3" -regex = { version = "1.5", optional = true } unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" @@ -31,10 +28,6 @@ url = "2.2" [dev-dependencies] walkdir = "2.3" -[features] -# build clippy with internal lints enabled, off by default -internal = ["serde_json", "tempfile", "regex"] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 57cabe437034a..272444475c0c4 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -5,6 +5,7 @@ use clippy_config::types::{ SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::is_cfg_test; use rustc_hir::{ AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant, VariantData, @@ -263,10 +264,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { continue; } - if let Some(cur_v) = cur_v { - if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span { - Self::lint_member_name(cx, &variant.ident, &cur_v.ident); - } + if let Some(cur_v) = cur_v + && cur_v.ident.name.as_str() > variant.ident.name.as_str() + && cur_v.span != variant.span + { + Self::lint_member_name(cx, &variant.ident, &cur_v.ident); } cur_v = Some(variant); } @@ -278,10 +280,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { continue; } - if let Some(cur_f) = cur_f { - if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span { - Self::lint_member_name(cx, &field.ident, &cur_f.ident); - } + if let Some(cur_f) = cur_f + && cur_f.ident.name.as_str() > field.ident.name.as_str() + && cur_f.span != field.span + { + Self::lint_member_name(cx, &field.ident, &cur_f.ident); } cur_f = Some(field); } @@ -342,7 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { struct CurItem<'a> { item: &'a Item<'a>, order: usize, - name: String, + name: Option, } let mut cur_t: Option> = None; @@ -359,32 +362,36 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // as no sorting by source map/line of code has to be applied. // for item in items { - if item.span.in_external_macro(cx.sess().source_map()) { + if is_cfg_test(cx.tcx, item.hir_id()) { continue; } - let ident = if let Some(ident) = item.kind.ident() { - ident - } else if let ItemKind::Impl(_) = item.kind - && !get_item_name(item).is_empty() - { - rustc_span::Ident::empty() // FIXME: a bit strange, is there a better way to do it? - } else { - continue; - }; - - if ident.name.as_str().starts_with('_') { - // Filters out unnamed macro-like impls for various derives, - // e.g. serde::Serialize or num_derive::FromPrimitive. + if item.span.in_external_macro(cx.sess().source_map()) { continue; } - if ident.name == rustc_span::sym::std && item.span.is_dummy() { - if let ItemKind::ExternCrate(None, _) = item.kind { - // Filters the auto-included Rust standard library. + if let Some(ident) = item.kind.ident() { + if ident.name.as_str().starts_with('_') { + // Filters out unnamed macro-like impls for various derives, + // e.g. serde::Serialize or num_derive::FromPrimitive. continue; } - println!("Unknown item: {item:?}"); + + if ident.name == rustc_span::sym::std && item.span.is_dummy() { + if let ItemKind::ExternCrate(None, _) = item.kind { + // Filters the auto-included Rust standard library. + continue; + } + if cfg!(debug_assertions) { + rustc_middle::bug!("unknown item: {item:?}"); + } + } + } else if let ItemKind::Impl(_) = item.kind + && get_item_name(item).is_some() + { + // keep going below + } else { + continue; } let item_kind = convert_module_item_kind(&item.kind); @@ -493,7 +500,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte /// further in the [Rust Reference, Paths Chapter][rust_ref]. /// /// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1 -fn get_item_name(item: &Item<'_>) -> String { +fn get_item_name(item: &Item<'_>) -> Option { match item.kind { ItemKind::Impl(im) => { if let TyKind::Path(path) = im.self_ty.kind { @@ -513,27 +520,19 @@ fn get_item_name(item: &Item<'_>) -> String { } segs.push(String::new()); - segs.join("!!") + Some(segs.join("!!")) }, QPath::TypeRelative(_, _path_seg) => { // This case doesn't exist in the clippy tests codebase. - String::new() + None }, - QPath::LangItem(_, _) => String::new(), + QPath::LangItem(_, _) => None, } } else { // Impls for anything that isn't a named type can be skipped. - String::new() + None } }, - // FIXME: `Ident::empty` for anonymous items is a bit strange, is there - // a better way to do it? - _ => item - .kind - .ident() - .unwrap_or(rustc_span::Ident::empty()) - .name - .as_str() - .to_owned(), + _ => item.kind.ident().map(|name| name.as_str().to_owned()), } } diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index 78102772927c0..27e304a848e33 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -12,17 +12,17 @@ declare_clippy_lint! { /// regardless of whether good alternatives exist or not. If you want more /// precise lints for `as`, please consider using these separate lints: /// - /// - clippy::cast_lossless - /// - clippy::cast_possible_truncation - /// - clippy::cast_possible_wrap - /// - clippy::cast_precision_loss - /// - clippy::cast_sign_loss - /// - clippy::char_lit_as_u8 - /// - clippy::fn_to_numeric_cast - /// - clippy::fn_to_numeric_cast_with_truncation - /// - clippy::ptr_as_ptr - /// - clippy::unnecessary_cast - /// - invalid_reference_casting + /// - `clippy::cast_lossless` + /// - `clippy::cast_possible_truncation` + /// - `clippy::cast_possible_wrap` + /// - `clippy::cast_precision_loss` + /// - `clippy::cast_sign_loss` + /// - `clippy::char_lit_as_u8` + /// - `clippy::fn_to_numeric_cast` + /// - `clippy::fn_to_numeric_cast_with_truncation` + /// - `clippy::ptr_as_ptr` + /// - `clippy::unnecessary_cast` + /// - `invalid_reference_casting` /// /// There is a good explanation the reason why this lint should work in this /// way and how it is useful [in this diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index e5439a6d401b9..9acff676d4f6b 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -243,7 +243,7 @@ fn build_sugg<'tcx>( // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app) } - .maybe_par(); + .maybe_paren(); // Determine whether we need to reference the argument to clone_from(). let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg); @@ -284,7 +284,7 @@ fn build_sugg<'tcx>( let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)` // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)` - let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par(); + let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_paren(); let inner_type = cx.typeck_results().expr_ty(ref_expr); // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it // deref to a mutable reference. @@ -296,7 +296,7 @@ fn build_sugg<'tcx>( } else { // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)` // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)` - Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr() + Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_paren().mut_addr() }; match call_kind { diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index fecf316640636..457692ed5dc53 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -8,17 +8,18 @@ use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { - if let Some(lint_name) = extract_clippy_lint(lint) { - if lint_name.as_str() == "restriction" && name != sym::allow { - span_lint_and_help( - cx, - BLANKET_CLIPPY_RESTRICTION_LINTS, - lint.span(), - "`clippy::restriction` is not meant to be enabled as a group", - None, - "enable the restriction lints you need individually", - ); - } + if let Some(lint_name) = extract_clippy_lint(lint) + && lint_name.as_str() == "restriction" + && name != sym::allow + { + span_lint_and_help( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + lint.span(), + "`clippy::restriction` is not meant to be enabled as a group", + None, + "enable the restriction lints you need individually", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs index d3153ec6613b5..50943b36809d2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -6,10 +6,10 @@ use rustc_span::Span; use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { - if let LitKind::Str(is, _) = lit.kind { - if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { - return; - } + if let LitKind::Str(is, _) = lit.kind + && (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok()) + { + return; } span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 4c84e61b1f26c..a851daaede71b 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,10 +36,7 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc - || name == sym::cfg_attr_trace - || name == sym::rustc_on_unimplemented - || name == sym::reason { + if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index e04d2ad5d13b7..f7f168cb26792 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -14,8 +14,9 @@ mod useless_attribute; mod utils; use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; -use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind}; +use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -448,6 +449,31 @@ declare_clippy_lint! { "duplicated attribute" } +declare_clippy_lint! { + /// ### What it does + /// Checks for ignored tests without messages. + /// + /// ### Why is this bad? + /// The reason for ignoring the test may not be obvious. + /// + /// ### Example + /// ```no_run + /// #[test] + /// #[ignore] + /// fn test() {} + /// ``` + /// Use instead: + /// ```no_run + /// #[test] + /// #[ignore = "Some good reason"] + /// fn test() {} + /// ``` + #[clippy::version = "1.85.0"] + pub IGNORE_WITHOUT_REASON, + pedantic, + "ignored tests without messages" +} + pub struct Attributes { msrv: Msrv, } @@ -532,6 +558,7 @@ impl_lint_pass!(PostExpansionEarlyAttributes => [ ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, DEPRECATED_SEMVER, + IGNORE_WITHOUT_REASON, USELESS_ATTRIBUTE, BLANKET_CLIPPY_RESTRICTION_LINTS, SHOULD_PANIC_WITHOUT_EXPECT, @@ -546,28 +573,27 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - if let Some(items) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { - allow_attributes::check(cx, attr); - } - if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) + if let Some(items) = &attr.meta_item_list() + && let Some(ident) = attr.ident() + { + if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes::check(cx, attr); + } + if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes_without_reason::check(cx, ident.name, items, attr); + } + if is_lint_level(ident.name, attr.id) { + blanket_clippy_restriction_lints::check(cx, ident.name, items); + } + if items.is_empty() || !attr.has_name(sym::deprecated) { + return; + } + for item in items { + if let MetaItemInner::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) { - allow_attributes_without_reason::check(cx, ident.name, items, attr); - } - if is_lint_level(ident.name, attr.id) { - blanket_clippy_restriction_lints::check(cx, ident.name, items); - } - if items.is_empty() || !attr.has_name(sym::deprecated) { - return; - } - for item in items { - if let MetaItemInner::MetaItem(mi) = &item - && let MetaItemKind::NameValue(lit) = &mi.kind - && mi.has_name(sym::since) - { - deprecated_semver::check(cx, item.span(), lit); - } + deprecated_semver::check(cx, item.span(), lit); } } } @@ -575,6 +601,22 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::should_panic) { should_panic_without_expect::check(cx, attr); } + + if attr.has_name(sym::ignore) + && match &attr.kind { + AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }), + AttrKind::DocComment(..) => true, + } + { + span_lint_and_help( + cx, + IGNORE_WITHOUT_REASON, + attr.span, + "`#[ignore]` without reason", + None, + "add a reason with `= \"..\"`", + ); + } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) { diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs index e5cfbaf952a70..df01c7fde1819 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs @@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], diag.warn( "unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI", ) - .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") + .help("qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") .span_label(packed_span, "`packed` representation set here"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 1cb43ab02a305..d75b73280e632 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -14,75 +14,75 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if attr.span.in_external_macro(cx.sess().source_map()) { return; } - if let Some(lint_list) = &attr.meta_item_list() { - if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) { - for lint in lint_list { - match item.kind { - ItemKind::Use(..) => { - let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { - return; - }; + if let Some(lint_list) = &attr.meta_item_list() + && attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) + { + for lint in lint_list { + match item.kind { + ItemKind::Use(..) => { + let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { + return; + }; - if namespace.is_none() - && matches!( - name.as_str(), - "ambiguous_glob_reexports" - | "dead_code" - | "deprecated" - | "hidden_glob_reexports" - | "unreachable_pub" - | "unused" - | "unused_braces" - | "unused_import_braces" - | "unused_imports" - ) - { - return; - } + if namespace.is_none() + && matches!( + name.as_str(), + "ambiguous_glob_reexports" + | "dead_code" + | "deprecated" + | "hidden_glob_reexports" + | "unreachable_pub" + | "unused" + | "unused_braces" + | "unused_import_braces" + | "unused_imports" + ) + { + return; + } - if namespace == Some(sym::clippy) - && matches!( - name.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - | "unused_trait_names" - ) - { - return; - } - }, - ItemKind::ExternCrate(..) => { - if is_word(lint, sym::unused_imports) && skip_unused_imports { - return; - } - if is_word(lint, sym::unused_extern_crates) { - return; - } - }, - _ => {}, - } + if namespace == Some(sym::clippy) + && matches!( + name.as_str(), + "wildcard_imports" + | "enum_glob_use" + | "redundant_pub_crate" + | "macro_use_imports" + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" + | "disallowed_types" + | "unused_trait_names" + ) + { + return; + } + }, + ItemKind::ExternCrate(..) => { + if is_word(lint, sym::unused_imports) && skip_unused_imports { + return; + } + if is_word(lint, sym::unused_extern_crates) { + return; + } + }, + _ => {}, } - let line_span = first_line_of_span(cx, attr.span); + } + let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) { - if src.contains("#[") { - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { - diag.span_suggestion( - line_span, - "if you just forgot a `!`, use", - src.replacen("#[", "#![", 1), - Applicability::MaybeIncorrect, - ); - }); - } - } + if let Some(src) = line_span.get_source_text(cx) + && src.contains("#[") + { + #[expect(clippy::collapsible_span_lint_calls)] + span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { + diag.span_suggestion( + line_span, + "if you just forgot a `!`, use", + src.replacen("#[", "#![", 1), + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 92a0c7f9acbcd..52d1d5b4c67a1 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -179,9 +179,14 @@ pub struct AwaitHolding { impl AwaitHolding { pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - Self { - def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types), - } + let (def_ids, _) = create_disallowed_map( + tcx, + &conf.await_holding_invalid_types, + crate::disallowed_types::def_kind_predicate, + "type", + false, + ); + Self { def_ids } } } @@ -192,10 +197,9 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding { def_id, .. }) = expr.kind + && let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) { - if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) { - self.check_interior_types(cx, coroutine_layout); - } + self.check_interior_types(cx, coroutine_layout); } } } diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index 612712d16843c..129e774784061 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::HasSession; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_else_clause, is_in_const_context}; +use clippy_utils::{higher, is_else_clause, is_in_const_context, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,18 +47,25 @@ declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::If(cond, then, Some(else_)) = expr.kind - && matches!(cond.kind, ExprKind::DropTemps(_)) + if !expr.span.from_expansion() + && let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) && let Some(then_lit) = as_int_bool_lit(then) - && let Some(else_lit) = as_int_bool_lit(else_) + && let Some(else_lit) = as_int_bool_lit(r#else) && then_lit != else_lit - && !expr.span.from_expansion() && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); - let mut applicability = Applicability::MachineApplicable; + let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; let snippet = { - let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); + let mut sugg = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); if !then_lit { sugg = !sugg; } @@ -72,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { s }; - let into_snippet = snippet.clone().maybe_par(); + let into_snippet = snippet.clone().maybe_paren(); let as_snippet = snippet.as_ty(ty); span_lint_and_then( @@ -91,10 +99,11 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { } } -fn as_int_bool_lit(e: &Expr<'_>) -> Option { - if let ExprKind::Block(b, _) = e.kind +fn as_int_bool_lit(expr: &Expr<'_>) -> Option { + if let ExprKind::Block(b, _) = expr.kind && b.stmts.is_empty() && let Some(e) = b.expr + && !e.span.from_expansion() && let ExprKind::Lit(lit) = e.kind && let LitKind::Int(x, _) = lit.node { diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 7bb5dbee126ed..bc6ba84772b3d 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -13,7 +13,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does @@ -242,11 +242,11 @@ struct Hir2Qmm<'a, 'tcx, 'v> { impl<'v> Hir2Qmm<'_, '_, 'v> { fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec) -> Result, String> { for a in a { - if let ExprKind::Binary(binop, lhs, rhs) = &a.kind { - if binop.node == op { - v = self.extract(op, &[lhs, rhs], v)?; - continue; - } + if let ExprKind::Binary(binop, lhs, rhs) = &a.kind + && binop.node == op + { + v = self.extract(op, &[lhs, rhs], v)?; + continue; } v.push(self.run(a)?); } @@ -349,9 +349,13 @@ impl SuggestContext<'_, '_, '_> { if let Some(str) = simplify_not(self.cx, self.msrv, terminal) { self.output.push_str(&str); } else { - self.output.push('!'); - self.output - .push_str(&Sugg::hir_opt(self.cx, terminal)?.maybe_par().to_string()); + let mut app = Applicability::MachineApplicable; + let snip = Sugg::hir_with_context(self.cx, terminal, SyntaxContext::root(), "", &mut app); + // Ignore the case If the expression is inside a macro expansion, or the default snippet is used + if app != Applicability::MachineApplicable { + return None; + } + self.output.push_str(&(!snip).to_string()); } }, True | False | Not(_) => { @@ -414,12 +418,12 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let lhs_snippet = lhs.span.get_source_text(cx)?; let rhs_snippet = rhs.span.get_source_text(cx)?; - if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) { - if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) { - // e.g. `(a as u64) < b`. Without the parens the `<` is - // interpreted as a start of generic arguments for `u64` - return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); - } + if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) + && let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) + { + // e.g. `(a as u64) < b`. Without the parens the `<` is + // interpreted as a start of generic arguments for `u64` + return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); } Some(format!("{lhs_snippet}{op}{rhs_snippet}")) 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 8892a9e6b6b08..7cde007a9b66d 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -2,7 +2,7 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,6 +73,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { } }) && !is_from_proc_macro(cx, e) + && let e_ty = cx.typeck_results().expr_ty_adjusted(e) + // check if the reference is coercing to a mutable reference + && (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target)) && let Some(deref_text) = deref_target.span.get_source_text(cx) { span_lint_and_then( @@ -90,10 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // 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; - } + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() + && !implements_trait(cx, *inner_ty, deref_trait_id, &[]) + { + return; } diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index 64345c81a2482..ad0a4f8cdf35a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::{is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; +use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::{Adjust, AutoBorrow}; use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -29,10 +30,6 @@ pub(super) fn check<'tcx>( } let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) { - let operator_kind = match mutability { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; // Make sure that the span to be replaced doesn't include parentheses, that could break the // suggestion. let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { @@ -42,7 +39,7 @@ pub(super) fn check<'tcx>( } else { expr.span }; - (format!("&raw {operator_kind} {snip}"), span) + (format!("&raw {} {snip}", mutability.ptr_str()), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -59,3 +56,25 @@ pub(super) fn check<'tcx>( } false } + +/// Check for an implicit cast from reference to raw pointer outside an explicit `as`. +pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) { + if !expr.span.from_expansion() + && let ExprKind::AddrOf(BorrowKind::Ref, _, pointee) = expr.kind + && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..))) + && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr) + && matches!(deref.kind, Adjust::Deref(..)) + && let Adjust::Borrow(AutoBorrow::RawPtr(mutability)) = borrow.kind + // Do not suggest taking a raw pointer to a temporary value + && !is_expr_temporary_value(cx, pointee) + { + span_lint_and_then(cx, BORROW_AS_PTR, expr.span, "implicit borrow as raw pointer", |diag| { + diag.span_suggestion_verbose( + expr.span.until(pointee.span), + "use a raw pointer instead", + format!("&raw {} ", mutability.ptr_str()), + Applicability::MachineApplicable, + ); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 8b3529e84fc6e..164d3540253a0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -36,7 +36,7 @@ pub(super) fn check( span, format!("casting the result of `{cast_from}::abs()` to {cast_to}"), "replace with", - format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_paren()), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index 3ae43732dc03f..0f066fae11844 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -42,7 +42,7 @@ pub(super) fn check( diag.span_suggestion_verbose( expr.span, "use `Into::into` instead", - format!("{}.into()", from_sugg.maybe_par()), + format!("{}.into()", from_sugg.maybe_paren()), applicability, ); }, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index ca973f4bb1aae..8742f5f1a0e0e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -64,11 +64,11 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX)) }, ExprKind::MethodCall(method, _, [lo, hi], _) => { - if method.ident.as_str() == "clamp" { + if method.ident.as_str() == "clamp" //FIXME: make this a diagnostic item - if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) { - return lo_bits.max(hi_bits); - } + && let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) + { + return lo_bits.max(hi_bits); } nbits }, @@ -185,7 +185,7 @@ fn offer_suggestion( ) { let cast_to_snip = snippet(cx, cast_to_span, ".."); let suggestion = if cast_to_snip == "_" { - format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par()) + format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_paren()) } else { format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, "..")) }; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 57a135abc2e2b..3fca0f8970770 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -19,16 +19,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { - if method_path.ident.name.as_str() == "cast" - && let Some(generic_args) = method_path.args - && let [GenericArg::Type(cast_to)] = generic_args.args - // There probably is no obvious reason to do this, just to be consistent with `as` cases. - && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) - { - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } + } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind + && method_path.ident.name.as_str() == "cast" + && let Some(generic_args) = method_path.args + && let [GenericArg::Type(cast_to)] = generic_args.args + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) + { + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } 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 c48f253606dcc..a5b295c88b1c7 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 @@ -21,42 +21,41 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) start_ty, end_ty, }) = expr_cast_chain_tys(cx, expr) + && let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { - if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { - let from_size = from_layout.size.bytes(); - let to_size = to_layout.size.bytes(); - if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { - span_lint_and_then( - cx, - CAST_SLICE_DIFFERENT_SIZES, - expr.span, - format!( - "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", - start_ty.ty, end_ty.ty, - ), - |diag| { - let ptr_snippet = source::snippet(cx, left_cast.span, ".."); + let from_size = from_layout.size.bytes(); + let to_size = to_layout.size.bytes(); + if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { + span_lint_and_then( + cx, + CAST_SLICE_DIFFERENT_SIZES, + expr.span, + format!( + "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", + start_ty.ty, end_ty.ty, + ), + |diag| { + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); - let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { - Mutability::Mut => ("_mut", "mut"), - Mutability::Not => ("", "const"), - }; - let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", - // get just the ty from the TypeAndMut so that the printed type isn't something like `mut - // T`, extract just the `T` - end_ty.ty - ); + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { + Mutability::Mut => ("_mut", "mut"), + Mutability::Not => ("", "const"), + }; + let sugg = format!( + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty + ); - diag.span_suggestion( - expr.span, - format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), - sugg, - rustc_errors::Applicability::HasPlaceholders, - ); - }, - ); - } + diag.span_suggestion( + expr.span, + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + sugg, + rustc_errors::Applicability::HasPlaceholders, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs new file mode 100644 index 0000000000000..8ace27eca895e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::ty::is_normalizable; +use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::MANUAL_DANGLING_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref ptr_ty) = to.kind { + let init_expr = expr_or_init(cx, from); + if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) + && let Some(std_or_core) = std_or_core(cx) + { + let sugg_fn = match ptr_ty.mutbl { + Mutability::Not => "ptr::dangling", + Mutability::Mut => "ptr::dangling_mut", + }; + + let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_DANGLING_PTR, + expr.span, + "manual creation of a dangling pointer", + "use", + sugg, + Applicability::MachineApplicable, + ); + } + } +} + +// Checks if the given expression is a call to `align_of` whose generic argument matches the target +// type, or a positive constant literal that matches the target type's alignment. +fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool { + match expr.kind { + ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to), + ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to), + _ => false, + } +} + +fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind + && let Some(fun_id) = path_def_id(cx, fun) + && match_def_path(cx, fun_id, &paths::ALIGN_OF) + && let Some(args) = path.segments.last().and_then(|seg| seg.args) + && let [GenericArg::Type(generic_ty)] = args.args + { + let typeck = cx.typeck_results(); + return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id); + } + false +} + +fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) -> bool { + let LitKind::Int(val, _) = lit.node else { return false }; + if val == 0 { + return false; + } + let to_mid_ty = cx.typeck_results().node_type(to.hir_id); + is_normalizable(cx, cx.param_env, to_mid_ty) + && cx + .tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) +} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index dc2a1fa85bf5c..76931fce209e5 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -17,6 +17,7 @@ mod char_lit_as_u8; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; +mod manual_dangling_ptr; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -71,7 +72,7 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let y: i8 = -1; - /// y as u128; // will return 18446744073709551615 + /// y as u64; // will return 18446744073709551615 /// ``` #[clippy::version = "pre 1.29.0"] pub CAST_SIGN_LOSS, @@ -759,6 +760,32 @@ declare_clippy_lint! { "detects `as *mut _` and `as *const _` conversion" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. + /// + /// ### Why is this bad? + /// This creates a dangling pointer and is better expressed as + /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. + /// + /// ### Example + /// ```no_run + /// let ptr = 4 as *const u32; + /// let aligned = std::mem::align_of::() as *const u32; + /// let mut_ptr: *mut i64 = 8 as *mut _; + /// ``` + /// Use instead: + /// ```no_run + /// let ptr = std::ptr::dangling::(); + /// let aligned = std::ptr::dangling::(); + /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); + /// ``` + #[clippy::version = "1.87.0"] + pub MANUAL_DANGLING_PTR, + style, + "casting small constant literals to pointers to create dangling pointers" +} + pub struct Casts { msrv: Msrv, } @@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [ ZERO_PTR, REF_AS_PTR, AS_POINTER_UNDERSCORE, + MANUAL_DANGLING_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { + manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + } + if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { @@ -846,6 +878,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } + if self.msrv.meets(cx, msrvs::RAW_REF_OP) { + borrow_as_ptr::check_implicit_cast(cx, expr); + } cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index d57e391b55d54..6f944914b8fd6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -81,7 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) { ( "try `pointer::cast`, a safer alternative", - format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), + format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_paren()), ) }; 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 cad9c1df273f0..2471c7355518b 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 @@ -53,7 +53,8 @@ pub(super) fn check<'tcx>( } if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { - let sugg = Sugg::hir(cx, cast_expr, "_"); + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app); let constness = match *to_mutbl { Mutability::Not => "const", Mutability::Mut => "mut", @@ -65,8 +66,8 @@ pub(super) fn check<'tcx>( 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, + format!("{}.cast_{constness}()", sugg.maybe_paren()), + app, ); } } 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 72953818a708e..ae994e94a32b5 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -130,11 +130,11 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) { - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { - lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); - return true; - } + if let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) + { + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index b36c8662289ca..8ada608049c7b 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -253,11 +253,11 @@ fn get_types_from_cast<'a>( match limit.kind { // `from_type::from(_)` ExprKind::Call(path, _) => { - if let ExprKind::Path(ref path) = path.kind { + if let ExprKind::Path(ref path) = path.kind // `to_type` - if let Some(to_type) = get_implementing_type(path, types, func) { - return Some((from_type, to_type)); - } + && let Some(to_type) = get_implementing_type(path, types, func) + { + return Some((from_type, to_type)); } }, // `to_type::MAX` diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index a1ff20dee721f..1d44c7e9c88b0 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -62,6 +62,7 @@ impl CognitiveComplexity { let mut cc = 1u64; let mut returns = 0u64; + let mut prev_expr: Option<&ExprKind<'tcx>> = None; let _: Option = for_each_expr_without_closures(expr, |e| { match e.kind { ExprKind::If(_, _, _) => { @@ -73,9 +74,14 @@ impl CognitiveComplexity { } cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; }, - ExprKind::Ret(_) => returns += 1, + ExprKind::Ret(_) => { + if !matches!(prev_expr, Some(ExprKind::Ret(_))) { + returns += 1; + } + }, _ => {}, } + prev_expr = Some(&e.kind); ControlFlow::Continue(()) }); diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index e73bfc6ebf7a1..20fae8a6775b9 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,10 +1,12 @@ +use clippy_config::Conf; 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 rustc_ast::ast; +use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; +use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_hir::{Block, Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -75,105 +77,152 @@ declare_clippy_lint! { "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); +pub struct CollapsibleIf { + let_chains_enabled: bool, + lint_commented_code: bool, +} + +impl CollapsibleIf { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + Self { + let_chains_enabled: tcx.features().let_chains(), + lint_commented_code: conf.lint_commented_code, + } + } -impl EarlyLintPass for CollapsibleIf { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::If(cond, then, else_) = &expr.kind + fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { + if !block_starts_with_comment(cx, else_block) + && let Some(else_) = expr_block(else_block) + && cx.tcx.hir_attrs(else_.hir_id).is_empty() + && !else_.span.from_expansion() + && let ExprKind::If(..) = else_.kind + { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(else_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, + else_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(else_block.span), &mut applicability) + ), + applicability, + ); + } + } + + fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { + if let Some(inner) = expr_block(then) + && cx.tcx.hir_attrs(inner.hir_id).is_empty() + && let ExprKind::If(check_inner, _, None) = &inner.kind + && self.eligible_condition(check_inner) + && let ctxt = expr.span.ctxt() + && inner.span.ctxt() == ctxt + && (self.lint_commented_code || !block_starts_with_comment(cx, then)) + { + span_lint_and_then( + cx, + COLLAPSIBLE_IF, + expr.span, + "this `if` statement can be collapsed", + |diag| { + let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span(); + let then_closing_bracket = { + let end = then.span.shrink_to_hi(); + end.with_lo(end.lo() - rustc_span::BytePos(1)) + .with_leading_whitespace(cx) + .into_span() + }; + let inner_if = inner.span.split_at(2).0; + let mut sugg = vec![ + // Remove the outer then block `{` + (then_open_bracket, String::new()), + // Remove the outer then block '}' + (then_closing_bracket, String::new()), + // Replace inner `if` by `&&` + (inner_if, String::from("&&")), + ]; + sugg.extend(parens_around(check)); + sugg.extend(parens_around(check_inner)); + + diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable); + }, + ); + } + } + + pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool { + self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..)) + } +} + +impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); + +impl LateLintPass<'_> for CollapsibleIf { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::If(cond, then, else_) = &expr.kind && !expr.span.from_expansion() { - if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, then.span, else_); - } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { - check_collapsible_no_if_let(cx, expr, cond, then); + if let Some(else_) = else_ + && let ExprKind::Block(else_, None) = else_.kind + { + Self::check_collapsible_else_if(cx, then.span, else_); + } else if else_.is_none() + && self.eligible_condition(cond) + && let ExprKind::Block(then, None) = then.kind + { + self.check_collapsible_if_if(cx, expr, cond, then); } } } } -fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { +fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // We trim all opening braces and whitespaces and then check if the next string is a comment. - let trimmed_block_text = snippet_block(cx, expr.span, "..", None) + let trimmed_block_text = snippet_block(cx, block.span, "..", None) .trim_start_matches(|c: char| c.is_whitespace() || c == '{') .to_owned(); trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { - 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, - ); - } -} - -fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { - 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 { .. } }`. - && !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); - diag.span_suggestion( - expr.span, - "collapse nested if block", - format!( - "if {} {}", - lhs.and(&rhs), - snippet_block(cx, content.span, "..", Some(expr.span)), - ), - app, // snippet - ); - }, - ); +/// If `block` is a block with either one expression or a statement containing an expression, +/// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. +fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match block.stmts { + [] => block.expr, + [stmt] => { + if let StmtKind::Semi(expr) = stmt.kind { + Some(expr) + } else { + None + } + }, + _ => None, } } -/// If the block contains only one expression, return it. -fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { - if let [stmt] = &*block.stmts - && let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind +/// If the expression is a `||`, suggest parentheses around it. +fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { + if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind + && op.node == BinOpKind::Or { - Some(expr) + vec![ + (expr.span.shrink_to_lo(), String::from("(")), + (expr.span.shrink_to_hi(), String::from(")")), + ] } else { - None + vec![] } } diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 0e7f01e44b049..9c3009a86cdc0 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { unreachable!(); }; - let lhs = Sugg::hir(cx, lhs, "..").maybe_par(); + let lhs = Sugg::hir(cx, lhs, "..").maybe_paren(); let rhs = Sugg::hir(cx, rhs, "..").addr(); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 03ed9c657b30a..42fbe6438d4ea 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -256,7 +256,7 @@ fn lint_branches_sharing_code<'tcx>( let suggestion = reindent_multiline(&suggestion, true, indent); let span = span.with_hi(last_block.span.hi()); - // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) let span = span .map_range(cx, |src, range| { (range.start > 4 && src.get(range.start - 4..range.start)? == " ") @@ -539,10 +539,10 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .filter(|stmt| !ignore_span.overlaps(stmt.span)) .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); - if let Some(expr) = block.expr { - if res.is_continue() { - res = intravisit::walk_expr(&mut walker, expr); - } + if let Some(expr) = block.expr + && res.is_continue() + { + res = intravisit::walk_expr(&mut walker, expr); } res.is_break() diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs index 4d908af4084f3..9f82f87672794 100644 --- a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs +++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs @@ -165,17 +165,4 @@ macro_rules! declare_clippy_lint { $(, $eval_always)? } }; - - ( - $(#[doc = $lit:literal])* - pub $lint_name:ident, - internal, - $desc:literal - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Internal, $desc, - None, "0.0.0" - } - }; } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 39e4516370709..2cccd6ba27027 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -3,36 +3,6 @@ // Manual edits will be overwritten. pub static LINTS: &[&crate::LintInfo] = &[ - #[cfg(feature = "internal")] - crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_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, - #[cfg(feature = "internal")] - crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO, crate::absolute_paths::ABSOLUTE_PATHS_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, @@ -52,6 +22,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, crate::attrs::DUPLICATED_ATTRIBUTES_INFO, + crate::attrs::IGNORE_WITHOUT_REASON_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, @@ -96,6 +67,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::MANUAL_DANGLING_PTR_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, @@ -286,6 +258,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, + crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, @@ -312,6 +285,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO, crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, + crate::manual_abs_diff::MANUAL_ABS_DIFF_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, @@ -334,7 +308,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO, crate::manual_string_new::MANUAL_STRING_NEW_INFO, crate::manual_strip::MANUAL_STRIP_INFO, - crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, crate::match_result_ok::MATCH_RESULT_OK_INFO, @@ -344,10 +317,10 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::matches::MANUAL_MAP_INFO, crate::matches::MANUAL_OK_ERR_INFO, crate::matches::MANUAL_UNWRAP_OR_INFO, + crate::matches::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::matches::MATCH_AS_REF_INFO, crate::matches::MATCH_BOOL_INFO, crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO, - crate::matches::MATCH_ON_VEC_ITEMS_INFO, crate::matches::MATCH_OVERLAPPING_ARM_INFO, crate::matches::MATCH_REF_PATS_INFO, crate::matches::MATCH_SAME_ARMS_INFO, @@ -488,6 +461,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::SWAP_WITH_TEMPORARY_INFO, crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::UNBUFFERED_BYTES_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, @@ -664,6 +638,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::redundant_slicing::DEREF_BY_SLICING_INFO, crate::redundant_slicing::REDUNDANT_SLICING_INFO, crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, + crate::redundant_test_prefix::REDUNDANT_TEST_PREFIX_INFO, crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO, crate::ref_option_ref::REF_OPTION_REF_INFO, crate::ref_patterns::REF_PATTERNS_INFO, 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 bbd5dc15542d1..f8a9037fc8047 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 @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_ty_alias; +use clippy_utils::source::SpanRangeExt as _; use hir::ExprKind; use hir::def::Res; use rustc_errors::Applicability; @@ -70,15 +71,26 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && 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() + // do not suggest replacing an expression by a type name with placeholders + && !base.is_suggestable_infer_ty() { - span_lint_and_sugg( + let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; + if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) { + // Remove `<`, '>` has already been removed by the existing removal expression. + removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); + } + span_lint_and_then( cx, DEFAULT_CONSTRUCTED_UNIT_STRUCTS, - expr.span.with_lo(qpath.qself_span().hi()), + expr.span, "use of `default` to create a unit struct", - "remove this call to `default`", - String::new(), - Applicability::MachineApplicable, + |diag| { + diag.multipart_suggestion( + "remove this call to `default`", + removals, + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index de66ead4f4204..b60c11d79d48f 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -42,6 +42,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "1.86.0"] ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), + #[clippy::version = "1.86.0"] + ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), // end deprecated lints. used by `cargo dev deprecate_lint` ]} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 849c60b89b97e..7da5a530eaa3d 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1133,61 +1133,60 @@ fn report<'tcx>( impl<'tcx> Dereferencing<'tcx> { fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { - if let Some(outer_pat) = self.ref_locals.get_mut(&local) { - if let Some(pat) = outer_pat { - // Check for auto-deref - if !matches!( - cx.typeck_results().expr_adjustments(e), - [ - Adjustment { - kind: Adjust::Deref(_), - .. - }, - Adjustment { - kind: Adjust::Deref(_), - .. - }, + if let Some(outer_pat) = self.ref_locals.get_mut(&local) + && let Some(pat) = outer_pat + // Check for auto-deref + && !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), .. - ] - ) { - match get_parent_expr(cx, e) { - // Field accesses are the same no matter the number of references. - Some(Expr { - kind: ExprKind::Field(..), - .. - }) => (), - Some(&Expr { - span, - kind: ExprKind::Unary(UnOp::Deref, _), - .. - }) if !span.from_expansion() => { - // Remove explicit deref. - let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((span, snip.into())); - }, - Some(parent) if !parent.span.from_expansion() => { - // Double reference might be needed at this point. - if parent.precedence() == ExprPrecedence::Unambiguous { - // Parentheses would be needed here, don't lint. - *outer_pat = None; - } else { - pat.always_deref = false; - let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((e.span, format!("&{snip}"))); - } - }, - _ if !e.span.from_expansion() => { - // Double reference might be needed at this point. - pat.always_deref = false; - let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); - pat.replacements.push((e.span, format!("&{snip}"))); - }, - // Edge case for macros. The span of the identifier will usually match the context of the - // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc - // macros - _ => *outer_pat = None, + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, + .. + ] + ) + { + match get_parent_expr(cx, e) { + // Field accesses are the same no matter the number of references. + Some(Expr { + kind: ExprKind::Field(..), + .. + }) => (), + Some(&Expr { + span, + kind: ExprKind::Unary(UnOp::Deref, _), + .. + }) if !span.from_expansion() => { + // Remove explicit deref. + let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((span, snip.into())); + }, + Some(parent) if !parent.span.from_expansion() => { + // Double reference might be needed at this point. + if parent.precedence() == ExprPrecedence::Unambiguous { + // Parentheses would be needed here, don't lint. + *outer_pat = None; + } else { + pat.always_deref = false; + let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((e.span, format!("&{snip}"))); } - } + }, + _ if !e.span.from_expansion() => { + // Double reference might be needed at this point. + pat.always_deref = false; + let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); + pat.replacements.push((e.span, format!("&{snip}"))); + }, + // Edge case for macros. The span of the identifier will usually match the context of the + // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc + // macros + _ => *outer_pat = None, } } } diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 8d9222e4bf61e..10331b3855b84 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -94,18 +94,18 @@ fn check_struct<'tcx>( ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, ) { - if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args, .. }) = p.segments.last() { - let args = args.map(|a| a.args).unwrap_or(&[]); - - // ty_args contains the generic parameters of the type declaration, while args contains the - // arguments used at instantiation time. If both len are not equal, it means that some - // parameters were not provided (which means that the default values were used); in this - // case we will not risk suggesting too broad a rewrite. We won't either if any argument - // is a type or a const. - if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { - return; - } + if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind + && let Some(PathSegment { args, .. }) = p.segments.last() + { + let args = args.map(|a| a.args).unwrap_or(&[]); + + // ty_args contains the generic parameters of the type declaration, while args contains the + // arguments used at instantiation time. If both len are not equal, it means that some + // parameters were not provided (which means that the default values were used); in this + // case we will not risk suggesting too broad a rewrite. We won't either if any argument + // is a type or a const. + if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { + return; } } @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Default, def_id) diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index c85aca8d04efd..06528f875a29b 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived); + let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -235,7 +235,7 @@ fn check_hash_peq<'tcx>( { // 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); + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); if !hash_is_automatically_derived || peq_is_automatically_derived { return; @@ -278,7 +278,7 @@ fn check_ord_partial_ord<'tcx>( { // 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); + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; @@ -349,6 +349,10 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h { return; } + // The presence of `unsafe` fields prevents deriving `Clone` automatically + if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { + return; + } span_lint_and_note( cx, @@ -426,10 +430,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind { - if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - return ControlFlow::Break(()); - } + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + { + return ControlFlow::Break(()); } walk_expr(self, expr) @@ -479,7 +483,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() } -/// Creates the `ParamEnv` used for the give type's derived `Eq` impl. +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { // Initial map from generic index to param def. // Vec<(param_def, needs_eq)> diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 4b8a689e99478..fc6af204a74a0 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -4,6 +4,7 @@ use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, @@ -72,8 +73,15 @@ pub struct DisallowedMacros { impl DisallowedMacros { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self { + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_macros, + |def_kind| matches!(def_kind, DefKind::Macro(_)), + "macro", + false, + ); Self { - disallowed: create_disallowed_map(tcx, &conf.disallowed_macros), + disallowed, seen: FxHashSet::default(), derive_src: None, earlies, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 149cf1cf2def1..1382dafa931e4 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -63,9 +63,19 @@ pub struct DisallowedMethods { impl DisallowedMethods { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - Self { - disallowed: create_disallowed_map(tcx, &conf.disallowed_methods), - } + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_methods, + |def_kind| { + matches!( + def_kind, + DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn + ) + }, + "function", + false, + ); + Self { disallowed } } } @@ -74,12 +84,7 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (id, span) = match &expr.kind { - ExprKind::Path(path) - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = - cx.qpath_res(path, expr.hir_id) => - { - (id, expr.span) - }, + ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span), ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => { (id, name.ident.span) }, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 38903596414cf..2bae82648ac76 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; -use clippy_config::types::DisallowedPath; +use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -60,22 +60,7 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let mut def_ids = DefIdMap::default(); - let mut prim_tys = FxHashMap::default(); - for disallowed_path in &conf.disallowed_types { - let path: Vec<_> = disallowed_path.path().split("::").collect::>(); - for res in clippy_utils::def_path_res(tcx, &path) { - match res { - Res::Def(_, id) => { - def_ids.insert(id, (disallowed_path.path(), disallowed_path)); - }, - Res::PrimTy(ty) => { - prim_tys.insert(ty, (disallowed_path.path(), disallowed_path)); - }, - _ => {}, - } - } - } + let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true); Self { def_ids, prim_tys } } @@ -95,6 +80,19 @@ impl DisallowedTypes { } } +pub fn def_kind_predicate(def_kind: DefKind) -> bool { + matches!( + def_kind, + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::AssocTy + ) +} + impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs index 8cdaba88e5095..7a1c7c675d2ec 100644 --- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs +++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs @@ -113,20 +113,20 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b s != "-" && s.contains('-') } - if let Ok(url) = Url::parse(word) { + if let Ok(url) = Url::parse(word) // try to get around the fact that `foo::bar` parses as a valid URL - if !url.cannot_be_a_base() { - span_lint_and_sugg( - cx, - DOC_MARKDOWN, - span, - "you should put bare URLs between `<`/`>` or make a proper Markdown link", - "try", - format!("<{word}>"), - Applicability::MachineApplicable, - ); - return; - } + && !url.cannot_be_a_base() + { + span_lint_and_sugg( + cx, + DOC_MARKDOWN, + span, + "you should put bare URLs between `<`/`>` or make a proper Markdown link", + "try", + format!("<{word}>"), + Applicability::MachineApplicable, + ); + return; } // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index e75abf28bace8..039937e0207b0 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,11 +1,14 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; -use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; -use clippy_utils::{is_doc_hidden, return_ty}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::{Span, sym}; +use std::ops::ControlFlow; pub fn check( cx: &LateContext<'_>, @@ -13,7 +16,6 @@ pub fn check( sig: FnSig<'_>, headers: DocHeaders, body_id: Option, - panic_info: Option<(Span, bool)>, check_private_items: bool, ) { if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) { @@ -46,13 +48,16 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_info.is_some_and(|el| !el.1) { + if !headers.panics + && let Some(body_id) = body_id + && let Some(panic_span) = find_panic(cx, body_id) + { span_lint_and_note( cx, MISSING_PANICS_DOC, span, "docs for function which may panic missing `# Panics` section", - panic_info.map(|el| el.0), + Some(panic_span), "first possible panic found here", ); } @@ -89,3 +94,39 @@ pub fn check( } } } + +fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { + let mut panic_span = None; + let typeck = cx.tcx.typeck_body(body_id); + for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && (is_panic(cx, macro_call.def_id) + || matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) + && !cx.tcx.hir_is_inside_const_context(expr.hir_id) + && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) + && panic_span.is_none() + { + panic_span = Some(macro_call.span); + } + + // check for `unwrap` and `expect` for both `Option` and `Result` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or_else(|| method_chain_args(expr, &["expect"])) + && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() + && matches!( + get_type_diagnostic_name(cx, receiver_ty), + Some(sym::Option | sym::Result) + ) + && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) + && panic_span.is_none() + { + panic_span = Some(expr.span); + } + + // Visit all nodes to fulfill any `#[expect]`s after the first linted panic + ControlFlow::::Continue(()) + }); + panic_span +} diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 36fd396cc1df8..ab77edf1147cb 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -3,11 +3,8 @@ use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, @@ -16,18 +13,15 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, It use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; +use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments, }; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::edition::Edition; -use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; @@ -194,6 +188,19 @@ declare_clippy_lint! { /// } /// } /// ``` + /// + /// Individual panics within a function can be ignored with `#[expect]` or + /// `#[allow]`: + /// + /// ```no_run + /// # use std::num::NonZeroUsize; + /// pub fn will_not_panic(x: usize) { + /// #[expect(clippy::missing_panics_doc, reason = "infallible")] + /// let y = NonZeroUsize::new(1).unwrap(); + /// + /// // If any panics are added in the future the lint will still catch them + /// } + /// ``` #[clippy::version = "1.51.0"] pub MISSING_PANICS_DOC, pedantic, @@ -657,20 +664,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { self.check_private_items, ); match item.kind { - ItemKind::Fn { sig, body: body_id, .. } => { + ItemKind::Fn { sig, body, .. } => { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || item.span.in_external_macro(cx.tcx.sess.source_map())) { - let body = cx.tcx.hir_body(body_id); - - let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); missing_headers::check( cx, item.owner_id, sig, headers, - Some(body_id), - panic_info, + Some(body), self.check_private_items, ); } @@ -697,15 +700,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { if let TraitItemKind::Fn(sig, ..) = trait_item.kind && !trait_item.span.in_external_macro(cx.tcx.sess.source_map()) { - missing_headers::check( - cx, - trait_item.owner_id, - sig, - headers, - None, - None, - self.check_private_items, - ); + missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items); } }, Node::ImplItem(impl_item) => { @@ -713,16 +708,12 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { && !impl_item.span.in_external_macro(cx.tcx.sess.source_map()) && !is_trait_impl_item(cx, impl_item.hir_id()) { - let body = cx.tcx.hir_body(body_id); - - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value); missing_headers::check( cx, impl_item.owner_id, sig, headers, Some(body_id), - panic_span, self.check_private_items, ); } @@ -880,19 +871,18 @@ fn check_for_code_clusters<'a, Events: Iterator{}", doc[start..end].replace('`', "")); - diag.span_suggestion_verbose( - span, - "wrap the entire group in `` tags", - sugg, - Applicability::MaybeIncorrect, - ); - diag.help("separate code snippets will be shown with a gap"); - }); - } + span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| { + let sugg = format!("{}", doc[start..end].replace('`', "")); + diag.span_suggestion_verbose( + span, + "wrap the entire group in `` tags", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("separate code snippets will be shown with a gap"); + }); } code_includes_link = false; code_starts_at = None; @@ -1169,72 +1159,6 @@ fn check_doc<'a, Events: Iterator, Range { - cx: &'a LateContext<'tcx>, - is_const: bool, - panic_span: Option, - typeck_results: &'tcx ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { - pub fn find_span( - cx: &'a LateContext<'tcx>, - typeck_results: &'tcx ty::TypeckResults<'tcx>, - body: impl Visitable<'tcx>, - ) -> Option<(Span, bool)> { - let mut vis = Self { - cx, - is_const: false, - panic_span: None, - typeck_results, - }; - body.visit(&mut vis); - vis.panic_span.map(|el| (el, vis.is_const)) - } -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.panic_span.is_some() { - return; - } - - if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { - if is_panic(self.cx, macro_call.def_id) - || matches!( - self.cx.tcx.item_name(macro_call.def_id).as_str(), - "assert" | "assert_eq" | "assert_ne" - ) - { - self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); - self.panic_span = Some(macro_call.span); - } - } - - // check for `unwrap` and `expect` for both `Option` and `Result` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result) - { - self.panic_span = Some(expr.span); - } - } - - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - // Panics in const blocks will cause compilation to fail. - fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } -} - #[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type fn looks_like_refdef(doc: &str, range: Range) -> Option> { if range.end < range.start { diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index bf549dcdb5063..ec4538039a918 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -64,7 +64,10 @@ pub fn check( match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => match &item.kind { ItemKind::Fn(box Fn { - ident, sig, body: Some(block), .. + ident, + sig, + body: Some(block), + .. }) if ident.name == sym::main => { if !ignore { get_test_spans(&item, *ident, &mut test_attr_spans); diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 617982f4da30f..5c360ce6a5f7e 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -144,10 +144,10 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { // .. // } fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { - if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { - if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) { - return body.hir_id == drop_expr.hir_id; - } + if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) + && let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) + { + return body.hir_id == drop_expr.hir_id; } false } diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs index c67dcd3c82b50..0c5f8bbf4ca53 100644 --- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, snippet_indent}; use clippy_utils::tokenize_with_text; @@ -89,7 +91,7 @@ declare_clippy_lint! { #[derive(Debug)] struct ItemInfo { kind: &'static str, - name: Symbol, + name: Option, span: Span, mod_items: Option, } @@ -315,8 +317,12 @@ impl EmptyLineAfter { for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) { stop.comment_out(cx, &mut suggestions); } + let name = match info.name { + Some(name) => format!("{} `{name}`", info.kind).into(), + None => Cow::from("the following item"), + }; diag.multipart_suggestion_verbose( - format!("if the doc comment should not document `{}` comment it out", info.name), + format!("if the doc comment should not document {name} then comment it out"), suggestions, Applicability::MaybeIncorrect, ); @@ -381,13 +387,10 @@ impl EmptyLineAfter { ) { self.items.push(ItemInfo { kind: kind.descr(), - // FIXME: this `sym::empty` can be leaked, see - // https://github.com/rust-lang/rust/pull/138740#discussion_r2021979899 - name: if let Some(ident) = ident { ident.name } else { kw::Empty }, - span: if let Some(ident) = ident { - span.with_hi(ident.span.hi()) - } else { - span.with_hi(span.lo()) + name: ident.map(|ident| ident.name), + span: match ident { + Some(ident) => span.with_hi(ident.span.hi()), + None => span.shrink_to_lo(), }, mod_items: match kind { ItemKind::Mod(_, _, ModKind::Loaded(items, _, _, _)) => items @@ -447,7 +450,7 @@ impl EarlyLintPass for EmptyLineAfter { fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) { self.items.push(ItemInfo { kind: "crate", - name: kw::Crate, + name: Some(kw::Crate), span: krate.spans.inner_span.with_hi(krate.spans.inner_span.lo()), mod_items: krate .items diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index 7d87f04fef9d8..a38d6df89f2b9 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -1,10 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Item, ItemKind, Variant, VariantData}; +use clippy_utils::attrs::span_contains_cfg; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_lexer::TokenKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_hir::def::CtorOf; +use rustc_hir::def::DefKind::Ctor; +use rustc_hir::def::Res::Def; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node, Path, QPath, Variant, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -70,10 +75,23 @@ declare_clippy_lint! { "finds enum variants with empty brackets" } -declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); +#[derive(Debug)] +enum Usage { + Unused { redundant_use_sites: Vec }, + Used, + NoDefinition { redundant_use_sites: Vec }, +} + +#[derive(Default)] +pub struct EmptyWithBrackets { + // Value holds `Usage::Used` if the empty tuple variant was used as a function + empty_tuple_enum_variants: FxIndexMap, +} + +impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); -impl EarlyLintPass for EmptyWithBrackets { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +impl LateLintPass<'_> for EmptyWithBrackets { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Struct(ident, var_data, _) = &item.kind && has_brackets(var_data) && let span_after_ident = item.span.with_lo(ident.span.hi()) @@ -96,70 +114,175 @@ impl EarlyLintPass for EmptyWithBrackets { } } - fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) { + fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) { + // the span of the parentheses/braces let span_after_ident = variant.span.with_lo(variant.ident.span.hi()); - if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) { - span_lint_and_then( + if has_no_fields(cx, &variant.data, span_after_ident) { + match variant.data { + VariantData::Struct { .. } => { + // Empty struct variants can be linted immediately + span_lint_and_then( + cx, + EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + span_after_ident, + "enum variant has empty brackets", + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + }, + VariantData::Tuple(.., local_def_id) => { + // Don't lint reachable tuple enums + if cx.effective_visibilities.is_reachable(variant.def_id) { + return; + } + if let Some(entry) = self.empty_tuple_enum_variants.get_mut(&local_def_id) { + // empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the + // definition was encountered. Now that there's a definition, convert it + // to Usage::Unused. + if let Usage::NoDefinition { redundant_use_sites } = entry { + *entry = Usage::Unused { + redundant_use_sites: redundant_use_sites.clone(), + }; + } + } else { + self.empty_tuple_enum_variants.insert( + local_def_id, + Usage::Unused { + redundant_use_sites: vec![], + }, + ); + } + }, + VariantData::Unit(..) => {}, + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(def_id) = check_expr_for_enum_as_function(expr) { + if let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr) { + // Do not count expressions from macro expansion as a redundant use site. + if expr.span.from_expansion() { + return; + } + match self.empty_tuple_enum_variants.get_mut(&def_id) { + Some( + &mut (Usage::Unused { + ref mut redundant_use_sites, + } + | Usage::NoDefinition { + ref mut redundant_use_sites, + }), + ) => { + redundant_use_sites.push(parentheses_span); + }, + None => { + // The variant isn't in the IndexMap which means its definition wasn't encountered yet. + self.empty_tuple_enum_variants.insert( + def_id, + Usage::NoDefinition { + redundant_use_sites: vec![parentheses_span], + }, + ); + }, + _ => {}, + } + } else { + // The parentheses are not redundant. + self.empty_tuple_enum_variants.insert(def_id, Usage::Used); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'_>) { + for (local_def_id, usage) in &self.empty_tuple_enum_variants { + // Ignore all variants with Usage::Used or Usage::NoDefinition + let Usage::Unused { redundant_use_sites } = usage else { + continue; + }; + // Attempt to fetch the Variant from LocalDefId. + let Node::Variant(variant) = cx.tcx.hir_node( + cx.tcx + .local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()), + ) else { + continue; + }; + // Span of the parentheses in variant definition + let span = variant.span.with_lo(variant.ident.span.hi()); + span_lint_hir_and_then( cx, EMPTY_ENUM_VARIANTS_WITH_BRACKETS, - span_after_ident, + variant.hir_id, + span, "enum variant has empty brackets", |diagnostic| { - diagnostic.span_suggestion_hidden( - span_after_ident, - "remove the brackets", - "", - Applicability::MaybeIncorrect, - ); + if redundant_use_sites.is_empty() { + // If there's no redundant use sites, the definition is the only place to modify. + diagnostic.span_suggestion_hidden( + span, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + } else { + let mut parentheses_spans: Vec<_> = + redundant_use_sites.iter().map(|span| (*span, String::new())).collect(); + parentheses_spans.push((span, String::new())); + diagnostic.multipart_suggestion( + "remove the brackets", + parentheses_spans, + Applicability::MaybeIncorrect, + ); + } }, ); } } } -fn has_no_ident_token(braces_span_str: &str) -> bool { - !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) +fn has_brackets(var_data: &VariantData<'_>) -> bool { + !matches!(var_data, VariantData::Unit(..)) } -fn has_brackets(var_data: &VariantData) -> bool { - !matches!(var_data, VariantData::Unit(_)) -} - -fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { - if !var_data.fields().is_empty() { - return false; - } - +fn has_no_fields(cx: &LateContext<'_>, var_data: &VariantData<'_>, braces_span: Span) -> bool { + var_data.fields().is_empty() && // there might still be field declarations hidden from the AST // (conditionally compiled code using #[cfg(..)]) - - let Some(braces_span_str) = snippet_opt(cx, braces_span) else { - return false; - }; - - has_no_ident_token(braces_span_str.as_ref()) + !span_contains_cfg(cx, braces_span) } -#[cfg(test)] -mod unit_test { - use super::*; - - #[test] - fn test_has_no_ident_token() { - let input = "{ field: u8 }"; - assert!(!has_no_ident_token(input)); - - let input = "(u8, String);"; - assert!(!has_no_ident_token(input)); - - let input = " { - // test = 5 - } - "; - assert!(has_no_ident_token(input)); +// If expression HIR ID and callee HIR ID are same, returns the span of the parentheses, else, +// returns None. +fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + if let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + && let ExprKind::Call(callee, ..) = parent.kind + && callee.hir_id == expr.hir_id + { + Some(parent.span.with_lo(expr.span.hi())) + } else { + None + } +} - let input = " ();"; - assert!(has_no_ident_token(input)); +// Returns the LocalDefId of the variant being called as a function if it exists. +fn check_expr_for_enum_as_function(expr: &Expr<'_>) -> Option { + if let ExprKind::Path(QPath::Resolved( + _, + Path { + res: Def(Ctor(CtorOf::Variant, _), def_id), + .. + }, + )) = expr.kind + { + def_id.as_local() + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index dcfee0b6d3c62..182cb4e46d2bd 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -95,14 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { return; }; - if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy { - span_lint(cx, MAP_ENTRY, expr.span, lint_msg); - return; - } - if then_search.edits.is_empty() && else_search.edits.is_empty() { // No insertions return; + } else if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy { + // If there are other uses of the key, and the key is not copy, + // we cannot perform a fix automatically, but continue to emit a lint. + None } else if then_search.edits.is_empty() || else_search.edits.is_empty() { // if .. { insert } else { .. } or if .. { .. } else { insert } let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) { @@ -123,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), ), }; - format!( + Some(format!( "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}", map_ty.entry_path(), - ) + )) } else { // if .. { insert } else { insert } let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated { @@ -142,13 +141,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { }; let indent_str = snippet_indent(cx, expr.span); let indent_str = indent_str.as_deref().unwrap_or(""); - format!( + Some(format!( "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\ {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}", reindent_multiline(&then_str, true, Some(4 + indent_str.len())), reindent_multiline(&else_str, true, Some(4 + indent_str.len())), entry = map_ty.entry_path(), - ) + )) } } else { if then_search.edits.is_empty() { @@ -163,17 +162,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { then_search.snippet_occupied(cx, then_expr.span, &mut app) }; - format!( + Some(format!( "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}", map_ty.entry_path(), - ) + )) } else if let Some(insertion) = then_search.as_single_insertion() { let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; if contains_expr.negated { if insertion.value.can_have_side_effects() { - format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});") + Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")) } else { - format!("{map_str}.entry({key_str}).or_insert({value_str});") + Some(format!("{map_str}.entry({key_str}).or_insert({value_str});")) } } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. @@ -183,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); if contains_expr.negated { - format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});") + Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")) } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // This would need to be a different lint. @@ -192,7 +191,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } }; - span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); + if let Some(sugg) = sugg { + span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); + } else { + span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + } } } diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index f01b5c840d290..ec81294624efa 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { - if let ty::Adt(adt, _) = ty.kind() { - if adt.is_enum() { - ty = adt.repr().discr_type().to_ty(cx.tcx); - } + if let ty::Adt(adt, _) = ty.kind() + && adt.is_enum() + { + ty = adt.repr().discr_type().to_ty(cx.tcx); } match ty.kind() { ty::Int(IntTy::Isize) => { diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index e248f08789b2d..2cb3b32babe89 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -72,10 +72,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _: Span, fn_def_id: LocalDefId, ) { - if let Some(header) = fn_kind.header() { - if header.abi != ExternAbi::Rust { - return; - } + if let Some(header) = fn_kind.header() + && header.abi != ExternAbi::Rust + { + return; } let parent_id = cx @@ -93,12 +93,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { // find `self` ty for this trait if relevant if let ItemKind::Trait(_, _, _, _, _, items) = item.kind { for trait_item in items { - if trait_item.id.owner_id.def_id == fn_def_id { + if trait_item.id.owner_id.def_id == fn_def_id // be sure we have `self` parameter in this function - if trait_item.kind == (AssocItemKind::Fn { has_self: true }) { - trait_self_ty = - Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty()); - } + && trait_item.kind == (AssocItemKind::Fn { has_self: true }) + { + trait_self_ty = Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty()); } } } @@ -142,22 +141,22 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() { - if let PlaceBase::Local(lid) = cmt.place.base { - // FIXME(rust/#120456) - is `swap_remove` correct? - self.set.swap_remove(&lid); - } + if cmt.place.projections.is_empty() + && let PlaceBase::Local(lid) = cmt.place.base + { + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { - if cmt.place.projections.is_empty() { - if let PlaceBase::Local(lid) = cmt.place.base { - // FIXME(rust/#120456) - is `swap_remove` correct? - self.set.swap_remove(&lid); - } + if cmt.place.projections.is_empty() + && let PlaceBase::Local(lid) = cmt.place.base + { + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } @@ -171,10 +170,11 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { // skip if there is a `self` parameter binding to a type // that contains `Self` (i.e.: `self: Box`), see #4804 - if let Some(trait_self_ty) = self.trait_self_ty { - if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { - return; - } + if let Some(trait_self_ty) = self.trait_self_ty + && self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower + && cmt.place.ty().contains(trait_self_ty) + { + return; } if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { 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 f67d38d932b9a..c868b782f43c3 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -75,10 +75,10 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { - if is_panic(self.lcx, macro_call.def_id) { - self.result.push(expr.span); - } + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) + && is_panic(self.lcx, macro_call.def_id) + { + self.result.push(expr.span); } // check for `unwrap` 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 daa199779e3cb..d5f0659f8427f 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -154,7 +154,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su }; } - suggestion.maybe_par() + suggestion.maybe_paren() } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { @@ -165,7 +165,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_paren()), Applicability::MachineApplicable, ); } @@ -228,24 +228,24 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { - if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) + && let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { Some("exp2") } else { None - } { - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "exponent for bases 2 and e can be computed more accurately", - "consider using", - format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), - Applicability::MachineApplicable, - ); } + { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "exponent for bases 2 and e can be computed more accurately", + "consider using", + format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), + Applicability::MachineApplicable, + ); } // Check argument @@ -254,13 +254,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: ( SUBOPTIMAL_FLOPS, "square-root of a number can be computed more efficiently and accurately", - format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_paren()), ) } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value { ( IMPRECISE_FLOPS, "cube-root of a number can be computed more accurately", - format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_paren()), ) } else if let Some(exponent) = get_integer_from_float_constant(&value) { ( @@ -268,7 +268,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: "exponentiation with integer powers can be computed more efficiently", format!( "{}.powi({})", - Sugg::hir(cx, receiver, "..").maybe_par(), + Sugg::hir(cx, receiver, "..").maybe_paren(), numeric_literal::format(&exponent.to_string(), None, false) ), ) @@ -289,55 +289,53 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { - if value == Int(2) { - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind - { - if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() { - return; - } - } - } + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) + && value == Int(2) + && let Some(parent) = get_parent_expr(cx, expr) + { + if let Some(grandparent) = get_parent_expr(cx, parent) + && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind + && method_name.as_str() == "sqrt" + && detect_hypot(cx, receiver).is_some() + { + return; + } - if let ExprKind::Binary( - Spanned { - node: op @ (BinOpKind::Add | BinOpKind::Sub), - .. - }, - lhs, - rhs, - ) = parent.kind - { - let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; - - // Negate expr if original code has subtraction and expr is on the right side - let maybe_neg_sugg = |expr, hir_id| { - let sugg = Sugg::hir(cx, expr, ".."); - if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - -sugg - } else { - sugg - } - }; - - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - parent.span, - "multiply and add expressions can be calculated more efficiently and accurately", - "consider using", - format!( - "{}.mul_add({}, {})", - Sugg::hir(cx, receiver, "..").maybe_par(), - maybe_neg_sugg(receiver, expr.hir_id), - maybe_neg_sugg(other_addend, other_addend.hir_id), - ), - Applicability::MachineApplicable, - ); + if let ExprKind::Binary( + Spanned { + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. + }, + lhs, + rhs, + ) = parent.kind + { + let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + + // Negate expr if original code has subtraction and expr is on the right side + let maybe_neg_sugg = |expr, hir_id| { + let sugg = Sugg::hir(cx, expr, ".."); + if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { + -sugg + } else { + sugg } - } + }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + parent.span, + "multiply and add expressions can be calculated more efficiently and accurately", + "consider using", + format!( + "{}.mul_add({}, {})", + Sugg::hir(cx, receiver, "..").maybe_paren(), + maybe_neg_sugg(receiver, expr.hir_id), + maybe_neg_sugg(other_addend, other_addend.hir_id), + ), + Applicability::MachineApplicable, + ); } } } @@ -371,7 +369,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { { return Some(format!( "{}.hypot({})", - Sugg::hir(cx, lmul_lhs, "..").maybe_par(), + Sugg::hir(cx, lmul_lhs, "..").maybe_paren(), Sugg::hir(cx, rmul_lhs, "..") )); } @@ -403,7 +401,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { { return Some(format!( "{}.hypot({})", - Sugg::hir(cx, largs_0, "..").maybe_par(), + Sugg::hir(cx, largs_0, "..").maybe_paren(), Sugg::hir(cx, rargs_0, "..") )); } @@ -449,7 +447,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "(e.pow(x) - 1) can be computed more accurately", "consider using", - format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()), + format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_paren()), Applicability::MachineApplicable, ); } @@ -483,12 +481,12 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind { - if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind { - if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() { - return; - } - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind + && method_name.as_str() == "sqrt" + && detect_hypot(cx, receiver).is_some() + { + return; } let maybe_neg_sugg = |expr| { @@ -566,15 +564,15 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// If the two expressions are not negations of each other, then it /// returns None. fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { - if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind { - if eq_expr_value(cx, expr1_negated, expr2) { - return Some((false, expr2)); - } + if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind + && eq_expr_value(cx, expr1_negated, expr2) + { + return Some((false, expr2)); } - if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind { - if eq_expr_value(cx, expr1, expr2_negated) { - return Some((true, expr1)); - } + if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind + && eq_expr_value(cx, expr1, expr2_negated) + { + return Some((true, expr1)); } None } @@ -591,11 +589,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { { let positive_abs_sugg = ( "manual implementation of `abs` method", - format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); let negative_abs_sugg = ( "manual implementation of negation of `abs` method", - format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); let sugg = if is_testing_positive(cx, cond, body) { if if_expr_positive { @@ -672,7 +670,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { "consider using", format!( "{}.log({})", - Sugg::hir(cx, largs_self, "..").maybe_par(), + Sugg::hir(cx, largs_self, "..").maybe_paren(), Sugg::hir(cx, rargs_self, ".."), ), Applicability::MachineApplicable, @@ -703,7 +701,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { 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()); + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_paren()); if let ExprKind::Lit(literal) = mul_lhs.kind && let ast::LitKind::Float(ref value, float_type) = literal.node && float_type == ast::LitFloatType::Unsuffixed @@ -726,7 +724,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { } 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()); + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_paren()); if let ExprKind::Lit(literal) = mul_lhs.kind && let ast::LitKind::Float(ref value, float_type) = literal.node && float_type == ast::LitFloatType::Unsuffixed diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 5e3f6b6a13707..94e66769eb265 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { .into_owned() } else { let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "", &mut applicability); - format!("{}.to_string()", sugg.maybe_par()) + format!("{}.to_string()", sugg.maybe_paren()) }; span_useless_format(cx, call_site, sugg, applicability); } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 06224f57c5c5b..8a3f8e1c5874d 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -141,7 +141,7 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// If `allow-mixed-uninlined-format-args` is set to `false` in clippy.toml, /// the following code will also trigger the lint: /// ```no_run /// # let var = 42; @@ -159,7 +159,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 5b42a40d850bb..0535ecf5240f9 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, 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 clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; +use rustc_span::Symbol; use rustc_span::symbol::kw; -use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -185,13 +185,13 @@ impl FormatImplExpr<'_, '_> { && let trait_name = match placeholder.format_trait { FormatTrait::Display => sym::Display, FormatTrait::Debug => sym::Debug, - FormatTrait::LowerExp => sym!(LowerExp), - FormatTrait::UpperExp => sym!(UpperExp), - FormatTrait::Octal => sym!(Octal), + FormatTrait::LowerExp => sym::LowerExp, + FormatTrait::UpperExp => sym::UpperExp, + FormatTrait::Octal => sym::Octal, FormatTrait::Pointer => sym::Pointer, - FormatTrait::Binary => sym!(Binary), - FormatTrait::LowerHex => sym!(LowerHex), - FormatTrait::UpperHex => sym!(UpperHex), + FormatTrait::Binary => sym::Binary, + FormatTrait::LowerHex => sym::LowerHex, + FormatTrait::UpperHex => sym::UpperHex, } && trait_name == self.format_trait_impl.name && let Ok(index) = placeholder.argument.index diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index c8fe7ac73cb34..4b482f7b233b8 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -138,27 +138,28 @@ impl EarlyLintPass for Formatting { /// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint. fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind { - if !lhs.span.from_expansion() && !rhs.span.from_expansion() { - let eq_span = lhs.span.between(rhs.span); - if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind { - if let Some(eq_snippet) = snippet_opt(cx, eq_span) { - let op = op.as_str(); - let eqop_span = lhs.span.between(sub_rhs.span); - if eq_snippet.ends_with('=') { - span_lint_and_note( - cx, - SUSPICIOUS_ASSIGNMENT_FORMATTING, - eqop_span, - format!( - "this looks like you are trying to use `.. {op}= ..`, but you \ + if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + let eq_span = lhs.span.between(rhs.span); + if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind + && let Some(eq_snippet) = snippet_opt(cx, eq_span) + { + let op = op.as_str(); + let eqop_span = lhs.span.between(sub_rhs.span); + if eq_snippet.ends_with('=') { + span_lint_and_note( + cx, + SUSPICIOUS_ASSIGNMENT_FORMATTING, + eqop_span, + format!( + "this looks like you are trying to use `.. {op}= ..`, but you \ really are doing `.. = ({op} ..)`" - ), - None, - format!("to remove this lint, use either `{op}=` or `= {op}`"), - ); - } - } + ), + None, + format!("to remove this lint, use either `{op}=` or `= {op}`"), + ); } } } 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 7361546153c27..25b087e8484f2 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 @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { }; let sugg = - Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_par(); + Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_paren(); span_lint_and_sugg( cx, 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 854fe144c2919..fa63876410f01 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind}; +use rustc_hir::{BlockCheckMode, Body, ExprKind, FnDecl, ImplicitSelfKind, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -40,14 +40,25 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: name }; - // Body must be &(mut) .name + // Body must be `&(mut) .name`, potentially in an `unsafe` block // self_data is not necessarily self, to also lint sub-getters, etc… let block_expr = if let ExprKind::Block(block, _) = body.value.kind && block.stmts.is_empty() && let Some(block_expr) = block.expr { - block_expr + if let ExprKind::Block(unsafe_block, _) = block_expr.kind + && unsafe_block.stmts.is_empty() + && matches!( + unsafe_block.rules, + BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) + && let Some(unsafe_block_expr) = unsafe_block.expr + { + unsafe_block_expr + } else { + block_expr + } } else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index 4495aeb5953e0..2d22bb157a93c 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -59,9 +59,7 @@ impl RenamedFnArgs { let mut renamed: Vec<(Span, String)> = vec![]; debug_assert!(default_idents.size_hint() == current_idents.size_hint()); - while let (Some(default_ident), Some(current_ident)) = - (default_idents.next(), current_idents.next()) - { + while let (Some(default_ident), Some(current_ident)) = (default_idents.next(), current_idents.next()) { let has_name_to_check = |ident: Option| { if let Some(ident) = ident && ident.name != kw::Underscore diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs index 05dc47f6fe580..48d050aa36aa0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs @@ -47,16 +47,16 @@ pub(super) fn check_fn( } pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { - if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind // don't lint extern functions decls, it's not their fault - if sig.header.abi == ExternAbi::Rust { - check_arg_number( - cx, - sig.decl, - item.span.with_hi(sig.decl.output.span().hi()), - too_many_arguments_threshold, - ); - } + && sig.header.abi == ExternAbi::Rust + { + check_arg_number( + cx, + sig.decl, + item.span.with_hi(sig.decl.output.span().hi()), + too_many_arguments_threshold, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index fbbd33efd02d6..9e94280fc0746 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { |diag| { let mut app = Applicability::MachineApplicable; let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) - .maybe_par() + .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; let method_body = if let Some(first_stmt) = then_block.stmts.first() { diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 5f95464e4d494..076017a247b4b 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; +use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -134,6 +134,10 @@ fn lint_implicit_returns( }, ExprKind::Match(_, arms, _) => { + if let Some(await_expr) = desugar_await(expr) { + lint_return(cx, await_expr.hir_id, await_expr.span); + return LintLocation::Inner; + } for arm in arms { let res = lint_implicit_returns( cx, @@ -153,18 +157,18 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; let _: Option = for_each_expr_without_closures(block, |e| { - if let ExprKind::Break(dest, sub_expr) = e.kind { - if dest.target_id.ok() == Some(expr.hir_id) { - if call_site_span.is_none() && e.span.ctxt() == ctxt { - // At this point sub_expr can be `None` in async functions which either diverge, or return - // the unit type. - if let Some(sub_expr) = sub_expr { - lint_break(cx, e.hir_id, e.span, sub_expr.span); - } - } else { - // the break expression is from a macro call, add a return to the loop - add_return = true; + if let ExprKind::Break(dest, sub_expr) = e.kind + && dest.target_id.ok() == Some(expr.hir_id) + { + if call_site_span.is_none() && e.span.ctxt() == ctxt { + // At this point sub_expr can be `None` in async functions which either diverge, or return + // the unit type. + if let Some(sub_expr) = sub_expr { + lint_break(cx, e.hir_id, e.span, sub_expr.span); } + } else { + // the break expression is from a macro call, add a return to the loop + add_return = true; } } ControlFlow::Continue(()) @@ -241,6 +245,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { Some(e) => e, None => return, } + } else if let Some(expr) = get_async_closure_expr(cx.tcx, body.value) { + expr } else { body.value }; 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 5d7d3ae3f24b7..514e72a48682d 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -239,7 +239,7 @@ fn check_subtraction( // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) - && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_par) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren) && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { let sugg = format!( 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 430f24111832c..6b89abdb0367f 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 @@ -8,7 +8,7 @@ use rustc_hir::{ }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; +use rustc_middle::ty::{self, AssocItem, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -315,7 +315,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { assocs .filter_by_name_unhygienic(constraint.ident.name) .next() - .is_some_and(|assoc| assoc.is_type()) + .is_some_and(AssocItem::is_type) }) { emit_lint(cx, poly_trait, bounds, index, implied_constraints, bound); 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 e1dd7872b9d48..e6129757e560f 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -65,13 +65,13 @@ declare_clippy_lint! { } pub struct InconsistentStructConstructor { - lint_inconsistent_struct_field_initializers: bool, + check_inconsistent_struct_field_initializers: bool, } impl InconsistentStructConstructor { pub fn new(conf: &'static Conf) -> Self { Self { - lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + check_inconsistent_struct_field_initializers: conf.check_inconsistent_struct_field_initializers, } } } @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); let applicability = if all_fields_are_shorthand { Applicability::MachineApplicable - } else if self.lint_inconsistent_struct_field_initializers { + } else if self.check_inconsistent_struct_field_initializers { Applicability::MaybeIncorrect } else { return; diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 33431385c7de3..99a393b4d53af 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -2,13 +2,12 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; -use clippy_utils::{higher, is_from_proc_macro, is_in_test}; +use clippy_utils::{higher, is_from_proc_macro, is_in_test, sym}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -136,28 +135,28 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { let const_range = to_const_range(cx, range, size); - if let (Some(start), _) = const_range { - if start > size { - span_lint( - cx, - OUT_OF_BOUNDS_INDEXING, - range.start.map_or(expr.span, |start| start.span), - "range is out of bounds", - ); - return; - } + if let (Some(start), _) = const_range + && start > size + { + span_lint( + cx, + OUT_OF_BOUNDS_INDEXING, + range.start.map_or(expr.span, |start| start.span), + "range is out of bounds", + ); + return; } - if let (_, Some(end)) = const_range { - if end > size { - span_lint( - cx, - OUT_OF_BOUNDS_INDEXING, - range.end.map_or(expr.span, |end| end.span), - "range is out of bounds", - ); - return; - } + if let (_, Some(end)) = const_range + && end > size + { + span_lint( + cx, + OUT_OF_BOUNDS_INDEXING, + range.end.map_or(expr.span, |end| end.span), + "range is out of bounds", + ); + return; } if let (Some(_), Some(_)) = const_range { @@ -268,7 +267,7 @@ fn ty_has_applicable_get_function<'tcx>( index_expr: &Expr<'_>, ) -> bool { if let ty::Adt(_, _) = array_ty.kind() - && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| { + && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym::get).map(|m| { cx.tcx .fn_sig(m.def_id) .skip_binder() diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 960b9aa032bea..427a1f8255553 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -156,11 +156,12 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name.as_str() == "flat_map" && args.len() == 1 { - if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { - let body = cx.tcx.hir_body(body); - return is_infinite(cx, body.value); - } + if method.ident.name.as_str() == "flat_map" + && args.len() == 1 + && let ExprKind::Closure(&Closure { body, .. }) = args[0].kind + { + let body = cx.tcx.hir_body(body); + return is_infinite(cx, body.value); } Finite }, diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 4ae1119ab3a27..91f65d0b79ca0 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -123,7 +123,7 @@ fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg expr.span, "manual implementation of `Instant::elapsed`", "try", - format!("{}.elapsed()", sugg.maybe_par()), + format!("{}.elapsed()", sugg.maybe_paren()), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs index fc575bff7e63f..67ce57de254d3 100644 --- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -130,14 +130,14 @@ impl IntPlusOne { BinOpKind::Le => "<", _ => return None, }; - if let Some(snippet) = node.span.get_source_text(cx) { - if let Some(other_side_snippet) = other_side.span.get_source_text(cx) { - let rec = match side { - Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), - Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), - }; - return rec; - } + if let Some(snippet) = node.span.get_source_text(cx) + && let Some(other_side_snippet) = other_side.span.get_source_text(cx) + { + let rec = match side { + Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), + Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), + }; + return rec; } None } @@ -157,10 +157,10 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { - if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec); - } + if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind + && let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) + { + Self::emit_warning(cx, item, rec); } } } diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs index b42664340d1c8..b0ecc5d52ddb8 100644 --- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs @@ -91,49 +91,49 @@ fn upcast_comparison_bounds_err<'tcx>( rhs: &'tcx Expr<'_>, invert: bool, ) { - if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true); - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false); + if let Some((lb, ub)) = lhs_bounds + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + { + if rel == Rel::Eq || rel == Rel::Ne { + if norm_rhs_val < lb || norm_rhs_val > ub { + err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); } + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val < lb + } else { + ub < norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val <= lb + } else { + ub <= norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, true); + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val >= ub + } else { + lb >= norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val > ub + } else { + lb > norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, false); } } } 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 977fd5fce15be..b1271a264b548 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -377,22 +377,21 @@ impl ItemNameRepetitions { "field name starts with the struct's name", ); } - if field_words.len() > item_name_words.len() { + if field_words.len() > item_name_words.len() // lint only if the end is not covered by the start - if field_words + && field_words .iter() .rev() .zip(item_name_words.iter().rev()) .all(|(a, b)| a == b) - { - span_lint_hir( - cx, - STRUCT_FIELD_NAMES, - field.hir_id, - field.span, - "field name ends with the struct's name", - ); - } + { + span_lint_hir( + cx, + STRUCT_FIELD_NAMES, + field.hir_id, + field.span, + "field name ends with the struct's name", + ); } } } @@ -445,57 +444,56 @@ impl LateLintPass<'_> for ItemNameRepetitions { let item_name = ident.name.as_str(); let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules { - // constants don't have surrounding modules - if !mod_camel.is_empty() { - if mod_name == &ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } + if !item.span.from_expansion() && is_present_in_source(cx, item.span) + && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules + // constants don't have surrounding modules + && !mod_camel.is_empty() + { + if mod_name == &ident.name + && let ItemKind::Mod(..) = item.kind + && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() - && cx.tcx.visibility(mod_owner_id.def_id).is_public() - && item_camel.len() > mod_camel.len() - { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); - - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); - - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name starts with its containing module's name", - ), - _ => (), - } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) - { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name ends with its containing module's name", - ); - } + // The `module_name_repetitions` lint should only trigger if the item has the module in its + // name. Having the same name is accepted. + if cx.tcx.visibility(item.owner_id).is_public() + && cx.tcx.visibility(mod_owner_id.def_id).is_public() + && item_camel.len() > mod_camel.len() + { + let matching = count_match_start(mod_camel, &item_camel); + let rmatching = count_match_end(mod_camel, &item_camel); + let nchars = mod_camel.chars().count(); + + let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); + + if matching.char_count == nchars { + match item_camel.chars().nth(nchars) { + Some(c) if is_word_beginning(c) => span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name starts with its containing module's name", + ), + _ => (), } } + if rmatching.char_count == nchars + && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name ends with its containing module's name", + ); + } } } diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 77085d52a32ed..8c71d34c95f6d 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,13 +2,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; +use clippy_utils::{fulfill_or_allowed, get_item_name, get_parent_as_impl, is_trait_method, peel_ref_operators, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ - AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, + AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, TraitItemRef, TyKind, }; @@ -16,7 +16,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FnSig, Ty}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; use rustc_span::{Ident, Span, Symbol}; use rustc_trait_selection::traits::supertrait_def_ids; @@ -143,7 +142,6 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { && 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.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()) { @@ -157,7 +155,17 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { }, _ => return, }; - check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind); + check_for_is_empty( + cx, + sig.span, + sig.decl.implicit_self, + output, + ty_id, + name, + kind, + item.hir_id(), + ty_hir_id, + ); } } @@ -180,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lt.init); - let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par(); + let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, @@ -202,7 +210,11 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { expr.span, lhs_expr, peel_ref_operators(cx, rhs_expr), - (method.ident.name == sym::ne).then_some("!").unwrap_or_default(), + if method.ident.name == sym::ne { + "!" + } else { + Default::default() + }, ); } @@ -282,15 +294,10 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Iden { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx); - let is_empty = sym!(is_empty); - let is_empty_method_found = current_and_super_traits .items() - .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) - .any(|i| { - i.is_method() - && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1 - }); + .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty)) + .any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1); if !is_empty_method_found { span_lint( @@ -442,6 +449,7 @@ fn check_is_empty_sig<'tcx>( } /// Checks if the given type has an `is_empty` method with the appropriate signature. +#[expect(clippy::too_many_arguments)] fn check_for_is_empty( cx: &LateContext<'_>, span: Span, @@ -450,6 +458,8 @@ fn check_for_is_empty( impl_ty: DefId, item_name: Symbol, item_kind: &str, + len_method_hir_id: HirId, + ty_decl_hir_id: HirId, ) { // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to // find the correct inherent impls. @@ -459,12 +469,11 @@ fn check_for_is_empty( return; }; - let is_empty = Symbol::intern("is_empty"); let is_empty = cx .tcx .inherent_impls(impl_ty) .iter() - .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) + .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty)) .find(|item| item.is_fn()); let (msg, is_empty_span, self_kind) = match is_empty { @@ -505,14 +514,16 @@ fn check_for_is_empty( Some(_) => return, }; - span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| { - if let Some(span) = is_empty_span { - db.span_note(span, "`is_empty` defined here"); - } - if let Some(self_kind) = self_kind { - db.note(output.expected_sig(self_kind)); - } - }); + if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) { + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| { + if let Some(span) = is_empty_span { + db.span_note(span, "`is_empty` defined here"); + } + if let Some(self_kind) = self_kind { + db.note(output.expected_sig(self_kind)); + } + }); + } } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { @@ -522,10 +533,10 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method - if let Some(name) = get_item_name(cx, method) { - if name.as_str() == "is_empty" { - return; - } + if let Some(name) = get_item_name(cx, method) + && name.as_str() == "is_empty" + { + return; } check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); @@ -572,7 +583,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lit1); - let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par(); + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, @@ -587,11 +598,11 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex } fn is_empty_string(expr: &Expr<'_>) -> bool { - if let ExprKind::Lit(lit) = expr.kind { - if let LitKind::Str(lit, _) = lit.node { - let lit = lit.as_str(); - return lit.is_empty(); - } + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(lit, _) = lit.node + { + let lit = lit.as_str(); + return lit.is_empty(); } false } @@ -618,11 +629,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { - let is_empty = sym!(is_empty); cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) - .filter_by_name_unhygienic(is_empty) + .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty(cx, item)) }) } @@ -630,10 +640,9 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); cx.tcx .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) + .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty(cx, item)) }), ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 3fe3cd67e1671..5fa8f6f4bf3d4 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -65,7 +65,6 @@ mod declare_clippy_lint; #[macro_use] extern crate clippy_utils; -#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; pub mod ctfe; // Very important lint, do not remove (rust#125116) @@ -205,6 +204,7 @@ mod loops; mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; +mod manual_abs_diff; mod manual_assert; mod manual_async_fn; mod manual_bits; @@ -226,7 +226,6 @@ mod manual_rotate; mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; -mod manual_unwrap_or_default; mod map_unit_fn; mod match_result_ok; mod matches; @@ -320,6 +319,7 @@ mod redundant_locals; mod redundant_pub_crate; mod redundant_slicing; mod redundant_static_lifetimes; +mod redundant_test_prefix; mod redundant_type_annotations; mod ref_option_ref; mod ref_patterns; @@ -412,21 +412,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; use utils::attr_collector::{AttrCollector, AttrStorage}; -/// Register all pre expansion lints -/// -/// Pre-expansion lints run before any macro expansion has happened. -/// -/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate -/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. -/// -/// Used in `./src/driver.rs`. -pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); - - store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); -} - #[derive(Default)] struct RegistrationGroups { all: Vec, @@ -439,8 +424,6 @@ struct RegistrationGroups { restriction: Vec, style: Vec, suspicious: Vec, - #[cfg(feature = "internal")] - internal: Vec, } impl RegistrationGroups { @@ -456,8 +439,6 @@ impl RegistrationGroups { store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); store.register_group(true, "clippy::style", Some("clippy_style"), self.style); store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); - #[cfg(feature = "internal")] - store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal); } } @@ -472,8 +453,6 @@ pub(crate) enum LintCategory { Restriction, Style, Suspicious, - #[cfg(feature = "internal")] - Internal, } #[allow(clippy::enum_glob_use)] @@ -495,8 +474,6 @@ impl LintCategory { Restriction => &mut groups.restriction, Style => &mut groups.style, Suspicious => &mut groups.suspicious, - #[cfg(feature = "internal")] - Internal => &mut groups.internal, } } } @@ -530,8 +507,6 @@ impl LintInfo { Restriction => "restriction", Style => "style", Suspicious => "suspicious", - #[cfg(feature = "internal")] - Internal => "internal", } } } @@ -589,6 +564,13 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_removed(name, reason); } + // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate + // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); + + store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); + let format_args_storage = FormatArgsStorage::default(); let format_args = format_args_storage.clone(); store.register_early_pass(move || { @@ -601,30 +583,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let attrs = attr_storage.clone(); store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone()))); - // all the internal lints - #[cfg(feature = "internal")] - { - store.register_early_pass(|| { - Box::new(utils::internal_lints::unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths) - }); - store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); - store.register_late_pass(|_| { - Box::::default() - }); - store.register_late_pass(|_| { - Box::::default() - }); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); - store.register_late_pass(|_| { - Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new()) - }); - store.register_late_pass(|_| Box::new(utils::internal_lints::slow_symbol_comparisons::SlowSymbolComparisons)); - } - store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe)); store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); @@ -772,7 +730,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); + store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); @@ -857,7 +815,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone()))); store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf))); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); - store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets)); + store.register_late_pass(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())); 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)); @@ -879,6 +837,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); + store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); @@ -960,7 +919,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); @@ -971,7 +929,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); - store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); + store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); @@ -984,5 +942,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); + store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); // 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 dabef18b98a90..5ef5e3a44f85f 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -150,10 +150,10 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } = item.kind { check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv); - } else if let ItemKind::Impl(impl_) = item.kind { - if !item.span.from_expansion() { - report_extra_impl_lifetimes(cx, impl_); - } + } else if let ItemKind::Impl(impl_) = item.kind + && !item.span.from_expansion() + { + report_extra_impl_lifetimes(cx, impl_); } } @@ -300,8 +300,8 @@ fn could_use_elision<'tcx>( let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; - if let Some(trait_sig) = trait_sig - && non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv) + if let Some(&[trait_sig]) = trait_sig + && non_elidable_self_type(cx, func, trait_sig, msrv) { return None; } @@ -310,11 +310,11 @@ fn could_use_elision<'tcx>( let body = cx.tcx.hir_body(body_id); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); - if non_elidable_self_type(cx, func, Some(first_ident), msrv) { + if non_elidable_self_type(cx, func, first_ident, msrv) { return None; } - let mut checker = BodyLifetimeChecker; + let mut checker = BodyLifetimeChecker::new(cx); if checker.visit_expr(body.value).is_break() { return None; } @@ -384,8 +384,8 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option>, msrv: Msrv) -> bool { - if let Some(Some(ident)) = ident +fn non_elidable_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option, msrv: Msrv) -> bool { + if let Some(ident) = ident && ident.name == kw::SelfLower && !func.implicit_self.has_implicit_self() && let Some(self_ty) = func.inputs.first() @@ -911,10 +911,23 @@ fn elision_suggestions( Some(suggestions) } -struct BodyLifetimeChecker; +struct BodyLifetimeChecker<'tcx> { + tcx: TyCtxt<'tcx>, +} -impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { +impl<'tcx> BodyLifetimeChecker<'tcx> { + fn new(cx: &LateContext<'tcx>) -> Self { + Self { tcx: cx.tcx } + } +} + +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker<'tcx> { type Result = ControlFlow<()>; + type NestedFilter = middle_nested_filter::OnlyBodies; + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) -> ControlFlow<()> { if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 805de23408bf5..7cbfa2d097ae5 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -387,12 +387,11 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal { - if let Some(second_size) = groups.next() { - if !groups.all(|i| i == second_size) || first > second_size { - return Err(WarningType::UnusualByteGroupings); - } - } + if (radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal) + && let Some(second_size) = groups.next() + && (!groups.all(|i| i == second_size) || first > second_size) + { + return Err(WarningType::UnusualByteGroupings); } if let Some(second) = groups.next() { diff --git a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs index 975e6833a35f8..244e7c95122e0 100644 --- a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs @@ -45,15 +45,14 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option = ControlFlow::Continue(()); + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr<'_>, body: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = iterable.kind + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(iterable.hir_id) + && cx.tcx.is_diagnostic_item(sym::enumerate_method, method_id) + && let ExprKind::MethodCall(_, chars_recv, _, chars_span) = enumerate_recv.kind + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(enumerate_recv.hir_id) + && cx.tcx.is_diagnostic_item(sym::str_chars, method_id) + { + if let PatKind::Tuple([pat, _], _) = pat.kind + && let PatKind::Binding(_, binding_id, ..) = pat.kind + { + // Destructured iterator element `(idx, _)`, look for uses of the binding + for_each_expr(cx, body, |expr| { + if path_to_local_id(expr, binding_id) { + check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); + } + CONTINUE + }); + } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { + // Bound as a tuple, look for `tup.0` + for_each_expr(cx, body, |expr| { + if let ExprKind::Field(e, field) = expr.kind + && path_to_local_id(e, binding_id) + && field.name == sym::integer(0) + { + check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); + } + CONTINUE + }); + } + } +} + +fn check_index_usage<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + pat: &Pat<'_>, + enumerate_span: Span, + chars_span: Span, + chars_recv: &Expr<'_>, +) { + let Some(parent_expr) = index_consumed_at(cx, expr) else { + return; + }; + + let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let message = match parent_expr.kind { + ExprKind::MethodCall(segment, recv, ..) + // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here + // (contrary to the `ExprKind::Index` case which needs to handle both with `is_string_like` because `String` implements + // `Index` directly and no deref to `str` would happen in that case). + if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() + && BYTE_INDEX_METHODS.contains(&segment.ident.name.as_str()) + && eq_expr_value(cx, chars_recv, recv) => + { + "passing a character position to a method that expects a byte index" + }, + ExprKind::Index(target, ..) + if is_string_like(cx.typeck_results().expr_ty_adjusted(target).peel_refs()) + && eq_expr_value(cx, chars_recv, target) => + { + "indexing into a string with a character position where a byte index is expected" + }, + _ => return, + }; + + span_lint_hir_and_then( + cx, + CHAR_INDICES_AS_BYTE_INDICES, + expr.hir_id, + expr.span, + message, + |diag| { + diag.note("a character can take up more than one byte, so they are not interchangeable") + .span_note( + MultiSpan::from_spans(vec![pat.span, enumerate_span]), + "position comes from the enumerate iterator", + ) + .span_suggestion_verbose( + chars_span.to(enumerate_span), + "consider using `.char_indices()` instead", + "char_indices()", + Applicability::MaybeIncorrect, + ); + }, + ); +} + +/// Returns the expression which ultimately consumes the index. +/// This is usually the parent expression, i.e. `.split_at(idx)` for `idx`, +/// but for `.get(..idx)` we want to consider the method call the consuming expression, +/// which requires skipping past the range expression. +fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + match node { + Node::Expr(expr) if higher::Range::hir(expr).is_some() => {}, + Node::ExprField(_) => {}, + Node::Expr(expr) => return Some(expr), + _ => break, + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 412c78cc80411..d0b26c91ffafb 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -2,6 +2,7 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{ implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, @@ -11,7 +12,6 @@ use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, EarlyBinder, Ty}; -use rustc_span::sym; pub(super) fn check( cx: &LateContext<'_>, @@ -119,7 +119,7 @@ fn is_ref_iterable<'tcx>( && let typing_env = ty::TypingEnv::non_body_analysis(cx.tcx, fn_id) && implements_trait_with_env(cx.tcx, typing_env, req_self_ty, trait_id, Some(fn_id), &[]) && let Some(into_iter_ty) = - make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym!(IntoIter), [req_self_ty]) + make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym::IntoIter, [req_self_ty]) && let req_res_ty = normalize_with_regions(cx.tcx, typing_env, req_res_ty) && into_iter_ty == req_res_ty { @@ -152,7 +152,7 @@ fn is_ref_iterable<'tcx>( // Using by value won't consume anything if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::None, self_ty)); @@ -169,7 +169,7 @@ fn is_ref_iterable<'tcx>( }; if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::reborrow(mutbl), self_ty)); @@ -183,7 +183,7 @@ fn is_ref_iterable<'tcx>( let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl); if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::borrow(mutbl), self_ty)); @@ -206,7 +206,7 @@ fn is_ref_iterable<'tcx>( && target != self_ty && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::auto_reborrow(mutbl), target)) @@ -224,7 +224,7 @@ fn is_ref_iterable<'tcx>( if is_copy(cx, target) && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::Deref, target)) @@ -242,7 +242,7 @@ fn is_ref_iterable<'tcx>( if self_ty.is_ref() && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::auto_borrow(mutbl), target)) diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index 185d834becafc..e314bc2068b30 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -13,45 +13,45 @@ use rustc_span::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { let pat_span = pat.span; - if let PatKind::Tuple(pat, _) = pat.kind { - if pat.len() == 2 { - let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { - ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { - (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), - (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), - _ => return, - }, + if let PatKind::Tuple(pat, _) = pat.kind + && pat.len() == 2 + { + let arg_span = arg.span; + let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { + ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { + (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), + (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), _ => return, - }; - let mutbl = match mutbl { - Mutability::Not => "", - Mutability::Mut => "_mut", - }; - let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, - _ => arg, - }; + }, + _ => return, + }; + let mutbl = match mutbl { + Mutability::Not => "", + Mutability::Mut => "_mut", + }; + let arg = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, + _ => arg, + }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { - span_lint_and_then( - cx, - FOR_KV_MAP, - arg_span, - format!("you seem to want to iterate on a map's {kind}s"), - |diag| { - let map = sugg::Sugg::hir(cx, arg, "map"); - diag.multipart_suggestion( - "use the corresponding method", - vec![ - (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), - ], - Applicability::MachineApplicable, - ); - }, - ); - } + if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + span_lint_and_then( + cx, + FOR_KV_MAP, + arg_span, + format!("you seem to want to iterate on a map's {kind}s"), + |diag| { + let map = sugg::Sugg::hir(cx, arg, "map"); + diag.multipart_suggestion( + "use the corresponding method", + vec![ + (pat_span, snippet(cx, new_pat_span, kind).into_owned()), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())), + ], + Applicability::MachineApplicable, + ); + }, + ); } } } 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 aa8a2934f89bd..35737f3eafe23 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -3,6 +3,7 @@ use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -35,6 +36,7 @@ pub(super) fn check<'tcx>( && 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) + && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { let mut applicability = Applicability::MachineApplicable; 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 701567a7d84e7..d9c4b526da99e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -28,37 +28,37 @@ pub(super) fn check<'tcx>( end: Some(end), limits, }) = higher::Range::hir(arg) - { // the var must be a single name - if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let mut starts = vec![Start { - id: canonical_id, - kind: StartKind::Range, - }]; - - // This is one of few ways to return different iterators - // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 - let mut iter_a = None; - let mut iter_b = None; - - if let ExprKind::Block(block, _) = body.kind { - if let Some(loop_counters) = get_loop_counters(cx, block, expr) { - starts.extend(loop_counters); - } - iter_a = Some(get_assignments(block, &starts)); - } else { - iter_b = Some(get_assignment(body)); + && let PatKind::Binding(_, canonical_id, _, _) = pat.kind + { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; + + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); } + iter_a = Some(get_assignments(block, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } - let assignments = iter_a.into_iter().flatten().chain(iter_b); + let assignments = iter_a.into_iter().flatten().chain(iter_b); - let big_sugg = assignments - // The only statements in the for loops can be indexed assignments from - // indexed retrievals (except increments of loop counters). - .map(|o| { - o.and_then(|(lhs, rhs)| { - let rhs = fetch_cloned_expr(rhs); - if let ExprKind::Index(base_left, idx_left, _) = lhs.kind + let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). + .map(|o| { + o.and_then(|(lhs, rhs)| { + let rhs = fetch_cloned_expr(rhs); + 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() @@ -68,42 +68,41 @@ pub(super) fn check<'tcx>( && !local_used_in(cx, canonical_id, base_right) // Source and destination must be different && 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 - } - }) + { + 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 + } }) - .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) - .collect::>>() - .filter(|v| !v.is_empty()) - .map(|v| v.join("\n ")); - - if let Some(big_sugg) = big_sugg { - span_lint_and_sugg( - cx, - MANUAL_MEMCPY, - expr.span, - "it looks like you're manually copying between slices", - "try replacing the loop by", - big_sugg, - Applicability::Unspecified, - ); - return true; - } + }) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) + .collect::>>() + .filter(|v| !v.is_empty()) + .map(|v| v.join("\n ")); + + if let Some(big_sugg) = big_sugg { + span_lint_and_sugg( + cx, + MANUAL_MEMCPY, + expr.span, + "it looks like you're manually copying between slices", + "try replacing the loop by", + big_sugg, + Applicability::Unspecified, + ); + return true; } } false @@ -184,7 +183,12 @@ fn build_manual_memcpy_suggestion<'tcx>( { dst_base_str } else { - format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() + format!( + "{dst_base_str}[{}..{}]", + dst_offset.maybe_paren(), + dst_limit.maybe_paren() + ) + .into() }; let method_str = if is_copy(cx, elem_ty) { @@ -196,7 +200,12 @@ fn build_manual_memcpy_suggestion<'tcx>( 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!( + "{src_base_str}[{}..{}]", + src_offset.maybe_paren(), + src_limit.maybe_paren() + ) + .into() }; format!("{dst}.{method_str}(&{src});") diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs index 4473a3343c7c6..9527e258db8ab 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs @@ -81,15 +81,15 @@ fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, } fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) { - if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind { - if let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind { - let offending_arg = args - .iter() - .find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span)); + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind + { + let offending_arg = args + .iter() + .find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span)); - if let Some(offending_arg) = offending_arg { - report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span); - } + if let Some(offending_arg) = offending_arg { + report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 4b0bf5a4b3c94..2b66827e82eeb 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -1,3 +1,4 @@ +mod char_indices_as_byte_indices; mod empty_loop; mod explicit_counter_loop; mod explicit_into_iter_loop; @@ -740,6 +741,49 @@ declare_clippy_lint! { "manually filling a slice with a value" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of a character position yielded by `.chars().enumerate()` in a context where a **byte index** is expected, + /// such as an argument to a specific `str` method or indexing into a `str` or `String`. + /// + /// ### Why is this bad? + /// A character (more specifically, a Unicode scalar value) that is yielded by `str::chars` can take up multiple bytes, + /// so a character position does not necessarily have the same byte index at which the character is stored. + /// Thus, using the character position where a byte index is expected can unexpectedly return wrong values + /// or panic when the string consists of multibyte characters. + /// + /// For example, the character `a` in `äa` is stored at byte index 2 but has the character position 1. + /// Using the character position 1 to index into the string will lead to a panic as it is in the middle of the first character. + /// + /// Instead of `.chars().enumerate()`, the correct iterator to use is `.char_indices()`, which yields byte indices. + /// + /// This pattern is technically fine if the strings are known to only use the ASCII subset, + /// though in those cases it would be better to use `bytes()` directly to make the intent clearer, + /// but there is also no downside to just using `.char_indices()` directly and supporting non-ASCII strings. + /// + /// You may also want to read the [chapter on strings in the Rust Book](https://doc.rust-lang.org/book/ch08-02-strings.html) + /// which goes into this in more detail. + /// + /// ### Example + /// ```no_run + /// # let s = "..."; + /// for (idx, c) in s.chars().enumerate() { + /// let _ = s[idx..]; // ⚠️ Panics for strings consisting of multibyte characters + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # let s = "..."; + /// for (idx, c) in s.char_indices() { + /// let _ = s[idx..]; + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub CHAR_INDICES_AS_BYTE_INDICES, + correctness, + "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" +} + pub struct Loops { msrv: Msrv, enforce_iter_loop_reborrow: bool, @@ -777,6 +821,7 @@ impl_lint_pass!(Loops => [ UNUSED_ENUMERATE_INDEX, INFINITE_LOOP, MANUAL_SLICE_FILL, + CHAR_INDICES_AS_BYTE_INDICES, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -860,6 +905,7 @@ impl Loops { manual_flatten::check(cx, pat, arg, body, span, self.msrv); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); + char_indices_as_byte_indices::check(cx, pat, arg, body); } fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { 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 a24dd44790e1f..70ca452013f91 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 @@ -82,14 +82,14 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if bk == ty::BorrowKind::Mutable { - if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); - } - if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); - } + if bk == ty::BorrowKind::Mutable + && let PlaceBase::Local(id) = cmt.place.base + { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); + } + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } 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 0f62183eb33d6..7837b18bcd36c 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 @@ -31,155 +31,154 @@ pub(super) fn check<'tcx>( ref end, limits, }) = higher::Range::hir(arg) - { // the var must be a single name - if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { - let mut visitor = VarVisitor { - cx, - var: canonical_id, - indexed_mut: FxHashSet::default(), - indexed_indirectly: FxHashMap::default(), - indexed_directly: FxIndexMap::default(), - referenced: FxHashSet::default(), - nonindex: false, - prefer_mutable: false, - }; - walk_expr(&mut visitor, body); - - // linting condition: we only indexed one variable, and indexed it directly - if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { - let (indexed, (indexed_extent, indexed_ty)) = visitor - .indexed_directly - .into_iter() - .next() - .expect("already checked that we have exactly 1 element"); + && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind + { + let mut visitor = VarVisitor { + cx, + var: canonical_id, + indexed_mut: FxHashSet::default(), + indexed_indirectly: FxHashMap::default(), + indexed_directly: FxIndexMap::default(), + referenced: FxHashSet::default(), + nonindex: false, + prefer_mutable: false, + }; + walk_expr(&mut visitor, body); - // ensure that the indexed variable was declared before the loop, see #601 - if let Some(indexed_extent) = indexed_extent { - let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id); - let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); - let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); - if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { - return; - } - } + // linting condition: we only indexed one variable, and indexed it directly + if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { + let (indexed, (indexed_extent, indexed_ty)) = visitor + .indexed_directly + .into_iter() + .next() + .expect("already checked that we have exactly 1 element"); - // don't lint if the container that is indexed does not have .iter() method - let has_iter = has_iter_method(cx, indexed_ty); - if has_iter.is_none() { + // ensure that the indexed variable was declared before the loop, see #601 + if let Some(indexed_extent) = indexed_extent { + let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id); + let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); + let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); + if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { return; } + } - // don't lint if the container that is indexed into is also used without - // indexing - if visitor.referenced.contains(&indexed) { - return; - } + // don't lint if the container that is indexed does not have .iter() method + let has_iter = has_iter_method(cx, indexed_ty); + if has_iter.is_none() { + return; + } - let starts_at_zero = is_integer_const(cx, start, 0); + // don't lint if the container that is indexed into is also used without + // indexing + if visitor.referenced.contains(&indexed) { + return; + } - let skip = if starts_at_zero { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { - return; - } else { - format!(".skip({})", snippet(cx, start.span, "..")) - }; + let starts_at_zero = is_integer_const(cx, start, 0); - let mut end_is_start_plus_val = false; + let skip = if starts_at_zero { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { + return; + } else { + format!(".skip({})", snippet(cx, start.span, "..")) + }; - let take = if let Some(end) = *end { - let mut take_expr = end; + let mut end_is_start_plus_val = false; - if let ExprKind::Binary(ref op, left, right) = end.kind { - if op.node == BinOpKind::Add { - let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); - let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); + let take = if let Some(end) = *end { + let mut take_expr = end; - if start_equal_left { - take_expr = right; - } else if start_equal_right { - take_expr = left; - } + if let ExprKind::Binary(ref op, left, right) = end.kind + && op.node == BinOpKind::Add + { + let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); + let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); - end_is_start_plus_val = start_equal_left | start_equal_right; - } + if start_equal_left { + take_expr = right; + } else if start_equal_right { + take_expr = left; } - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { - return; - } else { - match limits { - ast::RangeLimits::Closed => { - let take_expr = sugg::Sugg::hir(cx, take_expr, ""); - format!(".take({})", take_expr + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => { - format!(".take({})", snippet(cx, take_expr.span, "..")) - }, - } - } - } else { - String::new() - }; + end_is_start_plus_val = start_equal_left | start_equal_right; + } - let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { - ("mut ", "iter_mut") + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { + return; } else { - ("", "iter") - }; + match limits { + ast::RangeLimits::Closed => { + let take_expr = sugg::Sugg::hir(cx, take_expr, ""); + format!(".take({})", take_expr + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => { + format!(".take({})", snippet(cx, take_expr.span, "..")) + }, + } + } + } else { + String::new() + }; - let take_is_empty = take.is_empty(); - let mut method_1 = take; - let mut method_2 = skip; + let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { + ("mut ", "iter_mut") + } else { + ("", "iter") + }; - if end_is_start_plus_val { - mem::swap(&mut method_1, &mut method_2); - } + let take_is_empty = take.is_empty(); + let mut method_1 = take; + let mut method_2 = skip; - if visitor.nonindex { - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - arg.span, - format!("the loop variable `{}` is used to index `{indexed}`", ident.name), - |diag| { - diag.multipart_suggestion( - "consider using an iterator and enumerate()", - vec![ - (pat.span, format!("({}, )", ident.name)), - ( - arg.span, - format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), - ), - ], - Applicability::HasPlaceholders, - ); - }, - ); + if end_is_start_plus_val { + mem::swap(&mut method_1, &mut method_2); + } + + if visitor.nonindex { + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + arg.span, + format!("the loop variable `{}` is used to index `{indexed}`", ident.name), + |diag| { + diag.multipart_suggestion( + "consider using an iterator and enumerate()", + vec![ + (pat.span, format!("({}, )", ident.name)), + ( + arg.span, + format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), + ), + ], + Applicability::HasPlaceholders, + ); + }, + ); + } else { + let repl = if starts_at_zero && take_is_empty { + format!("&{ref_mut}{indexed}") } else { - let repl = if starts_at_zero && take_is_empty { - format!("&{ref_mut}{indexed}") - } else { - format!("{indexed}.{method}(){method_1}{method_2}") - }; + format!("{indexed}.{method}(){method_1}{method_2}") + }; - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - arg.span, - format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), - |diag| { - diag.multipart_suggestion( - "consider using an iterator", - vec![(pat.span, "".to_string()), (arg.span, repl)], - Applicability::HasPlaceholders, - ); - }, - ); - } + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + arg.span, + format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), + |diag| { + diag.multipart_suggestion( + "consider using an iterator", + vec![(pat.span, "".to_string()), (arg.span, repl)], + Applicability::HasPlaceholders, + ); + }, + ); } } } @@ -346,10 +345,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } + if let ty::Ref(_, _, mutbl) = *ty.kind() + && mutbl == Mutability::Mut + { + self.prefer_mutable = true; } self.visit_expr(expr); } @@ -361,10 +360,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { iter::once(receiver).chain(args.iter()), ) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } + if let ty::Ref(_, _, mutbl) = *ty.kind() + && mutbl == Mutability::Mut + { + self.prefer_mutable = true; } self.visit_expr(expr); } diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index c3a2a38b5ec25..69c84bc7038ef 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -244,10 +244,10 @@ fn never_loop_expr<'tcx>( }); combine_seq(first, || { // checks if break targets a block instead of a loop - if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind { - if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) { - *reachable = true; - } + if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind + && let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) + { + *reachable = true; } NeverLoopResult::Diverging }) diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 3c6fbef2bda8c..2f6950b4380c3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -3,9 +3,7 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; -use rustc_hir::{ - AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind -}; +use rustc_hir::{AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -265,7 +263,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -283,12 +281,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() ), } } diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index df6e85611fb2c..9071c9c95f9d7 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,7 +5,8 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; +use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; use std::collections::BTreeMap; @@ -249,6 +250,20 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { }) .flatten() .copied() + .inspect(|&unsafe_block| { + if let LevelAndSource { + level: Level::Expect, + lint_id: Some(id), + .. + } = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block) + { + // Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon, + // which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out + // except for the one we emit the warning at, we must manually fulfill the lint + // for all unsafe blocks here. + cx.fulfill_expectation(id); + } + }) .map(|id| { // Remove the syntax context to hide "in this macro invocation" in the diagnostic. // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything diff --git a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs new file mode 100644 index 0000000000000..c515e41f242f5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs @@ -0,0 +1,152 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::HasSession as _; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects patterns like `if a > b { a - b } else { b - a }` and suggests using `a.abs_diff(b)`. + /// + /// ### Why is this bad? + /// Using `abs_diff` is shorter, more readable, and avoids control flow. + /// + /// ### Examples + /// ```no_run + /// # let (a, b) = (5_usize, 3_usize); + /// if a > b { + /// a - b + /// } else { + /// b - a + /// } + /// # ; + /// ``` + /// Use instead: + /// ```no_run + /// # let (a, b) = (5_usize, 3_usize); + /// a.abs_diff(b) + /// # ; + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_ABS_DIFF, + complexity, + "using an if-else pattern instead of `abs_diff`" +} + +impl_lint_pass!(ManualAbsDiff => [MANUAL_ABS_DIFF]); + +pub struct ManualAbsDiff { + msrv: Msrv, +} + +impl ManualAbsDiff { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +impl<'tcx> LateLintPass<'tcx> for ManualAbsDiff { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !expr.span.from_expansion() + && let Some(if_expr) = If::hir(expr) + && let Some(r#else) = if_expr.r#else + && let ExprKind::Binary(op, rhs, lhs) = if_expr.cond.kind + && let (BinOpKind::Gt | BinOpKind::Ge, mut a, mut b) | (BinOpKind::Lt | BinOpKind::Le, mut b, mut a) = + (op.node, rhs, lhs) + && let Some(ty) = self.are_ty_eligible(cx, a, b) + && is_sub_expr(cx, if_expr.then, a, b, ty) + && is_sub_expr(cx, r#else, b, a, ty) + { + span_lint_and_then( + cx, + MANUAL_ABS_DIFF, + expr.span, + "manual absolute difference pattern without using `abs_diff`", + |diag| { + if is_unsuffixed_numeral_lit(a) && !is_unsuffixed_numeral_lit(b) { + (a, b) = (b, a); + } + let applicability = { + let source_map = cx.sess().source_map(); + if span_contains_comment(source_map, if_expr.then.span) + || span_contains_comment(source_map, r#else.span) + { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + } + }; + let sugg = format!( + "{}.abs_diff({})", + Sugg::hir(cx, a, "..").maybe_paren(), + Sugg::hir(cx, b, "..") + ); + diag.span_suggestion(expr.span, "replace with `abs_diff`", sugg, applicability); + }, + ); + } + } +} + +impl ManualAbsDiff { + /// Returns a type if `a` and `b` are both of it, and this lint can be applied to that + /// type (currently, any primitive int, or a `Duration`) + fn are_ty_eligible<'tcx>(&self, cx: &LateContext<'tcx>, a: &Expr<'_>, b: &Expr<'_>) -> Option> { + let is_int = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) && self.msrv.meets(cx, msrvs::ABS_DIFF); + let is_duration = + |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); + + let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); + (a_ty == cx.typeck_results().expr_ty(b).peel_refs() && (is_int(a_ty) || is_duration(a_ty))).then_some(a_ty) + } +} + +/// Checks if the given expression is a subtraction operation between two expected expressions, +/// i.e. if `expr` is `{expected_a} - {expected_b}`. +/// +/// If `expected_ty` is a signed primitive integer, this function will only return `Some` if the +/// subtraction expr is wrapped in a cast to the equivalent unsigned int. +fn is_sub_expr( + cx: &LateContext<'_>, + expr: &Expr<'_>, + expected_a: &Expr<'_>, + expected_b: &Expr<'_>, + expected_ty: Ty<'_>, +) -> bool { + let expr = peel_blocks(expr).kind; + + if let ty::Int(ty) = expected_ty.kind() { + let unsigned = Ty::new_uint(cx.tcx, ty.to_unsigned()); + + return if let ExprKind::Cast(expr, cast_ty) = expr + && cx.typeck_results().node_type(cast_ty.hir_id) == unsigned + { + is_sub_expr(cx, expr, expected_a, expected_b, unsigned) + } else { + false + }; + } + + if let ExprKind::Binary(op, a, b) = expr + && let BinOpKind::Sub = op.node + && eq_expr_value(cx, a, expected_a) + && eq_expr_value(cx, b, expected_b) + { + true + } else { + false + } +} + +fn is_unsuffixed_numeral_lit(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Lit(lit) if lit.node.is_numeric() && lit.node.is_unsuffixed()) +} diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 83c16d4466d06..8378e15c581c6 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { ExprKind::Unary(UnOp::Not, e) => (e, ""), _ => (cond, "!"), }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_paren(); let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip}){semicolon}"); // we show to the user the suggestion without the comments, but when applying the fix, include the diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 50c8331eebab4..02afe9f0997de 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -181,7 +181,7 @@ fn maybe_emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggest make_assignment, hir_with_ignore_attr, } = suggestion; - let input = Sugg::hir(cx, input, "..").maybe_par(); + let input = Sugg::hir(cx, input, "..").maybe_paren(); let min = Sugg::hir(cx, min, ".."); let max = Sugg::hir(cx, max, ".."); let semicolon = if make_assignment.is_some() { ";" } else { "" }; diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index 9944c4f880481..444ecd5d2bb95 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -1,8 +1,9 @@ -use clippy_utils::SpanlessEq; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -11,9 +12,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; - -use clippy_config::Conf; declare_clippy_lint! { /// ### What it does @@ -141,8 +139,7 @@ fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.peel_refs().kind() { ty::Uint(_) => true, - ty::Int(_) => cx.tcx.features().enabled(Symbol::intern("int_roundings")), - + ty::Int(_) => cx.tcx.features().enabled(sym::int_roundings), _ => false, } } @@ -167,7 +164,7 @@ fn build_suggestion( rhs: &Expr<'_>, applicability: &mut Applicability, ) { - let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index faf01a276a131..8ab49bd2ea8ea 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -148,7 +148,7 @@ fn check_is_ascii( }; let default_snip = ".."; let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; if let Some((ty_span, ty)) = ty_sugg { suggestion.push((ty_span, format!("{recv}: {ty}"))); diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index 841adfec4624b..b4cd988329d32 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -1,13 +1,14 @@ -use clippy_utils::SpanlessEq; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use rustc_ast::LitKind; -use rustc_data_structures::packed::Pu128; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::ty_from_hir_ty; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Uint; -use rustc_session::declare_lint_pass; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -33,112 +34,111 @@ declare_clippy_lint! { "manually reimplementing `is_power_of_two`" } -declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); +pub struct ManualIsPowerOfTwo { + msrv: Msrv, +} -impl LateLintPass<'_> for ManualIsPowerOfTwo { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let mut applicability = Applicability::MachineApplicable; +impl_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); - if let ExprKind::Binary(bin_op, left, right) = expr.kind - && bin_op.node == BinOpKind::Eq - { - // a.count_ones() == 1 - if let ExprKind::MethodCall(method_name, receiver, [], _) = left.kind - && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() - && check_lit(right, 1) - { - build_sugg(cx, expr, receiver, &mut applicability); - } +impl ManualIsPowerOfTwo { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } - // 1 == a.count_ones() - if let ExprKind::MethodCall(method_name, receiver, [], _) = right.kind - && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() - && check_lit(left, 1) - { - build_sugg(cx, expr, receiver, &mut applicability); - } + fn build_sugg(&self, cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { + if is_in_const_context(cx) && !self.msrv.meets(cx, msrvs::CONST_IS_POWER_OF_TWO) { + return; + } - // a & (a - 1) == 0 - if let ExprKind::Binary(op1, left1, right1) = left.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, left1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && check_lit(right2, 1) - && check_lit(right, 0) - { - build_sugg(cx, expr, left1, &mut applicability); - } + let mut applicability = Applicability::MachineApplicable; + let snippet = Sugg::hir_with_applicability(cx, receiver, "_", &mut applicability); - // (a - 1) & a == 0; - if let ExprKind::Binary(op1, left1, right1) = left.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = left1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, right1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() - && check_lit(right2, 1) - && check_lit(right, 0) - { - build_sugg(cx, expr, right1, &mut applicability); - } + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + format!("{}.is_power_of_two()", snippet.maybe_paren()), + applicability, + ); + } +} - // 0 == a & (a - 1); - if let ExprKind::Binary(op1, left1, right1) = right.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, left1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && check_lit(right2, 1) - && check_lit(left, 0) +impl<'tcx> LateLintPass<'tcx> for ManualIsPowerOfTwo { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if !expr.span.from_expansion() + && let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq) + { + if let Some(a) = count_ones_receiver(cx, lhs) + && is_integer_literal(rhs, 1) { - build_sugg(cx, expr, left1, &mut applicability); - } - - // 0 == (a - 1) & a - if let ExprKind::Binary(op1, left1, right1) = right.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = left1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, right1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() - && check_lit(right2, 1) - && check_lit(left, 0) + self.build_sugg(cx, expr, a); + } else if let Some(a) = count_ones_receiver(cx, rhs) + && is_integer_literal(lhs, 1) + { + self.build_sugg(cx, expr, a); + } else if is_integer_literal(rhs, 0) + && let Some(a) = is_and_minus_one(cx, lhs) + { + self.build_sugg(cx, expr, a); + } else if is_integer_literal(lhs, 0) + && let Some(a) = is_and_minus_one(cx, rhs) { - build_sugg(cx, expr, right1, &mut applicability); + self.build_sugg(cx, expr, a); } } } } -fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, applicability: &mut Applicability) { - let snippet = snippet_with_applicability(cx, receiver.span, "..", applicability); - - span_lint_and_sugg( - cx, - MANUAL_IS_POWER_OF_TWO, - expr.span, - "manually reimplementing `is_power_of_two`", - "consider using `.is_power_of_two()`", - format!("{snippet}.is_power_of_two()"), - *applicability, - ); +/// Return the unsigned integer receiver of `.count_ones()` or the argument of +/// `::count_ones(…)`. +fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + let (method, ty, receiver) = if let ExprKind::MethodCall(method_name, receiver, [], _) = expr.kind { + (method_name, cx.typeck_results().expr_ty_adjusted(receiver), receiver) + } else if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, func_name)) = func.kind + { + (func_name, ty_from_hir_ty(cx, ty), arg) + } else { + return None; + }; + (method.ident.as_str() == "count_ones" && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) } -fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool { - if let ExprKind::Lit(lit) = expr.kind - && let LitKind::Int(Pu128(num), _) = lit.node - && num == expected_num +/// Return `greater` if `smaller == greater - 1` +fn is_one_less<'tcx>( + cx: &LateContext<'tcx>, + greater: &'tcx Expr<'tcx>, + smaller: &Expr<'tcx>, +) -> Option<&'tcx Expr<'tcx>> { + if let Some((lhs, rhs)) = unexpanded_binop_operands(smaller, BinOpKind::Sub) + && SpanlessEq::new(cx).eq_expr(greater, lhs) + && is_integer_literal(rhs, 1) + && matches!(cx.typeck_results().expr_ty_adjusted(greater).kind(), ty::Uint(_)) { - return true; + Some(greater) + } else { + None } - false } -fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { - SpanlessEq::new(cx).eq_expr(lhs, rhs) +/// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v` +fn is_and_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + let (lhs, rhs) = unexpanded_binop_operands(expr, BinOpKind::BitAnd)?; + is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs)) +} + +/// Return the operands of the `expr` binary operation if the operator is `op` and none of the +/// operands come from expansion. +fn unexpanded_binop_operands<'hir>(expr: &Expr<'hir>, op: BinOpKind) -> Option<(&'hir Expr<'hir>, &'hir Expr<'hir>)> { + if let ExprKind::Binary(binop, lhs, rhs) = expr.kind + && binop.node == op + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + Some((lhs, rhs)) + } else { + None + } } diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index 8dee29b2a0b5d..e4ad3953b671d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// _ = opt.as_slice(); /// _ = opt.as_slice(); /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.86.0"] pub MANUAL_OPTION_AS_SLICE, complexity, "manual `Option::as_slice`" diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 16dd1ad4e4784..98e8b1f5cf92c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -92,10 +92,10 @@ fn check_into_iter( && let [filter_params] = filter_body.params { if match_map_type(cx, left_expr) { - if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind { - if let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) { - make_span_lint_and_sugg(cx, parent_expr_span, sugg); - } + if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind + && let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) + { + make_span_lint_and_sugg(cx, parent_expr_span, sugg); } // Cannot lint other cases because `retain` requires two parameters } else { @@ -196,22 +196,21 @@ fn check_to_owned( && let filter_body = cx.tcx.hir_body(closure.body) && let [filter_params] = filter_body.params && msrv.meets(cx, msrvs::STRING_RETAIN) + && let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { - if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { - make_span_lint_and_sugg( - cx, - parent_expr_span, - format!( - "{}.retain(|{}| {})", - snippet(cx, left_expr.span, ".."), - snippet(cx, pat.span, ".."), - snippet(cx, filter_body.value.span, "..") - ), - ); - } - // Be conservative now. Do nothing for the `Binding` case. - // TODO: Ideally, we can rewrite the lambda by stripping one level of reference + make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain(|{}| {})", + snippet(cx, left_expr.span, ".."), + snippet(cx, pat.span, ".."), + snippet(cx, filter_body.value.span, "..") + ), + ); } + // Be conservative now. Do nothing for the `Binding` case. + // TODO: Ideally, we can rewrite the lambda by stripping one level of reference } fn make_sugg( diff --git a/src/tools/clippy/clippy_lints/src/manual_rotate.rs b/src/tools/clippy/clippy_lints/src/manual_rotate.rs index 07537fc65c08c..06ee00c2cef3c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rotate.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rotate.rs @@ -101,7 +101,7 @@ impl LateLintPass<'_> for ManualRotate { (r_shift_dir, r_amount) }; let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_par(); + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, MANUAL_ROTATE, diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs index 5c2a711b5cb23..7ca3b71206671 100644 --- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -113,15 +113,14 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>) && is_expr_kind_empty_str(&arg.kind) { warn_then_suggest(cx, span); - } else if let QPath::Resolved(_, path) = qpath { + } else if let QPath::Resolved(_, path) = qpath // From::from(...) or TryFrom::try_from(...) - if let [path_seg1, path_seg2] = path.segments - && is_expr_kind_empty_str(&arg.kind) - && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) - || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) - { - warn_then_suggest(cx, span); - } + && let [path_seg1, path_seg2] = path.segments + && is_expr_kind_empty_str(&arg.kind) + && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) + || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) + { + warn_then_suggest(cx, span); } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs deleted file mode 100644 index 87d2faa225c52..0000000000000 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ /dev/null @@ -1,212 +0,0 @@ -use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::GenericArgKind; -use rustc_session::declare_lint_pass; -use rustc_span::sym; - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, implements_trait}; -use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment}; - -declare_clippy_lint! { - /// ### What it does - /// Checks if a `match` or `if let` expression can be simplified using - /// `.unwrap_or_default()`. - /// - /// ### Why is this bad? - /// It can be done in one call with `.unwrap_or_default()`. - /// - /// ### Example - /// ```no_run - /// let x: Option = Some(String::new()); - /// let y: String = match x { - /// Some(v) => v, - /// None => String::new(), - /// }; - /// - /// let x: Option> = Some(Vec::new()); - /// let y: Vec = if let Some(v) = x { - /// v - /// } else { - /// Vec::new() - /// }; - /// ``` - /// Use instead: - /// ```no_run - /// let x: Option = Some(String::new()); - /// let y: String = x.unwrap_or_default(); - /// - /// let x: Option> = Some(Vec::new()); - /// let y: Vec = x.unwrap_or_default(); - /// ``` - #[clippy::version = "1.79.0"] - pub MANUAL_UNWRAP_OR_DEFAULT, - suspicious, - "check if a `match` or `if let` can be simplified with `unwrap_or_default`" -} - -declare_lint_pass!(ManualUnwrapOrDefault => [MANUAL_UNWRAP_OR_DEFAULT]); - -fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { - if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind - && let PatKind::Binding(_, pat_id, _, _) = pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) - || cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id)) - { - Some(pat_id) - } else { - None - } -} - -fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) - { - Some(arm.body) - } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) - { - Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { - // We consider that the `Some` check will filter it out if it's not right. - Some(arm.body) - } else { - None - } -} - -fn get_some_and_none_bodies<'tcx>( - cx: &LateContext<'tcx>, - arm1: &'tcx Arm<'tcx>, - arm2: &'tcx Arm<'tcx>, -) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { - if let Some(binding_id) = get_some(cx, arm1.pat) - && let Some(body_none) = get_none(cx, arm2) - { - Some(((arm1.body, binding_id), body_none)) - } else if let Some(binding_id) = get_some(cx, arm2.pat) - && let Some(body_none) = get_none(cx, arm1) - { - Some(((arm2.body, binding_id), body_none)) - } else { - None - } -} - -#[allow(clippy::needless_pass_by_value)] -fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) { - // Get expr_name ("if let" or "match" depending on kind of expression), the condition, the body for - // the some arm, the body for the none arm and the binding id of the some arm - let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match { - IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) - // Make sure there are no guards to keep things simple - if arm1.guard.is_none() - && arm2.guard.is_none() - // Get the some and none bodies and the binding id of the some arm - && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) => - { - ("match", condition, body_some, body_none, binding_id) - }, - IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _) - if let Some(binding_id) = get_some(cx, pat) => - { - ("if let", condition, if_expr, else_expr, binding_id) - }, - _ => { - // All other cases (match with number of arms != 2, if let without else, etc.) - return; - }, - }; - - // We check if the return type of the expression implements Default. - let expr_type = cx.typeck_results().expr_ty(expr); - if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) - && implements_trait(cx, expr_type, default_trait_id, &[]) - // We check if the initial condition implements Default. - && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) - && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() - && implements_trait(cx, condition_ty, default_trait_id, &[]) - // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. - && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind - && let Res::Local(local_id) = path.res - && local_id == binding_id - // We now check the `None` arm is calling a method equivalent to `Default::default`. - && let body_none = peel_blocks(body_none) - && is_default_equivalent(cx, body_none) - && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par) - { - // Machine applicable only if there are no comments present - let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - - // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) - .opt_def_id() - .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) - { - return span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - "replace it with", - format!("{receiver}::<{expr_type}>.unwrap_or_default()"), - applicability, - ); - } - - // We check if the expression type is still uncertain, in which case we ask the user to specify it - if !expr_type_is_certain(cx, condition) { - return span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - format!("ascribe the type {expr_type} and replace your expression with"), - format!("{receiver}.unwrap_or_default()"), - Applicability::Unspecified, - ); - } - - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - "replace it with", - format!("{receiver}.unwrap_or_default()"), - applicability, - ); - } -} - -impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) - && !expr.span.from_expansion() - && !is_in_const_context(cx) - { - handle(cx, if_let_or_match, expr); - } - } -} 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 56aead85e7c41..b607f8117eb89 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -101,10 +101,10 @@ fn is_unit_type(ty: Ty<'_>) -> bool { fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let ty::FnDef(id, _) = *ty.kind() { - if let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() { - return is_unit_type(fn_type.output()); - } + if let ty::FnDef(id, _) = *ty.kind() + && let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() + { + return is_unit_type(fn_type.output()); } false } 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 6f446bf956587..5b50efad3e44e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; @@ -99,7 +99,7 @@ fn check_arm<'tcx>( } else { String::new() }; - span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, msg, |diag| { + span_lint_hir_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.hir_id, 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}")); 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 4cc43e427ec61..abf723fa6f4ca 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -41,10 +41,10 @@ fn get_cond_expr<'tcx>( fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's // checked by `contains_unsafe_block` - if let ExprKind::Block(block, None) = expr.kind { - if block.stmts.is_empty() { - return block.expr; - } + if let ExprKind::Block(block, None) = expr.kind + && block.stmts.is_empty() + { + return block.expr; } None } @@ -61,13 +61,13 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { // } // Returns true if resolves to `Some(x)`, `false` otherwise fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { - if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) // there can be not statements in the block as they would be removed when switching to `.filter` - if let ExprKind::Call(callee, [arg]) = inner_expr.kind { - return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) - && path_to_local_id(arg, target); - } + && let ExprKind::Call(callee, [arg]) = inner_expr.kind + { + return ctxt == expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); } false } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index 576e42a564c2b..4959908dad635 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -85,7 +85,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool /// contains `Err(IDENT)`, `None` otherwise. fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> { if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind - && let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind + && let PatKind::Binding(BindingMode::NONE, _, ident, None) = &arg.kind && let res = cx.qpath_res(qpath, pat.hir_id) && let Res::Def(DefKind::Ctor(..), id) = res && let id @ Some(_) = cx.tcx.opt_parent(id) @@ -132,7 +132,7 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok } else { Applicability::MachineApplicable }; - let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); + let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let sugg = format!("{scrut}.{method}()"); // If the expression being expanded is the `if …` part of an `else if …`, it must be blockified. let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 2bf7ec8ab7dde..b64ae0b24d818 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,133 +1,219 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -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, peel_blocks, sugg}; +use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, ResultErr}; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind}; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_hir::def::Res; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_span::sym; -use super::MANUAL_UNWRAP_OR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; -pub(super) fn check_match<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - scrutinee: &'tcx Expr<'_>, - arms: &'tcx [Arm<'_>], -) { - let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some((or_arm, unwrap_arm)) = applicable_or_arm(cx, arms) { - check_and_lint(cx, expr, unwrap_arm.pat, scrutinee, unwrap_arm.body, or_arm.body, ty); +use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; + +fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { + if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind + && let PatKind::Binding(_, pat_id, _, _) = pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && let Some(lang_item) = cx.tcx.lang_items().from_def_id(def_id) + && matches!(lang_item, LangItem::OptionSome | LangItem::ResultOk) + { + Some(pat_id) + } else { + None } } -pub(super) fn check_if_let<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - let_pat: &'tcx Pat<'_>, - let_expr: &'tcx Expr<'_>, - then_expr: &'tcx Expr<'_>, - else_expr: &'tcx Expr<'_>, -) { - let ty = cx.typeck_results().expr_ty(let_expr); - let then_ty = cx.typeck_results().expr_ty(then_expr); - // The signature is `fn unwrap_or(self: Option, default: T) -> T`. - // When `expr_adjustments(then_expr).is_empty()`, `T` should equate to `default`'s type. - // Otherwise, type error will occur. - if cx.typeck_results().expr_adjustments(then_expr).is_empty() - && let rustc_middle::ty::Adt(_did, args) = ty.kind() - && let Some(some_ty) = args.first().and_then(|arg| arg.as_type()) - && some_ty != then_ty +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) { - return; + Some(arm.body) + } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) + { + Some(arm.body) + } else if let PatKind::Wild = arm.pat.kind { + // We consider that the `Some` check will filter it out if it's not right. + Some(arm.body) + } else { + None } - check_and_lint(cx, expr, let_pat, let_expr, then_expr, peel_blocks(else_expr), ty); } -fn check_and_lint<'tcx>( +fn get_some_and_none_bodies<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - let_pat: &'tcx Pat<'_>, - let_expr: &'tcx Expr<'_>, - then_expr: &'tcx Expr<'_>, - else_expr: &'tcx Expr<'_>, - ty: Ty<'tcx>, + arm1: &'tcx Arm<'tcx>, + arm2: &'tcx Arm<'tcx>, +) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { + if let Some(binding_id) = get_some(cx, arm1.pat) + && let Some(body_none) = get_none(cx, arm2) + { + Some(((arm1.body, binding_id), body_none)) + } else if let Some(binding_id) = get_some(cx, arm2.pat) + && let Some(body_none) = get_none(cx, arm1) + { + Some(((arm2.body, binding_id), body_none)) + } else { + None + } +} + +fn handle( + cx: &LateContext<'_>, + expr: &Expr<'_>, + expr_name: &'static str, + condition: &Expr<'_>, + body_some: &Expr<'_>, + body_none: &Expr<'_>, + binding_id: HirId, ) { - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = 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) - && (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(peel_blocks(then_expr), binding_hir_id) - && cx.typeck_results().expr_adjustments(then_expr).is_empty() - && let Some(ty_name) = find_type_name(cx, ty) - && let Some(or_body_snippet) = else_expr.span.get_source_text(cx) - && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() + // Only deal with situations where both alternatives return the same non-adjusted type. + if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) { + return; + } + + let expr_type = cx.typeck_results().expr_ty(expr); + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + if let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind + && let Res::Local(local_id) = path.res + && local_id == binding_id { - lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent); + // Machine applicable only if there are no comments present + let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let receiver = Sugg::hir_with_applicability(cx, condition, "_", &mut applicability).maybe_paren(); + + // We now check the `None` arm is calling a method equivalent to `Default::default`. + if !is_lint_allowed(cx, MANUAL_UNWRAP_OR_DEFAULT, expr.hir_id) + // We check if the return type of the expression implements Default. + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, expr_type, default_trait_id, &[]) + // We check if the initial condition implements Default. + && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) + && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() + && implements_trait(cx, condition_ty, default_trait_id, &[]) + && is_default_equivalent(cx, peel_blocks(body_none)) + { + // We now check if the condition is a None variant, in which case we need to specify the type + if path_res(cx, condition) + .opt_def_id() + .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) + { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}::<{expr_type}>.unwrap_or_default()"), + applicability, + ); + } + + // We check if the expression type is still uncertain, in which case we ask the user to specify it + if !expr_type_is_certain(cx, condition) { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + format!("ascribe the type {expr_type} and replace your expression with"), + format!("{receiver}.unwrap_or_default()"), + Applicability::Unspecified, + ); + } + + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}.unwrap_or_default()"), + applicability, + ); + } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) + && cx.typeck_results().expr_adjustments(body_some).is_empty() + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(indent) = indent_of(cx, expr.span) + && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + { + let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); + let mut app = Applicability::MachineApplicable; + let suggestion = Sugg::hir_with_context(cx, condition, expr.span.ctxt(), "..", &mut app).maybe_paren(); + 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 find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - 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 + match get_type_diagnostic_name(cx, ty)? { + sym::Option => Some("Option"), + sym::Result => Some("Result"), + _ => None, } } -fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(&'a Arm<'a>, &'a Arm<'a>)> { - 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::Expr(PatExpr { - hir_id, - kind: PatExprKind::Path(qpath), - .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *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] - && !contains_return_break_continue_macro(or_arm.body) +pub fn check_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + arms: &'tcx [Arm<'tcx>], +) { + if let [arm1, arm2] = arms + // Make sure there are no guards to keep things simple + && arm1.guard.is_none() + && arm2.guard.is_none() + // Get the some and none bodies and the binding id of the some arm + && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) { - Some((or_arm, unwrap_arm)) - } else { - None + handle(cx, expr, "match", scrutinee, body_some, body_none, binding_id); } } -fn lint<'tcx>( +pub fn check_if_let<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'tcx>, - scrutinee: &'tcx Expr<'_>, - ty_name: &str, - or_body_snippet: &str, - indent: usize, + expr: &'tcx Expr<'tcx>, + pat: &'tcx Pat<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + then_expr: &'tcx Expr<'tcx>, + else_expr: &'tcx Expr<'tcx>, ) { - let reindented_or_body = reindent_multiline(or_body_snippet, 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, - ); + if let Some(binding_id) = get_some(cx, pat) { + handle( + cx, + expr, + "if let", + scrutinee, + peel_blocks(then_expr), + peel_blocks(else_expr), + binding_id, + ); + } } 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 d29d1ea3e96d9..f14b69d91ce4b 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,17 +76,18 @@ where && first_attrs.is_empty() && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|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; - } + if let Some(last_pat) = last_pat_opt + && !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; - } + if let Some(pat) = arm.1 + && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) + && is_some(pat.kind) + { + return false; } } @@ -113,10 +114,10 @@ where // 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; - } + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; } span_lint_and_sugg( cx, 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 deleted file mode 100644 index dd71560e169ea..0000000000000 --- a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs +++ /dev/null @@ -1,50 +0,0 @@ -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 rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem}; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::MATCH_ON_VEC_ITEMS; - -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { - 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 let ExprKind::Index(array, index, _) = expr.kind - && is_vector(cx, array) - && !is_full_range(cx, index) - { - return Some(expr); - } - - None -} - -fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym::Vec) -} - -fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_lang_item(cx, ty, LangItem::RangeFull) -} diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs index 864923b27739d..adda35869900d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode; -use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability}; +use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context}; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind}; @@ -24,16 +24,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let bind_names = arms[0].pat.span; let match_body = peel_blocks(arms[0].body); let mut app = Applicability::MaybeIncorrect; - let mut snippet_body = snippet_block_with_context( - cx, - match_body.span, - arms[0].span.ctxt(), - "..", - Some(expr.span), - &mut app, - ) - .0 - .to_string(); + let ctxt = expr.span.ctxt(); + let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app) + .0 + .to_string(); // Do we need to add ';' to suggestion ? if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) @@ -77,10 +71,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e span, format!( "let {} = {};\n{}let {} = {snippet_body};", - snippet_with_applicability(cx, bind_names, "..", &mut app), - snippet_with_applicability(cx, matched_vars, "..", &mut app), + snippet_with_context(cx, bind_names, ctxt, "..", &mut app).0, + snippet_with_context(cx, matched_vars, ctxt, "..", &mut app).0, " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut app) + snippet_with_context(cx, pat_span, ctxt, "..", &mut app).0 ), ), None => { @@ -178,24 +172,24 @@ fn sugg_with_curlies<'a>( let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); - if let Some(parent_expr) = get_parent_expr(cx, match_expr) { - if let ExprKind::Closure { .. } = parent_expr.kind { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } + if let Some(parent_expr) = get_parent_expr(cx, match_expr) + && let ExprKind::Closure { .. } = parent_expr.kind + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); } // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) { - if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) + && let ExprKind::Match(..) = arm.body.kind + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); } let assignment_str = assignment.map_or_else(String::new, |span| { @@ -204,14 +198,17 @@ fn sugg_with_curlies<'a>( s }); + let ctxt = match_expr.span.ctxt(); let scrutinee = if needs_var_binding { format!( "let {} = {}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) + snippet_with_context(cx, bind_names, ctxt, "..", applicability).0, + snippet_with_context(cx, matched_vars, ctxt, "..", applicability).0 ) } else { - snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + snippet_with_context(cx, matched_vars, ctxt, "..", applicability) + .0 + .to_string() }; format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") 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 df1b83cbb516a..65b93a095b926 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 @@ -26,10 +26,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm && let ty::Str = ty.kind() { let mut visitor = MatchExprVisitor { cx }; - if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) { - 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 ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) + && 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()); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 11b588b33554d..24b4a6758004f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -80,18 +80,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { path }, PatKind::TupleStruct(path, patterns, ..) => { - if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - missing_variants.retain(|e| e.ctor_def_id() != Some(id)); - } + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() + && arm.guard.is_none() + && patterns.iter().all(|p| !is_refutable(cx, p)) + { + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } path }, PatKind::Struct(path, patterns, ..) => { - if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { - missing_variants.retain(|e| e.def_id != id); - } + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() + && arm.guard.is_none() + && patterns.iter().all(|p| !is_refutable(cx, p.pat)) + { + missing_variants.retain(|e| e.def_id != id); } path }, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index d0d2025878e48..8ce8453360f78 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 @@ -26,11 +26,12 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' if !matching_wild { // Looking for unused bindings (i.e.: `_e`) for pat in inner { - if let PatKind::Binding(_, id, ident, None) = pat.kind { - if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { - ident_bind_name = ident.name; - matching_wild = true; - } + if let PatKind::Binding(_, id, ident, None) = pat.kind + && ident.as_str().starts_with('_') + && !is_local_used(cx, arm.body, id) + { + ident_bind_name = ident.name; + matching_wild = true; } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 2b9173e6f4122..c6ebd6144c76f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -8,7 +8,6 @@ mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; -mod match_on_vec_items; mod match_ref_pats; mod match_same_arms; mod match_single_binding; @@ -724,38 +723,39 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `match vec[idx]` or `match vec[n..m]`. + /// Checks if a `match` or `if let` expression can be simplified using + /// `.unwrap_or_default()`. /// /// ### Why is this bad? - /// This can panic at runtime. + /// It can be done in one call with `.unwrap_or_default()`. /// /// ### Example - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; + /// ```no_run + /// let x: Option = Some(String::new()); + /// let y: String = match x { + /// Some(v) => v, + /// None => String::new(), + /// }; /// - /// match arr[idx] { - /// 0 => println!("{}", 0), - /// 1 => println!("{}", 3), - /// _ => {}, - /// } + /// let x: Option> = Some(Vec::new()); + /// let y: Vec = if let Some(v) = x { + /// v + /// } else { + /// Vec::new() + /// }; /// ``` - /// /// Use instead: - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; + /// ```no_run + /// let x: Option = Some(String::new()); + /// let y: String = x.unwrap_or_default(); /// - /// match arr.get(idx) { - /// Some(0) => println!("{}", 0), - /// Some(1) => println!("{}", 3), - /// _ => {}, - /// } + /// let x: Option> = Some(Vec::new()); + /// let y: Vec = x.unwrap_or_default(); /// ``` - #[clippy::version = "1.45.0"] - pub MATCH_ON_VEC_ITEMS, - pedantic, - "matching on vector elements can panic" + #[clippy::version = "1.79.0"] + pub MANUAL_UNWRAP_OR_DEFAULT, + suspicious, + "check if a `match` or `if let` can be simplified with `unwrap_or_default`" } declare_clippy_lint! { @@ -1040,7 +1040,7 @@ impl_lint_pass!(Matches => [ NEEDLESS_MATCH, COLLAPSIBLE_MATCH, MANUAL_UNWRAP_OR, - MATCH_ON_VEC_ITEMS, + MANUAL_UNWRAP_OR_DEFAULT, MATCH_STR_CASE_MISMATCH, SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, @@ -1118,7 +1118,6 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); - match_on_vec_items::check(cx, ex); match_str_case_mismatch::check(cx, ex, arms); redundant_guards::check(cx, arms, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 7e65d586110e5..6c5d7cab2036e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -67,10 +67,10 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) for arm in arms { let arm_expr = peel_blocks_with_stmt(arm.body); - if let Some(guard_expr) = &arm.guard { - if guard_expr.can_have_side_effects() { - return false; - } + if let Some(guard_expr) = &arm.guard + && guard_expr.can_have_side_effects() + { + return false; } if let PatKind::Wild = arm.pat.kind { 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 4184f8b9e6e8a..d3136c89178e6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -11,17 +11,17 @@ use super::MATCH_OVERLAPPING_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() { let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex)); - if !ranges.is_empty() { - if let Some((start, end)) = overlapping(&ranges) { - span_lint_and_note( - cx, - MATCH_OVERLAPPING_ARM, - start.span, - "some ranges overlap", - Some(end.span), - "overlaps with this", - ); - } + if !ranges.is_empty() + && let Some((start, end)) = overlapping(&ranges) + { + span_lint_and_note( + cx, + MATCH_OVERLAPPING_ARM, + start.span, + "some ranges overlap", + Some(end.span), + "overlaps with this", + ); } } } 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 722ea7042dd7f..db20be40f27ea 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 @@ -4,7 +4,7 @@ use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; -use clippy_utils::{higher, is_expn_of, is_trait_method}; +use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -12,7 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use std::fmt::Write; use std::ops::ControlFlow; @@ -138,9 +138,9 @@ fn find_method_and_type<'tcx>( Some(("is_some()", op_ty)) } else if Some(id) == lang_items.poll_ready_variant() { Some(("is_ready()", op_ty)) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) { + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym::V4)) { Some(("is_ipv4()", op_ty)) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) { + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym::V6)) { Some(("is_ipv6()", op_ty)) } else { None @@ -255,7 +255,7 @@ fn find_method_sugg_for_if_let<'tcx>( }; let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app) - .maybe_par() + .maybe_paren() .to_string(); diag.span_suggestion(span, "try", format!("{keyword} {sugg}.{good_method}"), app); @@ -279,7 +279,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op _ => op, }; let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { @@ -303,7 +303,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_par()); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } span_lint_and_sugg( @@ -345,8 +345,8 @@ fn found_good_method<'tcx>( arms, path_left, path_right, - Item::Diag(sym::IpAddr, sym!(V4)), - Item::Diag(sym::IpAddr, sym!(V6)), + Item::Diag(sym::IpAddr, sym::V4), + Item::Diag(sym::IpAddr, sym::V6), "is_ipv4()", "is_ipv6()", ) @@ -437,8 +437,8 @@ fn get_good_method<'tcx>( "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym!(V4)), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym!(V6)), "is_ipv6()", "is_ipv4()"), + "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), _ => return None, }; return find_good_method_for_matches_macro( diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 37bac561a6e06..d7dc7604088f7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -182,17 +182,16 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { - if let Some(adt) = ty.ty_adt_def() { - if get_attr( + if let Some(adt) = ty.ty_adt_def() + && get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop", ) .count() > 0 - { - return true; - } + { + return true; } if !self.seen_types.insert(ty) { 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 836c46240ce7b..08c0caa4266cc 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,5 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; +use clippy_utils::source::{ + SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, +}; use clippy_utils::ty::implements_trait; use clippy_utils::{ is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, @@ -34,8 +36,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { #[rustfmt::skip] pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { if let [arm1, arm2] = arms - && arm1.guard.is_none() - && arm2.guard.is_none() + && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() // don't lint for or patterns for now, this makes // the lint noisy in unnecessary situations @@ -106,7 +107,7 @@ fn report_single_pattern( format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); - if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { + if ex.span.eq_ctxt(expr.span) && snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { let msg = "this pattern is irrefutable, `match` is useless"; let (sugg, help) = if is_unit_expr(arm.body) { (String::new(), "`match` expression can be removed") @@ -163,10 +164,10 @@ fn report_single_pattern( 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, ".."), + snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), - snippet(cx, arm.pat.span, ".."), + snippet_with_applicability(cx, arm.pat.span, "..", &mut app), expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) @@ -174,8 +175,8 @@ fn report_single_pattern( 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, arm.pat.span, ".."), - snippet(cx, ex.span, ".."), + snippet_with_applicability(cx, arm.pat.span, "..", &mut app), + snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) diff --git a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs index b75d1ab9a7aa3..43102d78bfebd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs @@ -15,18 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { return; } for arm in arms { - if let PatKind::Or(fields) = arm.pat.kind { + if let PatKind::Or(fields) = arm.pat.kind // look for multiple fields in this arm that contains at least one Wild pattern - if fields.len() > 1 && fields.iter().any(is_wild) { - span_lint_and_help( - cx, - WILDCARD_IN_OR_PATTERNS, - arm.pat.span, - "wildcard pattern covers any other pattern as it will match anyway", - None, - "consider handling `_` separately", - ); - } + && fields.len() > 1 && fields.iter().any(is_wild) + { + span_lint_and_help( + cx, + WILDCARD_IN_OR_PATTERNS, + arm.pat.span, + "wildcard pattern covers any other pattern as it will match anyway", + None, + "consider handling `_` separately", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index a0919947b3fc7..a54d835b538c1 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -145,7 +145,7 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E "consider `Option::take()` instead", format!( "{}.take()", - Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par() + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_paren() ), applicability, ); @@ -178,7 +178,7 @@ fn check_replace_option_with_some( "consider `Option::replace()` instead", format!( "{}.replace({})", - Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_par(), + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_paren(), snippet_with_applicability(cx, src_arg.span, "_", &mut applicability) ), applicability, @@ -304,14 +304,12 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { && 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 !check_replace_option_with_none(cx, src, dest, expr.span) - && !check_replace_option_with_some(cx, src, dest, expr.span, self.msrv) - && !check_replace_with_default(cx, src, dest, expr, self.msrv) - { - check_replace_with_uninit(cx, src, dest, expr.span); - } + && !check_replace_option_with_none(cx, src, dest, expr.span) + && !check_replace_option_with_some(cx, src, dest, expr.span, self.msrv) + && !check_replace_with_default(cx, src, dest, expr, self.msrv) + { + check_replace_with_uninit(cx, src, dest, expr.span); } } } 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 1e9b29f567f41..f8520c23ea503 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 @@ -192,10 +192,10 @@ impl BindInsteadOfMap { } fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) { - return cx.tcx.parent(id) == variant_id; - } + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res + && let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) + { + return cx.tcx.parent(id) == variant_id; } false } 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 18568e3661fe5..d07870d4951e0 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,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; @@ -16,14 +17,15 @@ pub(super) fn check<'tcx>( call_span: Span, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, + msrv: Msrv, ) { - if let ExprKind::MethodCall(path_segment, ..) = recv.kind { - if matches!( + if let ExprKind::MethodCall(path_segment, ..) = recv.kind + && matches!( path_segment.ident.name.as_str(), "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" - ) { - return; - } + ) + { + return; } if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) @@ -58,11 +60,15 @@ pub(super) fn check<'tcx>( let suggestion_source = reindent_multiline( &format!( - "std::path::Path::new({}) + "std::path::Path::new({recv_source}) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_source, - ext_str.strip_prefix('.').unwrap() + .{}|ext| ext.eq_ignore_ascii_case(\"{}\"))", + if msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND) { + "is_some_and(" + } else { + "map_or(false, " + }, + ext_str.strip_prefix('.').unwrap(), ), true, Some(indent_of(cx, call_span).unwrap_or(0) + 4), diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 1ee27d90d0545..2ecf3eb897988 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -40,10 +40,10 @@ pub(super) fn check( .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind() { - if let ty::Ref(..) = inner.kind() { - return; // don't report clone_on_copy - } + if let ty::Ref(_, inner, _) = arg_ty.kind() + && let ty::Ref(..) = inner.kind() + { + return; // don't report clone_on_copy } if is_copy(cx, ty) { diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs index b5adc69e9a790..e666f31217cc8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; use clippy_utils::{is_mutable, is_trait_method, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; @@ -27,10 +27,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name().as_str() == "last") // if the resolved method is the same as the provided definition && fn_def.def_id() == last_def.def_id + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && !has_non_owning_mutable_access(cx, self_ty) { let mut sugg = vec![(call_span, String::from("next_back()"))]; let mut dont_apply = false; + // if `self_expr` is a reference, it is mutable because it is used for `.last()` + // TODO: Change this to lint only when the referred iterator is not used later. If it is used later, + // changing to `next_back()` may change its behavior. if !(is_mutable(cx, self_expr) || self_type.is_ref()) { if let Some(hir_id) = path_to_local(self_expr) && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) 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 daa6e0e7f940c..f5688e370a478 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 @@ -54,10 +54,11 @@ pub(super) fn check<'tcx>( if is_type_lang_item(cx, arg_ty, hir::LangItem::String) { return false; } - if let ty::Ref(_, ty, ..) = arg_ty.kind() { - if ty.is_str() && can_be_static_str(cx, arg) { - return false; - } + if let ty::Ref(_, ty, ..) = arg_ty.kind() + && ty.is_str() + && can_be_static_str(cx, arg) + { + return false; } true } 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 ae300cd5fe56d..da123f13d46fa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -11,7 +11,7 @@ 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::{Ident, Symbol, sym}; +use rustc_span::symbol::{Ident, Symbol}; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; @@ -43,10 +43,10 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool } fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { - is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym::is_some) } fn is_ok_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { - is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_ok)) + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym::is_ok) } #[derive(Debug, Copy, Clone)] @@ -429,16 +429,15 @@ fn is_find_or_filter<'a>( } fn acceptable_methods(method: &PathSegment<'_>) -> bool { - let methods: [Symbol; 8] = [ - sym::clone, - sym::as_ref, - sym!(copied), - sym!(cloned), - sym!(as_deref), - sym!(as_mut), - sym!(as_deref_mut), - sym!(to_owned), - ]; - - methods.contains(&method.ident.name) + matches!( + method.ident.name, + sym::clone + | sym::as_ref + | sym::copied + | sym::cloned + | sym::as_deref + | sym::as_mut + | sym::as_deref_mut + | sym::to_owned + ) } diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index f7e116c5310ed..965993808f6b5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,10 +1,14 @@ use super::FILTER_MAP_BOOL_THEN; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; -use clippy_utils::{is_from_proc_macro, is_trait_method, peel_blocks}; +use clippy_utils::{ + CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, +}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, HirId, Param, Pat}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Binder; use rustc_middle::ty::adjustment::Adjust; @@ -44,17 +48,69 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(filter) = recv.span.get_source_text(cx) && let Some(map) = then_body.span.get_source_text(cx) { - span_lint_and_sugg( + span_lint_and_then( cx, FILTER_MAP_BOOL_THEN, call_span, "usage of `bool::then` in `filter_map`", - "use `filter` then `map` instead", - format!( - "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", - derefs = "*".repeat(needed_derefs) - ), - Applicability::MachineApplicable, + |diag| { + if can_filter_and_then_move_to_closure(cx, ¶m, recv, then_body) { + diag.span_suggestion( + call_span, + "use `filter` then `map` instead", + format!( + "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", + derefs = "*".repeat(needed_derefs) + ), + Applicability::MachineApplicable, + ); + } else { + diag.help("consider using `filter` then `map` instead"); + } + }, ); } } + +/// Returns a set of all bindings found in the given pattern. +fn find_bindings_from_pat(pat: &Pat<'_>) -> FxHashSet { + let mut bindings = FxHashSet::default(); + pat.walk(|p| { + if let rustc_hir::PatKind::Binding(_, hir_id, _, _) = p.kind { + bindings.insert(hir_id); + } + true + }); + bindings +} + +/// Returns true if we can take a closure parameter and have it in both the `filter` function and +/// the`map` function. This is not the case if: +/// +/// - The `filter` would contain an early return, +/// - `filter` and `then` contain captures, and any of those are &mut +fn can_filter_and_then_move_to_closure<'tcx>( + cx: &LateContext<'tcx>, + param: &Param<'tcx>, + filter: &'tcx Expr<'tcx>, + then: &'tcx Expr<'tcx>, +) -> bool { + if contains_return(filter) { + return false; + } + + let Some(filter_captures) = can_move_expr_to_closure(cx, filter) else { + return true; + }; + let Some(then_captures) = can_move_expr_to_closure(cx, then) else { + return true; + }; + + let param_bindings = find_bindings_from_pat(param.pat); + filter_captures.iter().all(|(hir_id, filter_cap)| { + param_bindings.contains(hir_id) + || !then_captures + .get(hir_id) + .is_some_and(|then_cap| matches!(*filter_cap | *then_cap, CaptureKind::Ref(Mutability::Mut))) + }) +} 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 f4840785584ef..045363058d198 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 @@ -1,25 +1,31 @@ +use std::fmt::Write as _; + use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::{self as hir, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::GenericParamDefKind; 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<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { 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 mut app = Applicability::MaybeIncorrect; + let turbofish = match func.kind { + ExprKind::Path(QPath::TypeRelative(hir_ty, _)) => build_full_type(cx, hir_ty, &mut app), + ExprKind::Path(QPath::Resolved(Some(self_ty), _)) => build_full_type(cx, self_ty, &mut app), + _ => return, + }; + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_paren(); let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( cx, @@ -28,54 +34,47 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", sugg, - Applicability::MaybeIncorrect, + app, ); } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { - fn strip_angle_brackets(s: &str) -> Option<&str> { - s.strip_prefix('<')?.strip_suffix('>') - } - - let call_site = expr.span.source_callsite(); - if let Some(snippet) = call_site.get_source_text(cx) - && let snippet_split = snippet.split("::").collect::>() - && let Some((_, elements)) = snippet_split.split_last() +/// Build a type which can be used in a turbofish syntax from `hir_ty`, either by copying the +/// existing generic arguments with the exception of elided lifetimes, or by inserting placeholders +/// for types and consts without default values. +fn build_full_type(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, app: &mut Applicability) -> String { + if let TyKind::Path(ty_qpath) = hir_ty.kind + && let QPath::Resolved(None, ty_path) = &ty_qpath + && let Res::Def(_, ty_did) = ty_path.res { - 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() + let mut ty_str = itertools::join(ty_path.segments.iter().map(|s| s.ident), "::"); + let mut first = true; + let mut append = |arg: &str| { + write!(&mut ty_str, "{}{arg}", [", ", "<"][usize::from(first)]).unwrap(); + first = false; + }; + if let Some(args) = ty_path.segments.last().and_then(|segment| segment.args) { + args.args + .iter() + .filter(|arg| !matches!(arg, GenericArg::Lifetime(lt) if lt.is_elided())) + .for_each(|arg| append(&snippet_with_applicability(cx, arg.span().source_callsite(), "_", app))); } 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("::")) - } + cx.tcx + .generics_of(ty_did) + .own_params + .iter() + .filter(|param| { + matches!( + param.kind, + GenericParamDefKind::Type { has_default: false, .. } + | GenericParamDefKind::Const { has_default: false, .. } + ) + }) + .for_each(|_| append("_")); } + ty_str.push_str([">", ""][usize::from(first)]); + ty_str } else { - ty.to_string() + snippet_with_applicability(cx, hir_ty.span.source_callsite(), "_", app).into() } } diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index 4c81b22861b4c..545bef1a4c5bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -14,15 +14,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if expr.span.in_external_macro(cx.sess().source_map()) || !receiver.span.eq_ctxt(expr.span) { return; } - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(parent) = get_parent_expr(cx, parent) { - if is_inside_always_const_context(cx.tcx, expr.hir_id) - && let Some(macro_call) = root_macro_call(parent.span) - && is_assert_macro(cx, macro_call.def_id) - { - return; - } - } + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(parent) = get_parent_expr(cx, parent) + && is_inside_always_const_context(cx.tcx, expr.hir_id) + && let Some(macro_call) = root_macro_call(parent.span) + && is_assert_macro(cx, macro_call.def_id) + { + return; } let init_expr = expr_or_init(cx, receiver); if !receiver.span.eq_ctxt(init_expr.span) { 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 49de83885a1ca..17cc07b91c5da 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,16 +1,22 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{get_iterator_item_ty, is_type_diagnostic_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_middle::ty; 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 is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + let expr_ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, expr_ty, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) + && let ty::Adt(_, args) = expr_ty.kind() + && let Some(iter_item_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)) + && let ty::Ref(_, iter_item_ty, _) = iter_item_ty.kind() + && *iter_item_ty == args.type_at(0) && let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index bafabec7e0695..adeff375c8aad 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -6,12 +6,12 @@ use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::symbol::{Ident, Symbol}; /// /// Returns true if the expression is a method call to `method_name` @@ -154,7 +154,7 @@ fn expression_type( if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Option) && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() - && is_method(cx, filter_arg, sym::Option, sym!(is_some), &[]) + && is_method(cx, filter_arg, sym::Option, sym::is_some, &[]) { return Some(FilterType::IsSome); } @@ -162,7 +162,7 @@ fn expression_type( if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Result) && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() - && is_method(cx, filter_arg, sym::Result, sym!(is_ok), &[]) + && is_method(cx, filter_arg, sym::Result, sym::is_ok, &[]) { return Some(FilterType::IsOk); } 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 94415fc91061e..3ac9299ba9157 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 @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( (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) + && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs index 9b358235a40df..90d5d9df55eed 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -8,14 +8,14 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) { - if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { - span_lint( - cx, - ITERATOR_STEP_BY_ZERO, - expr.span, - "`Iterator::step_by(0)` will panic at runtime", - ); - } + if is_trait_method(cx, expr, sym::Iterator) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + { + span_lint( + cx, + ITERATOR_STEP_BY_ZERO, + expr.span, + "`Iterator::step_by(0)` will panic at runtime", + ); } } 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 13918ed11b87d..18978a1d2bc86 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 @@ -106,15 +106,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { }; let check_lit = |expr: &hir::Expr<'_>, check_min: bool| { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let ast::LitKind::Int(value, _) = lit.node { - if value == maxval { - return Some(MinMax::Max); - } - - if check_min && value == minval { - return Some(MinMax::Min); - } + if let hir::ExprKind::Lit(lit) = &expr.kind + && let ast::LitKind::Int(value, _) = lit.node + { + if value == maxval { + return Some(MinMax::Max); + } + + if check_min && value == minval { + return Some(MinMax::Min); } } @@ -125,10 +125,10 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { return r; } - if ty.is_signed() { - if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind { - return check_lit(val, true); - } + if ty.is_signed() + && let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind + { + return check_lit(val, true); } None 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 098721dc046f8..8167e4f960534 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 @@ -77,7 +77,7 @@ pub(super) fn check( s @ Cow::Borrowed(_) => s, }, RepeatKind::String => Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app) - .maybe_par() + .maybe_paren() .to_string() .into(), }; 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 128b3695f48b7..333a33f7527d6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -51,19 +51,19 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ let closure_expr = peel_blocks(closure_body.value); match closure_body.params[0].pat.kind { hir::PatKind::Ref(inner, Mutability::Not) => { - if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind { - if ident_eq(name, closure_expr) { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind + && ident_eq(name, closure_expr) + { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } }, hir::PatKind::Binding(hir::BindingMode::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); - } + if ident_eq(name, inner) + && 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, [], _) => { @@ -114,19 +114,17 @@ fn handle_path( ) { if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() && cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) - { // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option` // and `Result`. - if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() - && let args = args.as_slice() - && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) - && let ty::Ref(_, ty, Mutability::Not) = ty.kind() - && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() - && lst.iter().all(|l| l.as_type() == Some(*ty)) - && !should_call_clone_as_function(cx, *ty) - { - lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); - } + && let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() + && let args = args.as_slice() + && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) + && let ty::Ref(_, ty, Mutability::Not) = ty.kind() + && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() + && lst.iter().all(|l| l.as_type() == Some(*ty)) + && !should_call_clone_as_function(cx, *ty) + { + lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 6cf0936c598fa..a2a522a60687d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -41,7 +41,7 @@ fn extract_count_with_applicability( return Some(format!("{count}")); } let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability) - .maybe_par() + .maybe_paren() .into_string(); if lower_bound == 0 { if range.limits == RangeLimits::Closed { diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 1d9296016e25f..ad374dee516cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -114,6 +114,7 @@ mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; mod suspicious_to_owned; +mod swap_with_temporary; mod type_id_on_box; mod unbuffered_bytes; mod uninit_assumed_init; @@ -478,9 +479,6 @@ declare_clippy_lint! { /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. /// - /// ### Known problems - /// The error type needs to implement `Debug` - /// /// ### Example /// ```no_run /// # let x = Ok::<_, ()>(()); @@ -2429,7 +2427,7 @@ declare_clippy_lint! { /// /// ### Limitations /// This lint currently only looks for usages of - /// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded + /// `.{then, then_some}(..).{unwrap_or, unwrap_or_else, unwrap_or_default}(..)`, but will be expanded /// to account for similar patterns. /// /// ### Example @@ -4286,7 +4284,7 @@ declare_clippy_lint! { /// ```no_run /// let last_arg = "echo hello world".split(' ').next_back(); /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.86.0"] pub DOUBLE_ENDED_ITERATOR_LAST, perf, "using `Iterator::last` on a `DoubleEndedIterator`" @@ -4478,12 +4476,59 @@ declare_clippy_lint! { /// ```no_run /// let _ = std::io::Error::other("bad".to_string()); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub IO_OTHER_ERROR, style, "calling `std::io::Error::new(std::io::ErrorKind::Other, _)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `std::mem::swap` with temporary values. + /// + /// ### Why is this bad? + /// Storing a new value in place of a temporary value which will + /// be dropped right after the `swap` is an inefficient way of performing + /// an assignment. The same result can be achieved by using a regular + /// assignment. + /// + /// ### Examples + /// ```no_run + /// fn replace_string(s: &mut String) { + /// std::mem::swap(s, &mut String::from("replaced")); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn replace_string(s: &mut String) { + /// *s = String::from("replaced"); + /// } + /// ``` + /// + /// Also, swapping two temporary values has no effect, as they will + /// both be dropped right after swapping them. This is likely an indication + /// of a bug. For example, the following code swaps the references to + /// the last element of the vectors, instead of swapping the elements + /// themselves: + /// + /// ```no_run + /// fn bug(v1: &mut [i32], v2: &mut [i32]) { + /// // Incorrect: swapping temporary references (`&mut &mut` passed to swap) + /// std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn correct(v1: &mut [i32], v2: &mut [i32]) { + /// std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap()); + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub SWAP_WITH_TEMPORARY, + complexity, + "detect swap with a temporary value" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4661,17 +4706,19 @@ impl_lint_pass!(Methods => [ UNBUFFERED_BYTES, MANUAL_CONTAINS, IO_OTHER_ERROR, + SWAP_WITH_TEMPORARY, ]); /// Extracts a method call name, args, and `Span` of the method name. pub fn method_call<'tcx>( recv: &'tcx Expr<'tcx>, ) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { - if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { - if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { - let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span, call_span)); - } + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind + && !args.iter().any(|e| e.span.from_expansion()) + && !receiver.span.from_expansion() + { + let name = path.ident.name.as_str(); + return Some((name, receiver, args, path.ident.span, call_span)); } None } @@ -4691,6 +4738,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { manual_c_str_literals::check(cx, expr, func, args, self.msrv); useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv); io_other_error::check(cx, expr, func, args, self.msrv); + swap_with_temporary::check(cx, expr, func, args); }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; @@ -4992,7 +5040,7 @@ impl Methods { }, ("ends_with", [arg]) => { if let ExprKind::MethodCall(.., span) = expr.kind { - case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); + case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv); } path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles); }, @@ -5421,15 +5469,21 @@ impl Methods { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or"); + obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, "unwrap_or"); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, ("unwrap_or_default", []) => { - if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) { - manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); + match method_call(recv) { + Some(("map", m_recv, [arg], span, _)) => { + manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); + }, + Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, None, then_method, "unwrap_or_default"); + }, + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, @@ -5441,7 +5495,15 @@ impl Methods { Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or_else"); + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + Some(u_arg), + then_method, + "unwrap_or_else", + ); }, _ => { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); 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 e4a29b6560e52..6efaba525e3ed 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -4,7 +4,9 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection}; +use clippy_utils::ty::{ + get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection, +}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id, @@ -23,6 +25,7 @@ use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, name_span: Span, @@ -30,6 +33,11 @@ pub(super) fn check<'tcx>( iter_expr: &'tcx Expr<'tcx>, call_span: Span, ) { + let iter_ty = cx.typeck_results().expr_ty(iter_expr); + if has_non_owning_mutable_access(cx, iter_ty) { + return; // don't lint if the iterator has side effects + } + match cx.tcx.parent_hir_node(collect_expr.hir_id) { Node::Expr(parent) => { check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); @@ -377,20 +385,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) { - if let Some(index) = self.hir_id_uses_map.remove(&hir_id) { - if self - .illegal_mutable_capture_ids - .intersection(&self.current_mutably_captured_ids) - .next() - .is_none() - { - if let Some(hir_id) = self.current_statement_hir_id { - self.hir_id_uses_map.insert(hir_id, index); - } - } else { - self.uses[index] = None; + if let Some(hir_id) = path_to_local(recv) + && let Some(index) = self.hir_id_uses_map.remove(&hir_id) + { + if self + .illegal_mutable_capture_ids + .intersection(&self.current_mutably_captured_ids) + .next() + .is_none() + { + if let Some(hir_id) = self.current_statement_hir_id { + self.hir_id_uses_map.insert(hir_id, index); } + } else { + self.uses[index] = None; } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index 88b9c69f6f949..cd1b97f3c51b5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -9,26 +9,27 @@ use super::NEEDLESS_OPTION_TAKE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place - if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) { - if let Some(function_name) = source_of_temporary_value(recv) { - span_lint_and_then( - cx, - NEEDLESS_OPTION_TAKE, - expr.span, - "called `Option::take()` on a temporary value", - |diag| { - diag.note(format!( - "`{function_name}` creates a temporary value, so calling take() has no effect" - )); - diag.span_suggestion( - expr.span.with_lo(recv.span.hi()), - "remove", - "", - Applicability::MachineApplicable, - ); - }, - ); - } + if !recv.is_syntactic_place_expr() + && is_expr_option(cx, recv) + && let Some(function_name) = source_of_temporary_value(recv) + { + span_lint_and_then( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + |diag| { + diag.note(format!( + "`{function_name}` creates a temporary value, so calling take() has no effect" + )); + diag.span_suggestion( + expr.span.with_lo(recv.span.hi()), + "remove", + "", + Applicability::MachineApplicable, + ); + }, + ); } } @@ -44,10 +45,10 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> { match expr.peel_borrows().kind { ExprKind::Call(function, _) => { - if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind { - if !func_path.segments.is_empty() { - return Some(func_path.segments[0].ident.name.as_str()); - } + if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind + && !func_path.segments.is_empty() + { + return Some(func_path.segments[0].ident.name.as_str()); } if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind { return Some(func_path_segment.ident.name.as_str()); diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 9a5ffdeaf4e8e..1cc56de48763e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -14,14 +14,17 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: &'tcx hir::Expr<'_>, + unwrap_arg: Option<&'tcx hir::Expr<'_>>, then_method_name: &str, unwrap_method_name: &str, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { - let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) { + let then_eager = switch_to_eager_eval(cx, then_arg); + let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + + let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect @@ -36,16 +39,17 @@ pub(super) fn check<'tcx>( _ => return, }; - // FIXME: Add `unwrap_or_else` symbol + // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol let els = match unwrap_method_name { - "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability), - "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => { + "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), + "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => { - snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()" + "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { + snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" }, + "unwrap_or_default" => "Default::default()".into(), _ => return, }; 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 6eeeea5d77c70..b78b082e460ef 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 @@ -6,13 +6,13 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, + contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use rustc_span::symbol::{self, Symbol, sym}; +use rustc_span::symbol::{self, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; @@ -66,8 +66,8 @@ pub(super) fn check<'tcx>( }; let sugg = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => sym!(unwrap_or_default), - ("or_insert", true) | ("or_insert_with", false) => sym!(or_default), + ("unwrap_or", true) | ("unwrap_or_else", false) => sym::unwrap_or_default, + ("or_insert", true) | ("or_insert_with", false) => sym::or_default, _ => return false, }; @@ -78,8 +78,7 @@ pub(super) fn check<'tcx>( .iter() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) .find_map(|assoc| { - if assoc.is_method() - && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { Some(assoc.def_id) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs index d318462e58415..8b51268da465e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs @@ -3,33 +3,33 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_enum_variant_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_enum_variant_ctor, sym}; use super::SEEK_FROM_CURRENT; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) { - if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) + && implements_trait(cx, ty, def_id, &[]) + && arg_is_seek_from_current(cx, arg) + { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEEK_FROM_CURRENT, - expr.span, - "using `SeekFrom::Current` to start from current position", - "replace with", - format!("{snip}.stream_position()"), - applicability, - ); - } + span_lint_and_sugg( + cx, + SEEK_FROM_CURRENT, + expr.span, + "using `SeekFrom::Current` to start from current position", + "replace with", + format!("{snip}.stream_position()"), + applicability, + ); } } @@ -37,14 +37,12 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) if let ExprKind::Call(f, [arg]) = expr.kind && let ExprKind::Path(ref path) = f.kind && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id() - && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id) - { + && is_enum_variant_ctor(cx, sym::SeekFrom, sym::Current, ctor_call_id) // check if argument of `SeekFrom::Current` is `0` - if let ExprKind::Lit(lit) = arg.kind - && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node - { - return true; - } + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node + { + return true; } false diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7b1dd9e58c50e..b8405a78f23a9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified}; +use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified, sym}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::SEEK_TO_START_INSTEAD_OF_REWIND; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(func, [arg]) = arg.kind && let ExprKind::Path(ref path) = func.kind && let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id) + && is_enum_variant_ctor(cx, sym::SeekFrom, sym::Start, ctor_call_id) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node { 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 4ccefb7ec9d77..d183457da25a2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -238,15 +238,14 @@ fn indirect_usage<'tcx>( unwrap_kind: Some(unwrap_kind), .. } = iter_usage + && parent_id == local_hir_id { - if parent_id == local_hir_id { - return Some(IndirectUsage { - name: ident.name, - span: stmt.span, - init_expr, - unwrap_kind, - }); - } + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); } } 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 1bd48525f12d8..788014d9bb632 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs @@ -13,11 +13,11 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr< && 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) { + 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; - } + && !map_mutated_vars.is_empty() + { + return; } span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs new file mode 100644 index 0000000000000..de729fb343a34 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use rustc_ast::BorrowKind; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{Expr, ExprKind, Node, QPath}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::SWAP_WITH_TEMPORARY; + +const MSG_TEMPORARY: &str = "this expression returns a temporary value"; +const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value"; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { + if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind + && let Some(func_def_id) = func_path.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id) + { + match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) { + (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => { + emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp); + }, + (ArgKind::RefMutToTemp(left_temp), right) => emit_lint_assign(cx, expr, &right, &args[0], left_temp), + (left, ArgKind::RefMutToTemp(right_temp)) => emit_lint_assign(cx, expr, &left, &args[1], right_temp), + _ => {}, + } + } +} + +enum ArgKind<'tcx> { + // Mutable reference to a place, coming from a macro + RefMutToPlaceAsMacro(&'tcx Expr<'tcx>), + // Place behind a mutable reference + RefMutToPlace(&'tcx Expr<'tcx>), + // Temporary value behind a mutable reference + RefMutToTemp(&'tcx Expr<'tcx>), + // Any other case + Expr(&'tcx Expr<'tcx>), +} + +impl<'tcx> ArgKind<'tcx> { + fn new(arg: &'tcx Expr<'tcx>) -> Self { + if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind { + if target.is_syntactic_place_expr() { + if arg.span.from_expansion() { + ArgKind::RefMutToPlaceAsMacro(arg) + } else { + ArgKind::RefMutToPlace(target) + } + } else { + ArgKind::RefMutToTemp(target) + } + } else { + ArgKind::Expr(arg) + } + } +} + +// Emits a note either on the temporary expression if it can be found in the same context as the +// base and returns `true`, or on the mutable reference to the temporary expression otherwise and +// returns `false`. +fn emit_note(diag: &mut Diag<'_, ()>, base: &Expr<'_>, expr: &Expr<'_>, expr_temp: &Expr<'_>) -> bool { + if base.span.eq_ctxt(expr.span) { + diag.span_note(expr_temp.span.source_callsite(), MSG_TEMPORARY); + true + } else { + diag.span_note(expr.span.source_callsite(), MSG_TEMPORARY_REFMUT); + false + } +} + +fn emit_lint_useless( + cx: &LateContext<'_>, + expr: &Expr<'_>, + left: &Expr<'_>, + right: &Expr<'_>, + left_temp: &Expr<'_>, + right_temp: &Expr<'_>, +) { + span_lint_and_then( + cx, + SWAP_WITH_TEMPORARY, + expr.span, + "swapping temporary values has no effect", + |diag| { + emit_note(diag, expr, left, left_temp); + emit_note(diag, expr, right, right_temp); + }, + ); +} + +fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>, reftemp: &Expr<'_>, temp: &Expr<'_>) { + span_lint_and_then( + cx, + SWAP_WITH_TEMPORARY, + expr.span, + "swapping with a temporary value is inefficient", + |diag| { + if !emit_note(diag, expr, reftemp, temp) { + return; + } + + // Make the suggestion only when the original `swap()` call is a statement + // or the last expression in a block. + if matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(..) | Node::Block(..)) { + let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let assign_target = match target { + ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => { + Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref() + }, + ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + ArgKind::RefMutToTemp(_) => unreachable!(), + }; + let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability); + diag.span_suggestion( + expr.span, + "use assignment instead", + format!("{assign_target} = {assign_source}"), + applicability, + ); + } + }, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index ca42a9ac04e0b..f920f306bc1ed 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,11 +1,10 @@ use super::utils::clone_or_copy_needed; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; @@ -45,30 +44,32 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { - span_lint_and_sugg( + span_lint( cx, UNNECESSARY_FILTER_MAP, expr.span, - format!("{name} is unnecessary"), - "try removing the filter_map", - String::new(), - Applicability::MaybeIncorrect, + String::from("this call to `.filter_map(..)` is unnecessary"), ); + return; + } + if name == "filter_map" { + "map(..)" + } else { + "map(..).next()" } - if name == "filter_map" { "map" } else { "map(..).next()" } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { match cx.typeck_results().expr_ty(body.value).kind() { ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => { - if name == "filter_map" { "filter" } else { "find" } + if name == "filter_map" { "filter(..)" } else { "find(..)" } }, _ => return, } } else { return; }; - span_lint_and_sugg( + span_lint( cx, if name == "filter_map" { UNNECESSARY_FILTER_MAP @@ -76,10 +77,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a UNNECESSARY_FIND_MAP }, expr.span, - format!("this `.{name}` can be written more simply"), - "try instead", - sugg.to_string(), - Applicability::MaybeIncorrect, + format!("this `.{name}(..)` can be written more simply using `.{sugg}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 9f4080100da20..71e606add526e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -23,56 +23,61 @@ pub(super) fn check<'tcx>( let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); - if is_option || is_result || is_bool { - if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind { - let body = cx.tcx.hir_body(body); - let body_expr = &body.value; + if (is_option || is_result || is_bool) + && let hir::ExprKind::Closure(&hir::Closure { + body, + fn_decl, + kind: hir::ClosureKind::Closure, + .. + }) = arg.kind + { + let body = cx.tcx.hir_body(body); + let body_expr = &body.value; - if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { - return false; - } + if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { + return false; + } - if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { - let msg = if is_option { - "unnecessary closure used to substitute value for `Option::None`" - } else if is_result { - "unnecessary closure used to substitute value for `Result::Err`" - } else { - "unnecessary closure used with `bool::then`" - }; - let applicability = if body - .params - .iter() - // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable - } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect - }; + if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else if is_result { + "unnecessary closure used to substitute value for `Result::Err`" + } else { + "unnecessary closure used with `bool::then`" + }; + let applicability = if body + .params + .iter() + // bindings are checked to be unused above + .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + && matches!( + fn_decl.output, + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) + ) { + Applicability::MachineApplicable + } else { + // replacing the lambda may break type inference + Applicability::MaybeIncorrect + }; - // This is a duplicate of what's happening in clippy_lints::methods::method_call, - // which isn't ideal, We want to get the method call span, - // but prefer to avoid changing the signature of the function itself. - if let hir::ExprKind::MethodCall(.., span) = expr.kind { - span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { - diag.span_suggestion_verbose( - span, - format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), - applicability, - ); - }); - return true; - } + // This is a duplicate of what's happening in clippy_lints::methods::method_call, + // which isn't ideal, We want to get the method call span, + // but prefer to avoid changing the signature of the function itself. + if let hir::ExprKind::MethodCall(.., span) = expr.kind { + span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { + diag.span_suggestion_verbose( + span, + format!("use `{simplify_using}` instead"), + format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + applicability, + ); + }); + return true; } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index d7bd522ddab94..b90748dd1585f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -76,7 +76,7 @@ pub(super) fn check<'a>( && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then thats a strange edge case and + // xor, because if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) && !is_local_used(cx, non_binding_location, hir_id) @@ -92,7 +92,7 @@ pub(super) fn check<'a>( // we may need to add parens around the suggestion // in case the parent expression has additional method calls, // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)` - // being converted to `Some(5) == Some(5).then(|| 1)` isnt + // being converted to `Some(5) == Some(5).then(|| 1)` isn't // the same thing let inner_non_binding = Sugg::NonParen(Cow::Owned(format!( @@ -109,8 +109,8 @@ pub(super) fn check<'a>( let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { match parent_expr.kind { - ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), - ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), _ => binop, } } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 3611b341897a6..b0cc7a785bc31 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -8,6 +8,9 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_span::symbol::sym; +/// Checks if `expr`, of type `ty`, corresponds to a slice or can be dereferenced to a slice, or if +/// `expr` is a method call to `.iter()` on such a type. In these cases, return the slice-like +/// expression. pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs index 662f7cd8500cf..9ee1e2f3fd17a 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -6,14 +6,14 @@ use rustc_lint::EarlyContext; use super::BUILTIN_TYPE_SHADOW; pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } + if let GenericParamKind::Type { .. } = param.kind + && let Some(prim_ty) = PrimTy::from_name(param.ident.name) + { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs index d5b5b2bf2dd1b..3cb51671aaf18 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs @@ -6,20 +6,20 @@ use rustc_lint::EarlyContext; use super::REDUNDANT_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind { - if let PatKind::Wild = right.kind { - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN, - pat.span, - format!( - "the `{} @ _` pattern can be written as just `{}`", - ident.name, ident.name, - ), - "try", - format!("{}{}", ann.prefix_str(), ident.name), - Applicability::MachineApplicable, - ); - } + if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind + && let PatKind::Wild = right.kind + { + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN, + pat.span, + format!( + "the `{} @ _` pattern can be written as just `{}`", + ident.name, ident.name, + ), + "try", + format!("{}{}", ann.prefix_str(), ident.name), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs index 00f46629f102c..fffaf40c9d141 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -7,30 +7,30 @@ use rustc_span::Span; use super::UNNEEDED_WILDCARD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { - if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { - if let Some((left_index, left_pat)) = patterns[..rest_index] - .iter() - .rev() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); - } + if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind + && let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) + { + if let Some((left_index, left_pat)) = patterns[..rest_index] + .iter() + .rev() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); + } - if let Some((right_index, right_pat)) = patterns[rest_index + 1..] - .iter() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint( - cx, - patterns[rest_index].span.shrink_to_hi().to(right_pat.span), - right_index == 0, - ); - } + if let Some((right_index, right_pat)) = patterns[rest_index + 1..] + .iter() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint( + cx, + patterns[rest_index].span.shrink_to_hi().to(right_pat.span), + right_index == 0, + ); } } } 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 d52fe7e7d5b9c..394bc4aef1cc7 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 @@ -111,10 +111,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { // Checks if impl_param_name is the same as one of type_param_names, // and is in a different position fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { - if let Some(j) = type_param_names.get(impl_param_name) { - if i != *j { - return true; - } + if let Some(j) = type_param_names.get(impl_param_name) + && i != *j + { + return true; } false } 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 cdd6f4e5b033a..c8e3462b24ef4 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 @@ -3,14 +3,15 @@ use std::ops::ControlFlow; use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr, higher}; -use rustc_ast::{LitKind, RangeLimits}; +use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{BinOpKind, Block, Body, Expr, ExprKind, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -134,18 +135,30 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind && let ExprKind::Binary(bin_op, left, right) = &condition.kind - - && let Some((cmp, asserted_len, slice_len)) = len_comparison(bin_op.node, left, right) - && let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind - && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() - && method.ident.name == sym::len - // check if `then` block has a never type expression && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind && cx.typeck_results().expr_ty(then_expr).is_never() + { + len_comparison(bin_op.node, left, right)? + } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro) => Some((macro_call, BinOpKind::Eq)), + Some(sym::assert_ne_macro) => Some((macro_call, BinOpKind::Ne)), + _ => None, + } + }) && let Some((left, right, _)) = find_assert_eq_args(cx, expr, macro_call.expn) + { + len_comparison(bin_op, left, right)? + } else { + return None; + }; + + if let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind + && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && method.ident.name == sym::len { Some((cmp, asserted_len, recv)) } else { @@ -168,6 +181,7 @@ enum IndexEntry<'hir> { /// if the `assert!` asserts the right length. AssertWithIndex { highest_index: usize, + is_first_highest: bool, asserted_len: usize, assert_span: Span, slice: &'hir Expr<'hir>, @@ -177,6 +191,7 @@ enum IndexEntry<'hir> { /// Indexing without an `assert!` IndexWithoutAssert { highest_index: usize, + is_first_highest: bool, indexes: Vec, slice: &'hir Expr<'hir>, }, @@ -244,28 +259,41 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni assert_span, slice, } => { - *entry = IndexEntry::AssertWithIndex { - highest_index: index, - asserted_len: *asserted_len, - assert_span: *assert_span, - slice, - indexes: vec![expr.span], - comparison: *comparison, - }; + if slice.span.lo() > assert_span.lo() { + *entry = IndexEntry::AssertWithIndex { + highest_index: index, + is_first_highest: true, + asserted_len: *asserted_len, + assert_span: *assert_span, + slice, + indexes: vec![expr.span], + comparison: *comparison, + }; + } }, IndexEntry::IndexWithoutAssert { - highest_index, indexes, .. + highest_index, + indexes, + is_first_highest, + .. } | IndexEntry::AssertWithIndex { - highest_index, indexes, .. + highest_index, + indexes, + is_first_highest, + .. } => { indexes.push(expr.span); + if *is_first_highest { + (*is_first_highest) = *highest_index >= index; + } *highest_index = (*highest_index).max(index); }, } } else { indexes.push(IndexEntry::IndexWithoutAssert { highest_index: index, + is_first_highest: true, indexes: vec![expr.span], slice, }); @@ -284,15 +312,18 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un if let Some(entry) = entry { if let IndexEntry::IndexWithoutAssert { highest_index, + is_first_highest, indexes, slice, } = entry + && expr.span.lo() <= slice.span.lo() { *entry = IndexEntry::AssertWithIndex { highest_index: *highest_index, indexes: mem::take(indexes), + is_first_highest: *is_first_highest, slice, - assert_span: expr.span, + assert_span: expr.span.source_callsite(), comparison, asserted_len, }; @@ -301,7 +332,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un indexes.push(IndexEntry::StrayAssert { asserted_len, comparison, - assert_span: expr.span, + assert_span: expr.span.source_callsite(), slice, }); } @@ -325,12 +356,13 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap match *entry { IndexEntry::AssertWithIndex { highest_index, + is_first_highest, asserted_len, ref indexes, comparison, assert_span, slice, - } if indexes.len() > 1 => { + } if indexes.len() > 1 && !is_first_highest => { // if we have found an `assert!`, let's also check that it's actually right // and if it covers the highest index and if not, suggest the correct length let sugg = match comparison { @@ -378,8 +410,9 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap IndexEntry::IndexWithoutAssert { ref indexes, highest_index, + is_first_highest, slice, - } if indexes.len() > 1 => { + } if indexes.len() > 1 && !is_first_highest => { // if there was no `assert!` but more than one index, suggest // adding an `assert!` that covers the highest index report_lint( diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 38a19dd2999bb..67537a251da7f 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -139,12 +139,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir_get_parent_item(hir_id).def_id; - if parent != CRATE_DEF_ID { - if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) { - if let hir::ItemKind::Trait(..) = &item.kind { - return; - } - } + if parent != CRATE_DEF_ID + && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) + && let hir::ItemKind::Trait(..) = &item.kind + { + return; } } @@ -156,9 +155,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let mir = cx.tcx.optimized_mir(def_id); + let mir = cx.tcx.mir_drops_elaborated_and_const_checked(def_id); - if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) + if let Ok(()) = is_min_const_fn(cx, &mir.borrow(), self.msrv) && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = cx.tcx.hir_node_by_def_id(def_id) { diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 28dc242742842..1932d2d5f9785 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -209,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id) // don't trigger if this impl was derived - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() // find `Debug::fmt` function && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt) @@ -224,11 +224,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { // NB: can't call cx.typeck_results() as we are not in a body && let typeck_results = cx.tcx.typeck_body(*body_id) && should_lint(cx, typeck_results, block) - { // we intentionally only lint structs, see lint description - if let ItemKind::Struct(_, data, _) = &self_item.kind { - check_struct(cx, typeck_results, block, self_ty, item, data); - } + && let ItemKind::Struct(_, data, _) = &self_item.kind + { + check_struct(cx, typeck_results, block, self_ty, item, data); } } } diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index f49e03ea76528..1f613171b46e8 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -160,12 +160,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; - if let Some(trait_def_id) = trait_def_id { - if trait_def_id.is_local() && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; - } + if let Some(trait_def_id) = trait_def_id + && trait_def_id.is_local() + && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + { + // If a trait is being implemented for an item, and the + // trait is not exported, we don't need #[inline] + return; } let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); 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 fbd287f528544..0e08558596283 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 @@ -135,10 +135,10 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> { } fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(self.cx, e) { - if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - return; - } + if let Some(macro_call) = root_macro_call_first_node(self.cx, e) + && self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" + { + return; } span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges"); } @@ -328,22 +328,22 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) { + if path_to_local_id(expr, self.var) // Check that this is a read, not a write. - if !is_in_assignment_position(self.cx, expr) { - span_lint_and_then( - self.cx, - MIXED_READ_WRITE_IN_EXPRESSION, - expr.span, - format!("unsequenced read of `{}`", self.cx.tcx.hir_name(self.var)), - |diag| { - diag.span_note( - self.write_expr.span, - "whether read occurs before this write depends on evaluation order", - ); - }, - ); - } + && !is_in_assignment_position(self.cx, expr) + { + span_lint_and_then( + self.cx, + MIXED_READ_WRITE_IN_EXPRESSION, + expr.span, + format!("unsequenced read of `{}`", self.cx.tcx.hir_name(self.var)), + |diag| { + diag.span_note( + self.write_expr.span, + "whether read occurs before this write depends on evaluation order", + ); + }, + ); } match expr.kind { // We're about to descend a closure. Since we don't know when (or @@ -373,10 +373,10 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { /// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`. fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::Assign(lhs, ..) = parent.kind { - return lhs.hir_id == expr.hir_id; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Assign(lhs, ..) = parent.kind + { + return lhs.hir_id == expr.hir_id; } false } diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 7287193326f7a..98614baffcea6 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -119,22 +119,22 @@ impl EarlyLintPass for ModStyle { } for folder in &folder_segments { - if !mod_folders.contains(folder) { - if let Some((file, path)) = file_map.get(folder) { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); - } + if !mod_folders.contains(folder) + && let Some((file, path)) = file_map.get(folder) + { + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + let mut correct = path.to_path_buf(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2adc27c0b709a..c6c27e22b90e5 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -142,10 +142,9 @@ fn collect_unsafe_exprs<'tcx>( .typeck_results() .type_dependent_def_id(expr.hir_id) .map(|def_id| cx.tcx.fn_sig(def_id)) + && sig.skip_binder().safety().is_unsafe() { - if sig.skip_binder().safety().is_unsafe() { - unsafe_ops.push(("unsafe method call occurs here", expr.span)); - } + unsafe_ops.push(("unsafe method call occurs here", expr.span)); } }, diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 7abc5870d00e0..a45031ce22b91 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -82,10 +82,10 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { - if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { - if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { - self.check_sig(cx, item.owner_id.def_id, sig.decl); - } + if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + { + self.check_sig(cx, item.owner_id.def_id, sig.decl); } } diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index 3c4ba5141dd9b..d98c70e7f5a85 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -77,16 +77,16 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - if ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( - self.cx, - MUT_MUT, - expr.hir_id, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); - } + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) + { + span_lint_hir( + self.cx, + MUT_MUT, + expr.hir_id, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs index 13a23a13b9c24..270eebe075804 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -101,14 +101,13 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> { return; }, ExprKind::Path(_) => { - if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { - if adj + if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) + && adj .iter() .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut))) - { - self.found = true; - return; - } + { + self.found = true; + return; } }, // Don't check await desugars diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 49fd29d1dd6dc..fe2157ca533a6 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -91,19 +91,19 @@ declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); impl<'tcx> LateLintPass<'tcx> for Mutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind() { - if is_type_diagnostic_item(cx, ty, sym::Mutex) { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + if let ty::Adt(_, subst) = ty.kind() + && is_type_diagnostic_item(cx, ty, sym::Mutex) + { + let mutex_param = subst.type_at(0); + if let Some(atomic_name) = get_atomic_name(mutex_param) { + let msg = format!( + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - } + ); + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 2eacd6875d6b4..f768e11a4a2bb 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { || is_receiver_of_method_call(cx, e) || is_as_argument(cx, e) { - snip = snip.maybe_par(); + snip = snip.maybe_paren(); } span_lint_and_sugg( @@ -426,10 +426,10 @@ fn fetch_bool_block(expr: &Expr<'_>) -> Option { } fn fetch_bool_expr(expr: &Expr<'_>) -> Option { - if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind { - if let LitKind::Bool(value) = lit_ptr.node { - return Some(value); - } + if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind + && let LitKind::Bool(value) = lit_ptr.node + { + return Some(value); } None } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index b61061d076b74..7052e1d0fbe5d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -86,11 +86,11 @@ fn should_skip<'tcx>( return false; } - if let PatKind::Binding(.., name, _) = arg.pat.kind { + if let PatKind::Binding(.., name, _) = arg.pat.kind // If it's a potentially unused variable, we don't check it. - if name.name == kw::Underscore || name.as_str().starts_with('_') { - return true; - } + && (name.name == kw::Underscore || name.as_str().starts_with('_')) + { + return true; } // All spans generated from a proc-macro invocation are the same... @@ -164,13 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { }; // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); @@ -353,10 +353,10 @@ impl MutablyUsedVariablesCtxt<'_> { for (parent, node) in self.tcx.hir_parent_iter(item) { if let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(parent) { return fn_sig.header.is_unsafe(); - } else if let Node::Block(block) = node { - if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { - return true; - } + } else if let Node::Block(block) = node + && matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) + { + return true; } } false @@ -426,10 +426,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { // upon! self.add_mutably_used_var(*vid); } - } else if borrow == ty::BorrowKind::Immutable { + } else if borrow == ty::BorrowKind::Immutable // If there is an `async block`, it'll contain a call to a closure which we need to // go into to ensure all "mutate" checks are found. - if let Node::Expr(Expr { + && let Node::Expr(Expr { kind: ExprKind::Call( _, @@ -442,9 +442,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { ), .. }) = self.tcx.hir_node(cmt.hir_id) - { - self.async_closures.insert(*def_id); - } + { + self.async_closures.insert(*def_id); } } @@ -460,10 +459,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && !projections.is_empty() { - if !projections.is_empty() { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } } @@ -477,10 +475,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && self.is_in_unsafe_block(id) { - if self.is_in_unsafe_block(id) { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } self.prev_bind = None; } @@ -499,15 +496,14 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && let FakeReadCause::ForLet(Some(inner)) = cause { - if let FakeReadCause::ForLet(Some(inner)) = cause { - // Seems like we are inside an async function. We need to store the closure `DefId` - // to go through it afterwards. - self.async_closures.insert(inner); - self.add_alias(cmt.hir_id, *vid); - self.prev_move_to_closure.insert(*vid); - self.prev_bind = None; - } + // Seems like we are inside an async function. We need to store the closure `DefId` + // to go through it afterwards. + self.async_closures.insert(inner); + self.add_alias(cmt.hir_id, *vid); + self.prev_move_to_closure.insert(*vid); + self.prev_bind = None; } } @@ -522,10 +518,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && self.is_in_unsafe_block(id) { - if self.is_in_unsafe_block(id) { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } } } 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 978fde33d5170..275d710c76a9b 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 @@ -98,13 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } // Allow `Borrow` or functions to be taken by value @@ -197,20 +197,18 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { { // Dereference suggestion let sugg = |diag: &mut Diag<'_, ()>| { - 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), - rustc_hir::Safety::Safe, - ) - .is_ok() - { - diag.span_help(span, "or consider marking this type as `Copy`"); - } - } + if let ty::Adt(def, ..) = ty.kind() + && let Some(span) = cx.tcx.hir_span_if_local(def.did()) + && type_allowed_to_implement_copy( + cx.tcx, + cx.param_env, + ty, + traits::ObligationCause::dummy_with_span(span), + rustc_hir::Safety::Safe, + ) + .is_ok() + { + diag.span_help(span, "or consider marking this type as `Copy`"); } if is_type_diagnostic_item(cx, ty, sym::Vec) @@ -254,29 +252,28 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) { - if let Some(clone_spans) = + if is_type_lang_item(cx, ty, LangItem::String) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) - { + { + diag.span_suggestion( + input.span, + "consider changing the type to", + "&str", + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { diag.span_suggestion( - input.span, - "consider changing the type to", - "&str", + span, + span.get_source_text(cx) + .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), + suggestion, Applicability::Unspecified, ); - - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - span.get_source_text(cx) - .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), - suggestion, - Applicability::Unspecified, - ); - } - - return; } + + return; } diag.span_suggestion_verbose( diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs index cce0617ba3925..4a86c3720ca24 100644 --- a/src/tools/clippy/clippy_lints/src/needless_update.rs +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -53,18 +53,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, _) = ty.kind() { - let variant = def.non_enum_variant(); - if fields.len() == variant.fields.len() - && !variant.is_field_list_non_exhaustive() - { - span_lint( - cx, - NEEDLESS_UPDATE, - base.span, - "struct update has no effect, all the fields in the struct have already been specified", - ); - } + if let ty::Adt(def, _) = ty.kind() + && fields.len() == def.non_enum_variant().fields.len() + && !def.variant(0_usize.into()).is_field_list_non_exhaustive() + { + span_lint( + cx, + NEEDLESS_UPDATE, + base.span, + "struct update has no effect, all the fields in the struct have already been specified", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 429afff9b6642..74c8142787ebc 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -16,9 +16,6 @@ declare_clippy_lint! { /// ### Why is this bad? /// It's more readable to just negate. /// - /// ### Known problems - /// This only catches integers (for now). - /// /// ### Example /// ```rust,ignore /// let a = x * -1; @@ -38,23 +35,32 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, left, right) = e.kind { - if BinOpKind::Mul == op.node { - match (&left.kind, &right.kind) { - (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), - _ => {}, - } + if let ExprKind::Binary(ref op, left, right) = e.kind + && BinOpKind::Mul == op.node + { + match (&left.kind, &right.kind) { + (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + _ => {}, } } } } fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { + const F16_ONE: u16 = 1.0_f16.to_bits(); + const F128_ONE: u128 = 1.0_f128.to_bits(); 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() + && matches!( + consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)), + Constant::Int(1) + | Constant::F16(F16_ONE) + | Constant::F32(1.0) + | Constant::F64(1.0) + | Constant::F128(F128_ONE) + ) + && cx.typeck_results().expr_ty(exp).is_numeric() { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut 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 1e469c3b63a43..4b73a4455f55b 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -99,10 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { let mut impls = HirIdSet::default(); for &d in cx.tcx.local_trait_impls(default_trait_id) { 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.local_def_id_to_hir_id(local_def_id)); - } + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() + { + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } } self.impling_types = Some(impls); diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 7187a8f2c11a1..7ab7976d5697a 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -182,23 +182,22 @@ impl NoEffect { ); return true; } - } else if let StmtKind::Let(local) = stmt.kind { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) - && !matches!(local.source, LocalSource::AsyncFn) - && let Some(init) = local.init - && local.els.is_none() - && !local.pat.span.from_expansion() - && has_no_effect(cx, init) - && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind - && ident.name.to_ident_string().starts_with('_') - && !in_automatically_derived(cx.tcx, local.hir_id) - { - if let Some(l) = self.local_bindings.last_mut() { - l.push(hir_id); - self.underscore_bindings.insert(hir_id, ident.span); - } - return true; + } else if let StmtKind::Let(local) = stmt.kind + && !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && !matches!(local.source, LocalSource::AsyncFn) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + && !in_automatically_derived(cx.tcx, local.hir_id) + { + if let Some(l) = self.local_bindings.last_mut() { + l.push(hir_id); + self.underscore_bindings.insert(hir_id, ident.span); } + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 448bb603cf2c9..93865197ec965 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core}; +use clippy_utils::{ + is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, +}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; @@ -98,7 +100,7 @@ declare_clippy_lint! { /// /// impl PartialOrd for A { /// fn partial_cmp(&self, other: &Self) -> Option { - /// Some(self.cmp(other)) + /// Some(self.cmp(other)) // or self.cmp(other).into() /// } /// } /// ``` @@ -185,65 +187,66 @@ impl LateLintPass<'_> for NonCanonicalImpls { if block.stmts.is_empty() && let Some(expr) = block.expr - && expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified) + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) { + return; } // Fix #12683, allow [`needless_return`] here else if block.expr.is_none() && let Some(stmt) = block.stmts.first() && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })), + kind: ExprKind::Ret(Some(ret)), .. }) = stmt.kind - && expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified) + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) { - } else { - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { + return; + } - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; + span_lint_and_then( + cx, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); - }, - ); - } + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); } } } @@ -251,10 +254,11 @@ impl LateLintPass<'_> for NonCanonicalImpls { /// Return true if `expr_kind` is a `cmp` call. fn expr_is_cmp<'tcx>( cx: &LateContext<'tcx>, - expr_kind: &'tcx ExprKind<'tcx>, + expr: &'tcx Expr<'tcx>, impl_item: &ImplItem<'_>, needs_fully_qualified: &mut bool, ) -> bool { + let impl_item_did = impl_item.owner_id.def_id; if let ExprKind::Call( Expr { kind: ExprKind::Path(some_path), @@ -262,11 +266,17 @@ fn expr_is_cmp<'tcx>( .. }, [cmp_expr], - ) = expr_kind + ) = expr.kind { is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, needs_fully_qualified) + && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) + } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { + cx.tcx + .typeck(impl_item_did) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) } else { false } 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 9b53608ae7f3c..63859c0396e48 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -449,7 +449,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { dereferenced_expr = parent_expr; }, - ExprKind::Index(e, _, _) if ptr::eq(&**e, cur_expr) => { + ExprKind::Index(e, _, _) if ptr::eq(&raw const **e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, // meaning `e` must be referenced. // no need to go further up since a method call is involved now. diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index 8305bf345ef19..f6bc9428d65f2 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// static FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "FOO".to_lowercase()); /// static BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "BAR".to_lowercase()); /// ``` - #[clippy::version = "1.81.0"] + #[clippy::version = "1.86.0"] pub NON_STD_LAZY_STATICS, pedantic, "lazy static that could be replaced by `std::sync::LazyLock`" @@ -121,7 +121,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { cx, NON_STD_LAZY_STATICS, macro_call.span, - "this macro has been superceded by `std::sync::LazyLock`", + "this macro has been superseded by `std::sync::LazyLock`", ); return; } @@ -240,7 +240,7 @@ impl LazyInfo { cx, NON_STD_LAZY_STATICS, self.ty_span_no_args, - "this type has been superceded by `LazyLock` in the standard library", + "this type has been superseded by `LazyLock` in the standard library", |diag| { diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); }, diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs index 16c4391c0fbea..635f5678e2a65 100644 --- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs +++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs @@ -82,11 +82,10 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit if let ty::Adt(adt_def, _) = receiver_ty.kind() && adt_def.is_struct() && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + && let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - let arg_snippet = get_arg_snippet(cx, arg, rcv_path); - suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); - } + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index cf6b8992973a7..9b2cfd91b8535 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -98,7 +98,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let arg_snip = snippet(cx, arg_span, ".."); let expr_snip; let eq_impl; - if with_deref.is_implemented() { + if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() { expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { 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 74e0a6333db0f..047a5a0159cb0 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 @@ -50,7 +50,7 @@ pub(crate) fn check<'tcx>( // format the suggestion let suggestion = format!( "{}.abs()", - sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_par() + sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_paren() ); // spans the lint span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs index 0358232282786..e1fd09549a4b8 100644 --- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs @@ -103,7 +103,7 @@ enum Parens { /// /// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently. /// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since -/// the the cast expression will not apply to the same expression. +/// the cast expression will not apply to the same expression. /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced /// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be /// interpreted as a statement. The same behavior happens for `match`, `loop`, diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs index fc5565e821edd..2e6a071eb1848 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs @@ -12,15 +12,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); } - if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { - if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint( - cx, - MODULO_ONE, - expr.span, - "any number modulo -1 will panic/overflow or result in 0", - ); - } + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() + && is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) + { + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs index 96c9775e292e6..e6be536ca0f4e 100644 --- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs @@ -75,10 +75,10 @@ impl Context { hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { let body_span = cx.tcx.hir_span_with_body(body_owner); - if let Some(span) = self.const_span { - if span.contains(body_span) { - return; - } + if let Some(span) = self.const_span + && span.contains(body_span) + { + return; } self.const_span = Some(body_span); }, @@ -90,10 +90,10 @@ impl Context { let body_owner = cx.tcx.hir_body_owner(body.id()); let body_span = cx.tcx.hir_span_with_body(body_owner); - if let Some(span) = self.const_span { - if span.contains(body_span) { - return; - } + if let Some(span) = self.const_span + && span.contains(body_span) + { + return; } self.const_span = None; } 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 378fed481f4fe..0faa7b9e64665 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -47,12 +47,11 @@ pub(crate) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); let rcpy = is_copy(cx, rty); - if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { - if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) - || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) - { - return; // Don't lint - } + if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) + && ((are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) + || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))) + { + return; // Don't lint } // either operator autorefs or both args are copyable if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { @@ -86,7 +85,7 @@ pub(crate) fn check<'tcx>( left.span, "use the left value directly", lsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -105,7 +104,7 @@ pub(crate) fn check<'tcx>( right.span, "use the right value directly", rsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -137,7 +136,7 @@ pub(crate) fn check<'tcx>( left.span, "use the left value directly", lsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -164,7 +163,7 @@ pub(crate) fn check<'tcx>( right.span, "use the right value directly", rsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }); } diff --git a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs index a6aba33e431a4..1477378914120 100644 --- a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( e.span, "bit mask could be simplified with a call to `trailing_zeros`", |diag| { - let sugg = Sugg::hir(cx, left1, "...").maybe_par(); + let sugg = Sugg::hir(cx, left1, "...").maybe_paren(); diag.span_suggestion( e.span, "try", 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 6f302ea196217..9487cec87efb8 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,8 +4,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, - is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, + is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -106,7 +106,7 @@ struct OptionOccurrence { fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", - cond_sugg.maybe_par(), + cond_sugg.maybe_paren(), if as_mut { ".as_mut()" } else if as_ref { @@ -212,6 +212,15 @@ fn try_get_option_occurrence<'tcx>( } } + let some_body_ty = cx.typeck_results().expr_ty(some_body); + let none_body_ty = cx.typeck_results().expr_ty(none_body); + // Check if coercion is needed for the `None` arm. If so, we cannot suggest because it will + // introduce a type mismatch. A special case is when both arms have the same type, then + // coercion is fine. + if some_body_ty != none_body_ty && expr_requires_coercion(cx, none_body) { + return None; + } + let mut app = Applicability::Unspecified; let (none_body, is_argless_call) = match none_body.kind { 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 65671b478ba74..8eaf65e63065e 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { items: impl_items, .. }) = item.kind - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && let Some(eq_trait) = cx.tcx.lang_items().eq_trait() && trait_ref.path.res.def_id() == eq_trait { diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs index 6d4216970cc4d..9b9024c810575 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { let sugg = format!( "{}.{}", sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability) - .maybe_par(), + .maybe_paren(), if is_eq { "is_none()" } else { "is_some()" } ); 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 0a8e288564875..5d30b66def2c8 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 @@ -178,19 +178,18 @@ impl PassByRefOrValue { && size <= self.ref_min_size && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind { - if let Some(typeck) = cx.maybe_typeck_results() { + if let Some(typeck) = cx.maybe_typeck_results() // Don't lint if a raw pointer is created. // TODO: Limit the check only to raw pointers to the argument (or part of the argument) // which escape the current function. - if typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr()) + && (typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr()) || typeck .adjustments() .items() .flat_map(|(_, a)| a) - .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer))) - { - continue; - } + .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer)))) + { + continue; } let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) { "self".into() @@ -282,12 +281,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } let attrs = cx.tcx.hir_attrs(hir_id); for a in attrs { - if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym::proc_macro_derive) - || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always)) - { - return; - } + if let Some(meta_items) = a.meta_item_list() + && (a.has_name(sym::proc_macro_derive) + || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))) + { + return; } } }, @@ -296,13 +294,13 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } self.check_poly_fn(cx, def_id, decl, Some(span)); diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index b653b459b04c8..35caac855cf61 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -173,16 +173,15 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let Some(mut searcher) = self.searcher.take() { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind - && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) - && name.ident.as_str() == "push" - { - searcher.err_span = searcher.err_span.to(stmt.span); - searcher.arg = Some(*arg_expr); - searcher.display_err(cx); - } + if let Some(mut searcher) = self.searcher.take() + && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + searcher.err_span = searcher.err_span.to(stmt.span); + searcher.arg = Some(*arg_expr); + searcher.display_err(cx); } } diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index 96d3f7196c0cb..19d9acfc9305a 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -177,17 +177,16 @@ fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mut PatKind::Or([p, ..]) => p, _ => p, }; - if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) { - if let [first, ..] = **adjustments { - if let ty::Ref(.., mutability) = *first.source.kind() { - let level = if p.hir_id == pat.hir_id { - Level::Top - } else { - Level::Lower - }; - result = Some((p.span, mutability, level)); - } - } + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && let [first, ..] = **adjustments + && let ty::Ref(.., mutability) = *first.source.kind() + { + let level = if p.hir_id == pat.hir_id { + Level::Top + } else { + Level::Lower + }; + result = Some((p.span, mutability, level)); } result.is_none() }); diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 901a1634ddc88..491961408adb8 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -264,8 +264,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { is_null_path(cx, l), is_null_path(cx, r), ) { - (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), - (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), + (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(), + (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(), _ => return check_ptr_eq(cx, expr, op.node, l, r), }; @@ -498,29 +498,33 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) { - if let FnRetTy::Return(ty) = sig.decl.output - && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty) - { + let FnRetTy::Return(ty) = sig.decl.output else { return }; + for (out, mutability, out_span) in get_lifetimes(ty) { + if mutability != Some(Mutability::Mut) { + continue; + } let out_region = cx.tcx.named_bound_var(out.hir_id); - let args: Option> = sig + // `None` if one of the types contains `&'a mut T` or `T<'a>`. + // Else, contains all the locations of `&'a T` types. + let args_immut_refs: Option> = sig .decl .inputs .iter() - .filter_map(get_ref_lm) + .flat_map(get_lifetimes) .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region) - .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span)) + .map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span)) .collect(); - if let Some(args) = args - && !args.is_empty() + if let Some(args_immut_refs) = args_immut_refs + && !args_immut_refs.is_empty() && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) { span_lint_and_then( cx, MUT_FROM_REF, - ty.span, + out_span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(args); + let ms = MultiSpan::from_spans(args_immut_refs); diag.span_note(ms, "immutable borrow here"); }, ); @@ -686,12 +690,36 @@ fn matches_preds<'tcx>( }) } -fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Ref(lt, ref m) = ty.kind { - Some((lt, m.mutbl, ty.span)) - } else { - None +struct LifetimeVisitor<'tcx> { + result: Vec<(&'tcx Lifetime, Option, Span)>, +} + +impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { + if let TyKind::Ref(lt, ref m) = ty.kind { + self.result.push((lt, Some(m.mutbl), ty.span)); + } + hir::intravisit::walk_ty(self, ty); } + + fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { + if let GenericArg::Lifetime(lt) = generic_arg { + self.result.push((lt, None, generic_arg.span())); + } + hir::intravisit::walk_generic_arg(self, generic_arg); + } +} + +/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not. +/// +/// The second field of the vector's elements indicate if the lifetime is attached to a +/// shared reference, a mutable reference, or neither. +fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, Span)> { + use hir::intravisit::VisitorExt as _; + + let mut visitor = LifetimeVisitor { result: Vec::new() }; + visitor.visit_ty_unambig(ty); + visitor.result } fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { @@ -728,8 +756,9 @@ fn check_ptr_eq<'tcx>( let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); - if let Some(left_snip) = left_var.span.get_source_text(cx) - && let Some(right_snip) = right_var.span.get_source_text(cx) + let mut app = Applicability::MachineApplicable; + let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app); + let right_snip = Sugg::hir_with_context(cx, right_var, expr.span.ctxt(), "_", &mut app); { let Some(top_crate) = std_or_core(cx) else { return }; let invert = if op == BinOpKind::Eq { "" } else { "!" }; @@ -740,7 +769,7 @@ fn check_ptr_eq<'tcx>( format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), "try", format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"), - Applicability::MachineApplicable, + app, ); } } @@ -748,7 +777,8 @@ fn check_ptr_eq<'tcx>( // If the given expression is a cast to a usize, return the lhs of the cast // E.g., `foo as *const _ as usize` returns `foo as *const _`. fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize + if !cast_expr.span.from_expansion() + && cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize && let ExprKind::Cast(expr, _) = cast_expr.kind { Some(expr) @@ -759,7 +789,8 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> // Peel raw casts if the remaining expression can be coerced to it fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> { - if let ExprKind::Cast(inner, _) = expr.kind + if !expr.span.from_expansion() + && let ExprKind::Cast(inner, _) = expr.kind && let ty::RawPtr(target_ty, _) = expr_ty.kind() && let inner_ty = cx.typeck_results().expr_ty(inner) && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index 68ae575c9063f..7f74a2fff9f20 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -77,10 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { // If the given expression is a cast from a usize, return the lhs of the cast fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind { - if is_expr_ty_usize(cx, cast_lhs_expr) { - return Some(cast_lhs_expr); - } + if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind + && is_expr_ty_usize(cx, cast_lhs_expr) + { + return Some(cast_lhs_expr); } None } @@ -91,14 +91,14 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind { - if is_expr_ty_raw_ptr(cx, arg_0) { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name.as_str() == "wrapping_offset" { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } + if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind + && is_expr_ty_raw_ptr(cx, arg_0) + { + if path_segment.ident.name == sym::offset { + return Some((arg_0, arg_1, Method::Offset)); + } + if path_segment.ident.name.as_str() == "wrapping_offset" { + return Some((arg_0, arg_1, Method::WrappingOffset)); } } None diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index a80e1f79bbc77..d318897443da5 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -5,6 +5,7 @@ use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, @@ -144,7 +145,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { && !span_contains_comment(cx.tcx.sess.source_map(), els.span) { let mut applicability = Applicability::MaybeIncorrect; - let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability); + let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); // Take care when binding is `ref` let sugg = if let PatKind::Binding( BindingMode(ByRef::Yes(ref_mutability), binding_mutability), diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index cc423eca74fbe..d292ed86ea4c6 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -179,10 +179,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, l, r) = expr.kind { - if self.msrv.meets(cx, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr, expr.span); - } + if let ExprKind::Binary(ref op, l, r) = expr.kind + && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + { + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } check_exclusive_range_plus_one(cx, expr); @@ -327,18 +327,18 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> inc: inclusive, }); } - } else if let Some(id) = path_to_local(r) { - if let Some(c) = ConstEvalCtxt::new(cx).eval(l) { - return Some(RangeBounds { - val: c, - expr: l, - id, - name_span: r.span, - val_span: l.span, - ord: ordering.reverse(), - inc: inclusive, - }); - } + } else if let Some(id) = path_to_local(r) + && let Some(c) = ConstEvalCtxt::new(cx).eval(l) + { + return Some(RangeBounds { + val: c, + expr: l, + id, + name_span: r.span, + val_span: l.span, + ord: ordering.reverse(), + inc: inclusive, + }); } } None @@ -361,8 +361,8 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { 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(); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_paren(); match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) { Some(true) => { diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect); @@ -398,8 +398,8 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { 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(); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_paren(); diag.span_suggestion( expr.span, "use", 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 6bb7650a7e1cf..689a2ac4c6aeb 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 @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -135,7 +135,7 @@ fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, 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)); + return Some((sym::Weak, func.span)); } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs index 8289ec47bc7e1..d2442ad0f373a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -1,14 +1,9 @@ -use std::ops::ControlFlow; - use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::for_each_expr_without_closures; +use clippy_utils::{desugar_await, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{ - Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource, -}; +use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::UpvarCapture; use rustc_session::declare_lint_pass; @@ -99,20 +94,3 @@ fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op None } } - -/// If `expr` is a desugared `.await`, return the original expression if it does not come from a -/// macro expansion. -fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind - && let ExprKind::Call(_, [into_future_arg]) = match_value.kind - && let ctxt = expr.span.ctxt() - && for_each_expr_without_closures(into_future_arg, |e| { - walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(())) - }) - .is_none() - { - Some(into_future_arg) - } else { - None - } -} diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index cfa622aea582f..e57b8cc2d84e3 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -109,10 +109,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(def, _) = arg_ty.kind() { - if def.is_manually_drop() { - continue; - } + if let ty::Adt(def, _) = arg_ty.kind() + && def.is_manually_drop() + { + continue; } // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }` @@ -182,20 +182,25 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let clone_usage = if local == ret_local { CloneUsage { - cloned_used: false, + cloned_use_loc: None.into(), cloned_consume_or_mutate_loc: None, clone_consumed_or_mutated: true, } } else { let clone_usage = visit_clone_usage(local, ret_local, mir, bb); - if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { + if clone_usage.cloned_use_loc.maybe_used() && clone_usage.clone_consumed_or_mutated { // cloned value is used, and the clone is modified or moved continue; - } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { + } else if let MirLocalUsage::Used(loc) = clone_usage.cloned_use_loc + && possible_borrower.local_is_alive_at(ret_local, loc) + { + // cloned value is used, and the clone is alive. + continue; + } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc // cloned value is mutated, and the clone is alive. - if possible_borrower.local_is_alive_at(ret_local, loc) { - continue; - } + && possible_borrower.local_is_alive_at(ret_local, loc) + { + continue; } clone_usage }; @@ -216,19 +221,18 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { 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 + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) + && call_snip .as_bytes() .iter() .all(|b| b.is_ascii_alphabetic() || *b == b'_') - { - app = Applicability::MachineApplicable; - } + { + 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 { + if clone_usage.cloned_use_loc.maybe_used() { diag.span_note(span, "cloned value is neither consumed nor mutated"); } else { diag.span_note( @@ -329,10 +333,33 @@ fn base_local_and_movability<'tcx>( (place.local, deref || field || slice) } -#[derive(Default)] +#[derive(Debug, Default)] +enum MirLocalUsage { + /// The local maybe used, but we are not sure how. + Unknown, + /// The local is not used. + #[default] + Unused, + /// The local is used at a specific location. + Used(mir::Location), +} + +impl MirLocalUsage { + fn maybe_used(&self) -> bool { + matches!(self, MirLocalUsage::Unknown | MirLocalUsage::Used(_)) + } +} + +impl From> for MirLocalUsage { + fn from(loc: Option) -> Self { + loc.map_or(MirLocalUsage::Unused, MirLocalUsage::Used) + } +} + +#[derive(Debug, Default)] struct CloneUsage { - /// Whether the cloned value is used after the clone. - cloned_used: bool, + /// The first location where the cloned value is used, if any. + cloned_use_loc: MirLocalUsage, /// The first location where the cloned value is consumed or mutated, if any. cloned_consume_or_mutate_loc: Option, /// Whether the clone value is mutated. @@ -360,7 +387,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, .map(|mut vec| (vec.remove(0), vec.remove(0))) { CloneUsage { - cloned_used: !cloned_use_locs.is_empty(), + cloned_use_loc: cloned_use_locs.first().copied().into(), cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), // Consider non-temporary clones consumed. // TODO: Actually check for mutation of non-temporaries. @@ -369,7 +396,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, } } else { CloneUsage { - cloned_used: true, + cloned_use_loc: MirLocalUsage::Unknown, cloned_consume_or_mutate_loc: None, clone_consumed_or_mutated: true, } 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 1498a49a7a4a9..84597269a58fa 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // avoid clippy::double_parens if !is_in_fn_call_arg { - hint = hint.maybe_par(); + hint = hint.maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); 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 f2fdac5a8afaf..7b381fac5f118 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -52,13 +52,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && is_not_macro_export(item) && !item.span.in_external_macro(cx.sess().source_map()) { - // FIXME: `DUMMY_SP` isn't right here, because it causes the - // resulting span to begin at the start of the file. - let span = item.span.with_hi( - item.kind - .ident() - .map_or(rustc_span::DUMMY_SP.hi(), |ident| ident.span.hi()), - ); + let span = item + .kind + .ident() + .map_or(item.span, |ident| item.span.with_hi(ident.span.hi())); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 7038b19d27596..1117dea703c2a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -135,25 +135,24 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { }; diag.span_suggestion(expr.span, help_msg, sugg, app); }); - } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { - if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( + } else if let Some(target_id) = cx.tcx.lang_items().deref_target() + && let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.typing_env(), Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), - ) { - if deref_ty == expr_ty { - let (lint, msg) = DEREF_BY_SLICING_LINT; - span_lint_and_then(cx, lint, expr.span, msg, |diag| { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - } else { - format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - }; - diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); - }); - } - } + ) + && deref_ty == expr_ty + { + let (lint, msg) = DEREF_BY_SLICING_LINT; + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if needs_parens_for_prefix { + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + } else { + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs new file mode 100644 index 0000000000000..84276e3216573 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs @@ -0,0 +1,161 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_test_function; +use clippy_utils::visitors::for_each_expr; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{self as hir, Body, ExprKind, FnDecl}; +use rustc_lexer::is_ident; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, Symbol, edition}; +use std::borrow::Cow; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for test functions (functions annotated with `#[test]`) that are prefixed + /// with `test_` which is redundant. + /// + /// ### Why is this bad? + /// This is redundant because test functions are already annotated with `#[test]`. + /// Moreover, it clutters the output of `cargo test` since test functions are expanded as + /// `module::tests::test_use_case` in the output. Without the redundant prefix, the output + /// becomes `module::tests::use_case`, which is more readable. + /// + /// ### Example + /// ```no_run + /// #[cfg(test)] + /// mod tests { + /// use super::*; + /// + /// #[test] + /// fn test_use_case() { + /// // test code + /// } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[cfg(test)] + /// mod tests { + /// use super::*; + /// + /// #[test] + /// fn use_case() { + /// // test code + /// } + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub REDUNDANT_TEST_PREFIX, + restriction, + "redundant `test_` prefix in test function name" +} + +declare_lint_pass!(RedundantTestPrefix => [REDUNDANT_TEST_PREFIX]); + +impl<'tcx> LateLintPass<'tcx> for RedundantTestPrefix { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'_>, + _decl: &FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + fn_def_id: LocalDefId, + ) { + // Ignore methods and closures. + let FnKind::ItemFn(ref ident, ..) = kind else { + return; + }; + + // Skip the lint if the function is within a macro expansion. + if ident.span.from_expansion() { + return; + } + + // Skip if the function name does not start with `test_`. + if !ident.as_str().starts_with("test_") { + return; + } + + // If the function is not a test function, skip the lint. + if !is_test_function(cx.tcx, fn_def_id) { + return; + } + + span_lint_and_then( + cx, + REDUNDANT_TEST_PREFIX, + ident.span, + "redundant `test_` prefix in test function name", + |diag| { + let non_prefixed = Symbol::intern(ident.as_str().trim_start_matches("test_")); + if is_invalid_ident(non_prefixed) { + // If the prefix-trimmed name is not a valid function name, do not provide an + // automatic fix, just suggest renaming the function. + diag.help( + "consider function renaming (just removing `test_` prefix will produce invalid function name)", + ); + } else { + let (sugg, msg): (Cow<'_, str>, _) = if name_conflicts(cx, body, non_prefixed) { + // If `non_prefixed` conflicts with another function in the same module/scope, + // do not provide an automatic fix, but still emit a fix suggestion. + ( + format!("{non_prefixed}_works").into(), + "consider function renaming (just removing `test_` prefix will cause a name conflict)", + ) + } else { + // If `non_prefixed` is a valid identifier and does not conflict with another function, + // so we can suggest an auto-fix. + (non_prefixed.as_str().into(), "consider removing the `test_` prefix") + }; + diag.span_suggestion(ident.span, msg, sugg, Applicability::MaybeIncorrect); + } + }, + ); + } +} + +/// Checks whether removal of the `_test` prefix from the function name will cause a name conflict. +/// +/// There should be no other function with the same name in the same module/scope. Also, there +/// should not be any function call with the same name within the body of the function, to avoid +/// recursion. +fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: Symbol) -> bool { + let tcx = cx.tcx; + let id = body.id().hir_id; + + // Iterate over items in the same module/scope + let (module, _module_span, _module_hir) = tcx.hir_get_module(tcx.parent_module(id)); + if module + .item_ids + .iter() + .any(|item| matches!(tcx.hir_item(*item).kind, hir::ItemKind::Fn { ident, .. } if ident.name == fn_name)) + { + // Name conflict found + return true; + } + + // Also check that within the body of the function there is also no function call + // with the same name (since it will result in recursion) + for_each_expr(cx, body, |expr| { + if let ExprKind::Path(qpath) = &expr.kind + && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() + && let Some(name) = tcx.opt_item_name(def_id) + && name == fn_name + { + // Function call with the same name found + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + +fn is_invalid_ident(ident: Symbol) -> bool { + // The identifier is either a reserved keyword, or starts with an invalid sequence. + ident.is_reserved(|| edition::LATEST_STABLE_EDITION) || !is_ident(ident.as_str()) +} diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 9443dca154e33..834ff2af0e883 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths}; +use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths, sym}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -76,7 +76,7 @@ declare_clippy_lint! { /// This is documented as an antipattern [on the regex documentation](https://docs.rs/regex/latest/regex/#avoid-re-compiling-regexes-especially-in-a-loop) /// /// ### Example - /// ```no_run + /// ```rust,ignore /// # let haystacks = [""]; /// # const MY_REGEX: &str = "a.b"; /// for haystack in haystacks { @@ -87,7 +87,7 @@ declare_clippy_lint! { /// } /// ``` /// can be replaced with - /// ```no_run + /// ```rust,ignore /// # let haystacks = [""]; /// # const MY_REGEX: &str = "a.b"; /// let regex = regex::Regex::new(MY_REGEX).unwrap(); @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { // // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only // perform the operation once and store the results - let regex_crates = find_crates(cx.tcx, sym!(regex)); + let regex_crates = find_crates(cx.tcx, sym::regex); let mut resolve = |path: &[&str], kind: RegexKind| { for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { if let Some(id) = res.opt_def_id() { 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 33815cc3bac1b..226e8ff6adbf5 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_span::symbol::Symbol; @@ -85,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { .associated_items(did) .in_definition_order() .filter(|assoc_item| assoc_item.is_fn()) - .map(|assoc_item| assoc_item.name()) + .map(AssocItem::name) .collect() } else { BTreeSet::new() diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index 6a0dfde2d9c9c..a8c6518b592ba 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -32,28 +32,28 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) { - if did == visit_did { - let mut seen_str = None; - let mut seen_string = None; - for item in *items { - match item.ident.as_str() { - "visit_str" => seen_str = Some(item.span), - "visit_string" => seen_string = Some(item.span), - _ => {}, - } - } - if let Some(span) = seen_string { - if seen_str.is_none() { - span_lint( - cx, - SERDE_API_MISUSE, - span, - "you should not implement `visit_string` without also implementing `visit_str`", - ); - } + if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) + && did == visit_did + { + let mut seen_str = None; + let mut seen_string = None; + for item in *items { + match item.ident.as_str() { + "visit_str" => seen_str = Some(item.span), + "visit_string" => seen_string = Some(item.span), + _ => {}, } } + if let Some(span) = seen_string + && seen_str.is_none() + { + span_lint( + cx, + SERDE_API_MISUSE, + span, + "you should not implement `visit_string` without also implementing `visit_str`", + ); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index 1185d67b1258b..ff6e6ef214b5b 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert { then: then_expr, .. }) = higher::If::hir(expr) - && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) + && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym::contains)//try_parse_contains(cx, cond_expr) && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr) { span_lint( @@ -118,7 +118,7 @@ fn find_insert_calls<'tcx>( expr: &'tcx Expr<'_>, ) -> Option> { for_each_expr(cx, expr, |e| { - if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert)) + if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) { diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index b82ddedd56c0a..14399867f3181 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -8,7 +8,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{ + Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, LocalSource, Node, Pat, PatKind, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -65,7 +67,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub SHADOW_REUSE, restriction, - "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`" + "rebinding a name to an expression that reuses the original value, e.g., `let x = x + 1`" } declare_clippy_lint! { @@ -125,6 +127,17 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { return; } + // Desugaring of a destructuring assignment may reuse the same identifier internally. + // Peel `Pat` and `PatField` nodes and check if we reach a desugared `Let` assignment. + if let Some((_, Node::LetStmt(let_stmt))) = cx + .tcx + .hir_parent_iter(pat.hir_id) + .find(|(_, node)| !matches!(node, Node::Pat(_) | Node::PatField(_))) + && let LocalSource::AssignDesugar(_) = let_stmt.source + { + return; + } + let HirId { owner, local_id } = id; // get (or insert) the list of items for this owner and symbol let (ref mut data, scope_owner) = *self.bindings.last_mut().unwrap(); @@ -167,10 +180,10 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); - if let Some(first_scope) = scope_tree.var_scope(first) { - if let Some(second_scope) = scope_tree.var_scope(second) { - return scope_tree.is_subscope_of(second_scope, first_scope); - } + if let Some(first_scope) = scope_tree.var_scope(first) + && let Some(second_scope) = scope_tree.var_scope(second) + { + return scope_tree.is_subscope_of(second_scope, first_scope); } false diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 76874cc342066..ccb1209c6fcbe 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -124,8 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { diag.span_label( apa.first_block_span, format!( - "temporary `{}` is currently being dropped at the end of its contained scope", - first_bind_ident + "temporary `{first_bind_ident}` is currently being dropped at the end of its contained scope" ), ); }, @@ -145,7 +144,10 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { Self { cx, type_cache } } - fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { + if !self.cx.tcx.recursion_limit().value_within_limit(depth) { + return false; + } let ty = self .cx .tcx @@ -157,12 +159,12 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { e.insert(false); }, } - let value = self.has_sig_drop_attr_uncached(ty); + let value = self.has_sig_drop_attr_uncached(ty, depth + 1); self.type_cache.insert(ty, value); value } - fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { if let Some(adt) = ty.ty_adt_def() { let mut iter = get_attr( self.cx.sess(), @@ -177,15 +179,15 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Adt(a, b) => { for f in a.all_fields() { let ty = f.ty(self.cx.tcx, b); - if self.has_sig_drop_attr(ty) { + if self.has_sig_drop_attr(ty, depth) { return true; } } for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() { - if self.has_sig_drop_attr(ty) { - return true; - } + if let GenericArgKind::Type(ty) = generic_arg.unpack() + && self.has_sig_drop_attr(ty, depth) + { + return true; } } false @@ -193,7 +195,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Array(ty, _) | rustc_middle::ty::RawPtr(ty, _) | rustc_middle::ty::Ref(_, ty, _) - | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty), + | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty, depth), _ => false, } } @@ -269,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { apa.has_expensive_expr_after_last_attr = false; }; let mut ac = AttrChecker::new(self.cx, self.type_cache); - if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) { + if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr), 0) { if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) @@ -317,7 +319,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } }, hir::StmtKind::Semi(semi_expr) => { - if has_drop(semi_expr, &apa.first_bind_ident, self.cx) { + if has_drop(semi_expr, apa.first_bind_ident, self.cx) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -414,7 +416,7 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Option, lcx: &LateContext<'_>) -> bool { +fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: Option, lcx: &LateContext<'_>) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res @@ -424,7 +426,7 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Option, lcx: &LateCo if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind && let [first_arg_ps, ..] = arg_path.segments && let Some(first_bind_ident) = first_bind_ident - && &first_arg_ps.ident == first_bind_ident + && first_arg_ps.ident == first_bind_ident { true } else { diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs index 50a6ee316c8a6..8c34da0d14a4d 100644 --- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -45,19 +45,20 @@ impl EarlyLintPass for SingleCharLifetimeNames { return; } - if let GenericParamKind::Lifetime = param.kind { - if !param.is_placeholder && param.ident.as_str().len() <= 2 { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - ctx, - SINGLE_CHAR_LIFETIME_NAMES, - param.ident.span, - "single-character lifetime names are likely uninformative", - |diag| { - diag.help("use a more informative name"); - }, - ); - } + if let GenericParamKind::Lifetime = param.kind + && !param.is_placeholder + && param.ident.as_str().len() <= 2 + { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + |diag| { + diag.help("use a more informative name"); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 35f80b2acda68..62939912304ba 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -204,17 +204,17 @@ impl SingleComponentPathImports { if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push(SingleUse { - name, - span: tree.0.span, - item_id: item.id, - can_suggest: false, - }); - } + if segments.len() == 1 + && let UseTreeKind::Simple(None) = tree.0.kind + { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 27c548bed9f64..43a3e69610513 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, - peel_blocks, + peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -12,7 +12,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::sym; use std::ops::ControlFlow; @@ -162,13 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if is_string(cx, left) { if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { let parent = get_parent_expr(cx, e); - if let Some(p) = parent { - if let ExprKind::Assign(target, _, _) = p.kind { + if let Some(p) = parent + && let ExprKind::Assign(target, _, _) = p.kind // avoid duplicate matches - if SpanlessEq::new(cx).eq_expr(target, left) { - return; - } - } + && SpanlessEq::new(cx).eq_expr(target, left) + { + return; } } span_lint( @@ -263,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind && let ExprKind::Index(left, right, _) = args.kind && let (method_names, expressions, _) = method_calls(left, 1) - && method_names == [sym!(as_bytes)] + && method_names == [sym::as_bytes] && expressions.len() == 1 && expressions[0].1.is_empty() 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 56bd8fefdb459..83241f97a99ac 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -59,25 +59,18 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { match expr.kind { hir::ExprKind::Binary(op, _, _) => { - self.check_expr_inner(cx, expr, op.node, op.span); - } + check_expr_inner(cx, expr, op.node, op.span); + }, hir::ExprKind::AssignOp(op, _, _) => { - self.check_expr_inner(cx, expr, op.node.into(), op.span); - } - _ => {} + check_expr_inner(cx, expr, op.node.into(), op.span); + }, + _ => {}, } } } -impl<'tcx> SuspiciousImpl { - fn check_expr_inner( - &mut self, - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - binop: hir::BinOpKind, - span: Span, - ) { - if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop) +fn check_expr_inner<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, binop: hir::BinOpKind, span: Span) { + if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop) && 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) @@ -98,18 +91,17 @@ impl<'tcx> SuspiciousImpl { .iter() .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))) && count_binops(body.value) == 1 - { - span_lint( - cx, - lint, - span, - format!( - "suspicious use of `{}` in `{}` impl", - binop.as_str(), - cx.tcx.item_name(trait_id) - ), - ); - } + { + span_lint( + cx, + lint, + span, + format!( + "suspicious use of `{}` in `{}` impl", + binop.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 0337b74b4b124..e3ecd6508bf9a 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -133,7 +133,7 @@ fn generate_swap_warning<'tcx>( applicability: &mut applicability, } .snippet_index_bindings(&[idx1, idx2, rhs1, rhs2]), - slice.maybe_par(), + slice.maybe_paren(), snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0, snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0, ), @@ -269,12 +269,11 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< if let ExprKind::Assign(lhs, rhs, _) = expr.kind { return Some((ExprOrIdent::Expr(lhs), rhs)); } - } else if let StmtKind::Let(expr) = stmt.kind { - if let Some(rhs) = expr.init { - if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind { - return Some((ExprOrIdent::Ident(ident_l), rhs)); - } - } + } else if let StmtKind::Let(expr) = stmt.kind + && let Some(rhs) = expr.init + && let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind + { + return Some((ExprOrIdent::Ident(ident_l), rhs)); } None } diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index fa36c9a21f65e..8aac3a5910294 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -151,20 +151,19 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .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) + if let Some(self_segments) = self_bounds_map.get(&trait_item_res) + && SpanlessEq::new(cx) .paths_by_resolution() .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", - ); - } + { + 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", + ); } }); } 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 f2c757952af38..df2f681a16291 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 @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( } } - sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_par()).into()); + sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_paren()).into()); // cast the result of `to_bits` if `to_ty` is signed sugg = if let ty::Int(int_ty) = to_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index fcc763763bd2f..933e25fe98c65 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, "use `pointer::cast` instead", - format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_par()), + format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else if from_pointee_ty == to_pointee_ty @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, format!("use `pointer::{method}` instead"), - format!("{}.{method}()", arg.maybe_par()), + format!("{}.{method}()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 45ee83c78ab67..e58212fae15cf 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) + format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() } else { @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( } else if *from_ptr_ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) .to_string() diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index b6f4c4d7f0a41..3147058b4cda0 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -591,26 +591,26 @@ impl Types { TyKind::Path(ref qpath) if !context.in_body => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); - if let Some(def_id) = res.opt_def_id() { - if self.is_type_change_allowed(context) { - // 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_config::conf` - - let mut triggered = false; - triggered |= box_collection::check(cx, hir_ty, qpath, def_id); - triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); - triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); - triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); - triggered |= option_option::check(cx, hir_ty, qpath, def_id); - triggered |= linked_list::check(cx, hir_ty, def_id); - triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); - triggered |= owned_cow::check(cx, qpath, def_id); - - if triggered { - return; - } + if let Some(def_id) = res.opt_def_id() + && self.is_type_change_allowed(context) + { + // 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_config::conf` + + let mut triggered = false; + triggered |= box_collection::check(cx, hir_ty, qpath, def_id); + triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); + triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); + triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); + triggered |= option_option::check(cx, hir_ty, qpath, def_id); + triggered |= linked_list::check(cx, hir_ty, def_id); + triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); + triggered |= owned_cow::check(cx, qpath, def_id); + + if triggered { + return; } } match *qpath { diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 0c17cc5d8f667..d321c48f6aff8 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -23,8 +23,8 @@ declare_clippy_lint! { /// implementations. /// /// ### Why is this bad? - /// This is a hard to find infinite recursion that will crash any code - /// using it. + /// Infinite recursion in trait implementation will either cause crashes + /// or result in an infinite loop, and it is hard to detect. /// /// ### Example /// ```no_run @@ -39,9 +39,31 @@ declare_clippy_lint! { /// } /// } /// ``` + /// /// Use instead: /// - /// In such cases, either use `#[derive(PartialEq)]` or don't implement it. + /// ```no_run + /// #[derive(PartialEq)] + /// enum Foo { + /// A, + /// B, + /// } + /// ``` + /// + /// As an alternative, rewrite the logic without recursion: + /// + /// ```no_run + /// enum Foo { + /// A, + /// B, + /// } + /// + /// impl PartialEq for Foo { + /// fn eq(&self, other: &Self) -> bool { + /// matches!((self, other), (Foo::A, Foo::A) | (Foo::B, Foo::B)) + /// } + /// } + /// ``` #[clippy::version = "1.77.0"] pub UNCONDITIONAL_RECURSION, suspicious, @@ -113,7 +135,7 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt }), )) = cx.tcx.hir_parent_iter(hir_id).next() // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. && let Some(trait_) = impl_.of_trait { @@ -218,7 +240,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local }), )) = cx.tcx.hir_parent_iter(hir_id).next() // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. && let Some(trait_) = impl_.of_trait && let Some(trait_def_id) = trait_.trait_def_id() @@ -315,7 +337,7 @@ impl UnconditionalRecursion { for (ty, impl_def_ids) in impls.non_blanket_impls() { let Some(self_def_id) = ty.def() else { continue }; for impl_def_id in impl_def_ids { - if !cx.tcx.has_attr(*impl_def_id, sym::automatically_derived) && + if !cx.tcx.is_automatically_derived(*impl_def_id) && let Some(assoc_item) = cx .tcx .associated_items(impl_def_id) diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs index e1fc644e4ceeb..79571b0409d21 100644 --- a/src/tools/clippy/clippy_lints/src/unicode.rs +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -76,10 +76,10 @@ declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_ impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Lit(lit) = expr.kind { - if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node { - check_str(cx, lit.span, expr.hir_id); - } + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(_, _) | LitKind::Char(_) = lit.node + { + check_str(cx, lit.span, expr.hir_id); } } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 937e35dea96d9..bcd05cceca9c3 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -93,13 +93,13 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Abort if the method is implementing a trait or of it a trait method. let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } // Get the wrapper and inner types, if can't, abort. diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 8966e6851ac2f..9ad184450de43 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -69,10 +69,10 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if self.msrv.meets(msrvs::OR_PATTERNS) { - if let ast::ExprKind::Let(pat, _, _, _) = &e.kind { - lint_unnested_or_patterns(cx, pat); - } + if self.msrv.meets(msrvs::OR_PATTERNS) + && let ast::ExprKind::Let(pat, _, _, _) = &e.kind + { + lint_unnested_or_patterns(cx, pat); } } @@ -120,18 +120,25 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { /// Remove all `(p)` patterns in `pat`. fn remove_all_parens(pat: &mut P) { - struct Visitor; + #[derive(Default)] + struct Visitor { + /// If is not in the outer most pattern. This is needed to avoid removing the outermost + /// parens because top-level or-patterns are not allowed in let statements. + is_inner: bool, + } + impl MutVisitor for Visitor { fn visit_pat(&mut self, pat: &mut P) { + let is_inner = mem::replace(&mut self.is_inner, true); walk_pat(self, pat); let inner = match &mut pat.kind { - Paren(i) => mem::replace(&mut i.kind, Wild), + Paren(i) if is_inner => mem::replace(&mut i.kind, Wild), _ => return, }; pat.kind = inner; } } - Visitor.visit_pat(pat); + Visitor::default().visit_pat(pat); } /// Insert parens where necessary according to Rust's precedence rules for patterns. diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 0687fc319af68..2d88c490b1abe 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -265,15 +265,14 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [arg_0]) = expr.kind { - if matches!( - func.kind, - ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) - ) { - return arg_0; - } - } + if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind + && let ExprKind::Call(func, [arg_0]) = expr.kind + && matches!( + func.kind, + ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) + { + return arg_0; } expr } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index b466a8e127a94..ce82b56eb946f 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,7 +1,7 @@ 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 clippy_utils::{higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; @@ -11,8 +11,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -307,8 +307,8 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { 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) + && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) + && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) && let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.local_id == id) // Span contexts should not differ with the conditional branch diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 57bb2fc27f145..3a9c997a579d1 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -92,36 +92,36 @@ fn into_iter_bound<'tcx>( let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { - if tr.self_ty().is_param(param_index) { - if tr.def_id() == into_iter_did { - into_iter_span = Some(*span); - } else { - let tr = cx.tcx.erase_regions(tr); - if tr.has_escaping_bound_vars() { - return None; - } - - // Substitute generics in the predicate and replace the IntoIterator type parameter with the - // `.into_iter()` receiver to see if the bound also holds for that type. - let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { - if i == param_index as usize { - GenericArg::from(into_iter_receiver) - } else { - arg - } - })); + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() + && tr.self_ty().is_param(param_index) + { + if tr.def_id() == into_iter_did { + into_iter_span = Some(*span); + } else { + let tr = cx.tcx.erase_regions(tr); + if tr.has_escaping_bound_vars() { + return None; + } - let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - if !cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .predicate_must_hold_modulo_regions(&obligation) - { - return None; + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg } + })); + + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .predicate_must_hold_modulo_regions(&obligation) + { + return None; } } } @@ -356,7 +356,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { 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 = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs deleted file mode 100644 index deb983b6971dc..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod almost_standard_lint_formulation; -pub mod collapsible_calls; -pub mod interning_defined_symbol; -pub mod invalid_paths; -pub mod lint_without_lint_pass; -pub mod msrv_attr_impl; -pub mod outer_expn_data_pass; -pub mod produce_ice; -pub mod slow_symbol_comparisons; -pub mod unnecessary_def_path; -pub mod unsorted_clippy_utils_paths; 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 deleted file mode 100644 index e454427adde1b..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ /dev/null @@ -1,241 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::ConstValue; -use rustc_middle::ty; -use rustc_session::impl_lint_pass; -use rustc_span::sym; -use rustc_span::symbol::Symbol; - -use std::borrow::Cow; - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - for def_id in def_path_def_ids(cx.tcx, module) { - for item in cx.tcx.module_children(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 Some(value) = value.to_u32().discard_err() - { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Call(func, [arg]) = &expr.kind - && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() - && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) - && let value = Symbol::intern(&arg).as_u32() - && let Some(&def_id) = self.symbol_map.get(&value) - { - 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) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -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 let ExprKind::AddrOf(_, _, e) = expr.kind - && let ExprKind::Unary(UnOp::Deref, e) = e.kind - { - e - } else { - expr - }; - if let ExprKind::MethodCall(_, item, [], _) = call.kind - // is a method call - && 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 - && 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 } - && let Some(is_to_owned) = paths - .iter() - .find_map(|path| if match_def_path(cx, did, path) { - Some(path == &paths::SYMBOL_TO_IDENT_STRING) - } else { - None - }) - .or_else(|| if cx.tcx.is_diagnostic_item(sym::to_string_method, did) { - Some(true) - } else { - None - }) - { - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - // is a string constant - if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs deleted file mode 100644 index b8bcb9b375601..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs +++ /dev/null @@ -1,74 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::match_type; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; - -declare_clippy_lint! { - /// ### What it does - /// - /// Detects symbol comparison using `Symbol::intern`. - /// - /// ### Why is this bad? - /// - /// Comparison via `Symbol::as_str()` is faster if the interned symbols are not reused. - /// - /// ### Example - /// - /// None, see suggestion. - pub SLOW_SYMBOL_COMPARISONS, - internal, - "detects slow comparisons of symbol" -} - -declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]); - -fn check_slow_comparison<'tcx>( - cx: &LateContext<'tcx>, - op1: &'tcx Expr<'tcx>, - op2: &'tcx Expr<'tcx>, -) -> Option<(Span, String)> { - if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL) - && let ExprKind::Call(fun, args) = op2.kind - && let ExprKind::Path(ref qpath) = fun.kind - && cx - .tcx - .is_diagnostic_item(sym::SymbolIntern, cx.qpath_res(qpath, fun.hir_id).opt_def_id()?) - && let [symbol_name_expr] = args - && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr) - { - Some((op1.span, symbol_name)) - } else { - None - } -} - -impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let ExprKind::Binary(op, left, right) = expr.kind - && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) - && let Some((symbol_span, symbol_name)) = - check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left)) - { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - SLOW_SYMBOL_COMPARISONS, - expr.span, - "comparing `Symbol` via `Symbol::intern`", - "use `Symbol::as_str` and check the string instead", - format!( - "{}.as_str() {} \"{symbol_name}\"", - snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability), - op.node.as_str() - ), - applicability, - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs deleted file mode 100644 index a5c4bf474f7aa..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs +++ /dev/null @@ -1,49 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Crate, ItemKind, ModKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; - -declare_clippy_lint! { - /// ### What it does - /// Checks that [`clippy_utils::paths`] is sorted lexically - /// - /// ### Why is this bad? - /// We like to pretend we're an example of tidy code. - /// - /// ### Example - /// Wrong ordering of the util::paths constants. - pub UNSORTED_CLIPPY_UTILS_PATHS, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]); - -impl EarlyLintPass for UnsortedClippyUtilsPaths { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - UNSORTED_CLIPPY_UTILS_PATHS, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 4476cd1005e7e..16066dd96c0ab 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -2,6 +2,3 @@ pub mod attr_collector; pub mod author; pub mod dump_hir; pub mod format_args_collector; - -#[cfg(feature = "internal")] -pub mod internal_lints; diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 405310512dff3..5b3f60ad6ab0b 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -68,6 +68,8 @@ declare_clippy_lint! { /// (including the standard library) provide modules named "prelude" specifically designed /// for wildcard import. /// + /// Wildcard imports reexported through `pub use` are also allowed. + /// /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. /// /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. @@ -121,7 +123,9 @@ impl LateLintPass<'_> for WildcardImports { } let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id); - if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { + if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) + && !self.warn_on_all + { return; } if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind 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 f6948be7f67aa..a97643e0eaca5 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 @@ -74,10 +74,10 @@ impl LateLintPass<'_> for ZeroSizedMapValues { fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { let parent_id = cx.tcx.hir_get_parent_item(hir_id); let second_parent_id = cx.tcx.hir_get_parent_item(parent_id.into()).def_id; - if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) { - if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return true; - } + if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) + && let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind + { + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index 7667db469689e..39c1aab8967ad 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -131,7 +131,7 @@ struct WaitFinder<'a, 'tcx> { local_id: HirId, state: VisitorState, early_return: Option, - // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targetted help + // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targeted help // messages missing_wait_branch: Option, } diff --git a/src/tools/clippy/clippy_lints_internal/Cargo.toml b/src/tools/clippy/clippy_lints_internal/Cargo.toml new file mode 100644 index 0000000000000..2a0ceac27a324 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "clippy_lints_internal" +version = "0.0.1" +edition = "2021" + +[dependencies] +clippy_config = { path = "../clippy_config" } +clippy_utils = { path = "../clippy_utils" } +regex = { version = "1.5" } +rustc-semver = "1.1" + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)] +rustc_private = true diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs similarity index 92% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs rename to src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs index 0a01a364a75b9..4fd5ea459a554 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs @@ -1,11 +1,12 @@ -use crate::utils::internal_lints::lint_without_lint_pass::is_lint_ref_type; +use crate::lint_without_lint_pass::is_lint_ref_type; use clippy_utils::diagnostics::span_lint_and_help; use regex::Regex; use rustc_hir::{Attribute, Item, ItemKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::impl_lint_pass; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks if lint formulations have a standardized format. /// @@ -14,9 +15,10 @@ declare_clippy_lint! { /// /// ### Example /// `Checks for use...` can be written as `Checks for usage...` . - pub ALMOST_STANDARD_LINT_FORMULATION, - internal, - "lint formulations must have a standardized format." + pub clippy::ALMOST_STANDARD_LINT_FORMULATION, + Warn, + "lint formulations must have a standardized format.", + report_in_external_macro: true } impl_lint_pass!(AlmostStandardFormulation => [ALMOST_STANDARD_LINT_FORMULATION]); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs similarity index 97% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs rename to src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs index 2e6fb7c4ce4d5..d7967a0cc022f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs @@ -4,12 +4,13 @@ use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_bloc use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; use rustc_span::Span; use std::borrow::{Borrow, Cow}; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Lints `span_lint_and_then` function calls, where the /// closure argument has only one statement and that statement is a method @@ -64,9 +65,10 @@ declare_clippy_lint! { /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" + pub clippy::COLLAPSIBLE_SPAN_LINT_CALLS, + Warn, + "found collapsible `span_lint_and_then` calls", + report_in_external_macro: true } declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); diff --git a/src/tools/clippy/clippy_lints_internal/src/interning_literals.rs b/src/tools/clippy/clippy_lints_internal/src/interning_literals.rs new file mode 100644 index 0000000000000..6cee37442349c --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/interning_literals.rs @@ -0,0 +1,102 @@ +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::match_type; +use clippy_utils::{def_path_def_ids, paths}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; +use rustc_middle::mir::ConstValue; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; +use rustc_span::sym; +use rustc_span::symbol::Symbol; + +declare_tool_lint! { + /// ### What it does + /// Checks for interning string literals as symbols + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// let _ = Symbol::intern("f32"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub clippy::INTERNING_LITERALS, + Warn, + "interning a symbol that is a literal", + report_in_external_macro: true +} + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol to the import path + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_LITERALS]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>) { + let modules = [ + ("kw", &paths::KW_MODULE[..]), + ("sym", &paths::SYM_MODULE), + ("sym", &paths::CLIPPY_SYM_MODULE), + ]; + for (prefix, module) in modules { + for def_id in def_path_def_ids(cx.tcx, module) { + // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will + // still lint but the suggestion will say to add it to `sym.rs` even if it's already there + if def_id.is_local() { + continue; + } + + for item in cx.tcx.module_children(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 Some(value) = value.to_u32().discard_err() + { + self.symbol_map.insert(value, (prefix, item.ident.name)); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Call(func, [arg]) = &expr.kind + && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() + && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) + && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) + { + span_lint_and_then( + cx, + INTERNING_LITERALS, + expr.span, + "interning a string literal", + |diag| { + let value = Symbol::intern(&arg).as_u32(); + let (message, path) = if let Some((prefix, name)) = self.symbol_map.get(&value) { + ("use the preinterned symbol", format!("{prefix}::{name}")) + } else { + ( + "add the symbol to `clippy_utils/src/sym.rs` and use it", + format!("sym::{}", arg.replace(|ch: char| !ch.is_alphanumeric(), "_")), + ) + }; + diag.span_suggestion_verbose(expr.span, message, path, Applicability::MaybeIncorrect); + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints_internal/src/invalid_paths.rs similarity index 79% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs rename to src/tools/clippy/clippy_lints_internal/src/invalid_paths.rs index 252ac5e676822..bee87efa3fcd4 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints_internal/src/invalid_paths.rs @@ -5,12 +5,13 @@ use rustc_hir as hir; use rustc_hir::Item; use rustc_hir::def::DefKind; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, FloatTy}; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks the paths module for invalid paths. /// @@ -19,9 +20,10 @@ declare_clippy_lint! { /// /// ### Example /// None. - pub INVALID_PATHS, - internal, - "invalid path" + pub clippy::INVALID_PATHS, + Warn, + "invalid path", + report_in_external_macro: true } declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); @@ -80,22 +82,22 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { .copied(); for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { let lang_item_path = cx.get_def_path(item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(item_def_id) { - if child.ident.name == *item { - return true; - } + if path_syms.starts_with(&lang_item_path) + && let [item] = &path_syms[lang_item_path.len()..] + { + if matches!( + cx.tcx.def_kind(item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(item_def_id) { + if child.ident.name == *item { + return true; } - } else { - for child in cx.tcx.associated_item_def_ids(item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } + } + } else { + for child in cx.tcx.associated_item_def_ids(item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; } } } diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs new file mode 100644 index 0000000000000..1c42f4112f9a1 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -0,0 +1,74 @@ +#![feature(let_chains, rustc_private)] +#![allow( + clippy::missing_docs_in_private_items, + clippy::must_use_candidate, + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic +)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications, + rustc::internal +)] +// Disable this rustc lint for now, as it was also done in rustc +#![allow(rustc::potential_query_instability)] +// None of these lints need a version. +#![allow(clippy::missing_clippy_version_attribute)] + +extern crate rustc_ast; +extern crate rustc_attr_parsing; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_lint_defs; +extern crate rustc_middle; +extern crate rustc_session; +extern crate rustc_span; + +mod almost_standard_lint_formulation; +mod collapsible_calls; +mod interning_literals; +mod invalid_paths; +mod lint_without_lint_pass; +mod msrv_attr_impl; +mod outer_expn_data_pass; +mod produce_ice; +mod unnecessary_def_path; +mod unsorted_clippy_utils_paths; + +use rustc_lint::{Lint, LintStore}; + +static LINTS: &[&Lint] = &[ + almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, + collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, + interning_literals::INTERNING_LITERALS, + invalid_paths::INVALID_PATHS, + lint_without_lint_pass::DEFAULT_LINT, + lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, + lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, + lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, + msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, + outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, + produce_ice::PRODUCE_ICE, + unnecessary_def_path::UNNECESSARY_DEF_PATH, + unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, +]; + +pub fn register_lints(store: &mut LintStore) { + store.register_lints(LINTS); + + store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); + store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); + store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs similarity index 90% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs rename to src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs index 94a2e598522b2..6a75defcce341 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -9,13 +9,14 @@ use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Ensures every lint is associated to a `LintPass`. /// @@ -37,12 +38,14 @@ declare_clippy_lint! { /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); /// // missing FORGOTTEN_LINT /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" + pub clippy::LINT_WITHOUT_LINT_PASS, + Warn, + "declaring a lint without associating it in a LintPass", + report_in_external_macro: true + } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for cases of an auto-generated lint without an updated description, /// i.e. `default lint description`. @@ -59,30 +62,32 @@ declare_clippy_lint! { /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" + pub clippy::DEFAULT_LINT, + Warn, + "found 'default lint description' in a lint declaration", + report_in_external_macro: true } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for invalid `clippy::version` attributes. /// /// Valid values are: /// * "pre 1.29.0" /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" + pub clippy::INVALID_CLIPPY_VERSION_ATTRIBUTE, + Warn, + "found an invalid `clippy::version` attribute", + report_in_external_macro: true } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" + pub clippy::MISSING_CLIPPY_VERSION_ATTRIBUTE, + Warn, + "found clippy lint without `clippy::version` attribute", + report_in_external_macro: true } #[derive(Clone, Debug, Default)] @@ -100,10 +105,6 @@ impl_lint_pass!(LintWithoutLintPass => [ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) { - return; - } - if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { check_invalid_clippy_version_attribute(cx, item); @@ -205,12 +206,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { mutbl: Mutability::Not, }, ) = ty.kind + && let TyKind::Path(ref path) = inner.kind + && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } + return match_def_path(cx, def_id, &paths::LINT); } false diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs similarity index 93% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs rename to src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index 558acacb97245..dda054546e262 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -5,16 +5,17 @@ use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint_defs::declare_tool_lint; use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; use rustc_session::declare_lint_pass; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" + pub clippy::MISSING_MSRV_ATTR_IMPL, + Warn, + "checking if all necessary steps were taken when adding a MSRV to a lint", + report_in_external_macro: true } declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs similarity index 92% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs rename to src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs index 326e172146130..e94419647978c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -4,10 +4,11 @@ use clippy_utils::{is_lint_allowed, method_calls, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for calls to `cx.outer().expn_data()` and suggests to use /// the `cx.outer_expn_data()` @@ -24,9 +25,10 @@ declare_clippy_lint! { /// ```rust,ignore /// expr.span.ctxt().outer_expn_data() /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" + pub clippy::OUTER_EXPN_EXPN_DATA, + Warn, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`", + report_in_external_macro: true } declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs similarity index 79% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs rename to src/tools/clippy/clippy_lints_internal/src/produce_ice.rs index 0a07919d659fe..14e93dc6d5f13 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs +++ b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs @@ -1,10 +1,11 @@ use rustc_ast::ast::NodeId; use rustc_ast::visit::FnKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; use rustc_span::Span; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Not an actual lint. This lint is only meant for testing our customized internal compiler /// error message by calling `panic`. @@ -16,9 +17,10 @@ declare_clippy_lint! { /// ```rust,ignore /// 🍦🍦🍦🍦🍦 /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" + pub clippy::PRODUCE_ICE, + Warn, + "this message should not appear anywhere as we ICE before and don't emit the lint", + report_in_external_macro: true } declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); @@ -35,7 +37,7 @@ impl EarlyLintPass for ProduceIce { fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Fn(_, _, func) => func.ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", FnKind::Closure(..) => false, } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs similarity index 97% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs rename to src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs index 76b0a52621be4..6bdfbed55b062 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs @@ -8,6 +8,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; use rustc_middle::ty::{self, Ty}; @@ -17,7 +18,7 @@ use rustc_span::symbol::Symbol; use std::str; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. /// @@ -34,9 +35,10 @@ declare_clippy_lint! { /// ```rust,ignore /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" + pub clippy::UNNECESSARY_DEF_PATH, + Warn, + "using a def path when a diagnostic item or a `LangItem` is available", + report_in_external_macro: true } impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); @@ -281,10 +283,10 @@ fn path_from_array(exprs: &[Expr<'_>]) -> Option> { exprs .iter() .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(sym, _) = lit.node + { + return Some((*sym.as_str()).to_owned()); } None diff --git a/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs new file mode 100644 index 0000000000000..8e281ecb2ee44 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint_defs::declare_tool_lint; +use rustc_session::declare_lint_pass; + +declare_tool_lint! { + /// ### What it does + /// Checks that [`clippy_utils::paths`] is sorted lexically + /// + /// ### Why is this bad? + /// We like to pretend we're an example of tidy code. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub clippy::UNSORTED_CLIPPY_UTILS_PATHS, + Warn, + "various things that will negatively affect your clippy experience", + report_in_external_macro: true +} + +declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]); + +impl EarlyLintPass for UnsortedClippyUtilsPaths { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate + .items + .iter() + .find(|item| item.kind.ident().is_some_and(|i| i.name.as_str() == "utils")) + && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = utils.kind + && let Some(paths) = items + .iter() + .find(|item| item.kind.ident().is_some_and(|i| i.name.as_str() == "paths")) + && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = paths.kind + { + let mut last_name: Option = None; + for item in items { + let name = item.kind.ident().expect("const items have idents").to_string(); + if let Some(last_name) = last_name + && *last_name > *name + { + span_lint( + cx, + UNSORTED_CLIPPY_UTILS_PATHS, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + last_name = Some(name); + } + } + } +} diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index ba4bb1d177c57..b98e990175033 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.87" +version = "0.1.88" # end autogenerated version edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 7c665b4249776..aceff14a1599c 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-03-20 +nightly-2025-04-22 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index c5dce26143be4..8996b694ed8f7 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -348,11 +348,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { safety: rs, define_opaque: _, }), - ) => eq_id(*li, *ri) - && lm == rm - && ls == rs - && eq_ty(lt, rt) - && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( Const(box ConstItem { defaultness: ld, @@ -370,11 +366,13 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: re, define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) + ) => { + eq_defaultness(*ld, *rd) && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_ty(lt, rt) - && eq_expr_opt(le.as_ref(), re.as_ref()), + && eq_expr_opt(le.as_ref(), re.as_ref()) + }, ( Fn(box ast::Fn { defaultness: ld, @@ -440,7 +438,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }, (Enum(li, le, lg), Enum(ri, re, rg)) => { eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg) - } + }, (Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => { eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg) }, @@ -471,7 +469,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }, (TraitAlias(li, lg, lb), TraitAlias(ri, rg, rb)) => { eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) - } + }, ( Impl(box ast::Impl { safety: lu, @@ -506,7 +504,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (MacroDef(li, ld), MacroDef(ri, rd)) => { eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body) - } + }, _ => false, } } @@ -531,13 +529,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { safety: rs, define_opaque: _, }), - ) => { - eq_id(*li, *ri) - && eq_ty(lt, rt) - && lm == rm - && eq_expr_opt(le.as_ref(), re.as_ref()) - && ls == rs - } + ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, ( Fn(box ast::Fn { defaultness: ld, @@ -620,7 +612,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) - } + }, ( Fn(box ast::Fn { defaultness: ld, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 17751e824c021..b9928b8eed497 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -43,14 +43,16 @@ pub enum Constant<'tcx> { Char(char), /// An integer's bit representation. Int(u128), - /// An `f16`. - F16(f16), + /// An `f16` bitcast to a `u16`. + // FIXME(f16_f128): use `f16` once builtins are available on all host tools platforms. + F16(u16), /// An `f32`. F32(f32), /// An `f64`. F64(f64), - /// An `f128`. - F128(f128), + /// An `f128` bitcast to a `u128`. + // FIXME(f16_f128): use `f128` once builtins are available on all host tools platforms. + F128(u128), /// `true` or `false`. Bool(bool), /// An array of constants. @@ -177,7 +179,7 @@ impl Hash for Constant<'_> { }, Self::F16(f) => { // FIXME(f16_f128): once conversions to/from `f128` are available on all platforms, - f.to_bits().hash(state); + f.hash(state); }, Self::F32(f) => { f64::from(f).to_bits().hash(state); @@ -186,7 +188,7 @@ impl Hash for Constant<'_> { f.to_bits().hash(state); }, Self::F128(f) => { - f.to_bits().hash(state); + f.hash(state); }, Self::Bool(b) => { b.hash(state); @@ -292,12 +294,12 @@ impl Constant<'_> { fn parse_f16(s: &str) -> Self { let f: Half = s.parse().unwrap(); - Self::F16(f16::from_bits(f.to_bits().try_into().unwrap())) + Self::F16(f.to_bits().try_into().unwrap()) } fn parse_f128(s: &str) -> Self { let f: Quad = s.parse().unwrap(); - Self::F128(f128::from_bits(f.to_bits())) + Self::F128(f.to_bits()) } } @@ -868,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), - ty::Float(FloatTy::F16) => Some(Constant::F16(f16::from_bits(int.into()))), + ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), - ty::Float(FloatTy::F128) => Some(Constant::F128(f128::from_bits(int.into()))), + ty::Float(FloatTy::F128) => Some(Constant::F128(int.into())), ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, @@ -892,10 +894,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F16 => Constant::F16(val.to_u16().discard_err()?), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), + FloatTy::F128 => Constant::F128(val.to_u128().discard_err()?), }); } Some(Constant::Vec(res)) diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 292792408c642..cd2098a89891d 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -17,16 +17,16 @@ use rustc_span::Span; use std::env; fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { - if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { - if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", - &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { - // extract just major + minor version and ignore patch versions - format!("rust-{}", n.rsplit_once('.').unwrap().1) - }) - )); - } + if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() + && let Some(lint) = lint.name_lower().strip_prefix("clippy::") + { + diag.help(format!( + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", + &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { + // extract just major + minor version and ignore patch versions + format!("rust-{}", n.rsplit_once('.').unwrap().1) + }) + )); } } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index c4d00002292c9..d4e66ebd8e1fb 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -118,18 +118,17 @@ impl<'hir> IfLet<'hir> { ) = expr.kind { let mut iter = cx.tcx.hir_parent_iter(expr.hir_id); - if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { - if let Some(( + if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() + && let Some(( _, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }), )) = iter.next() - { - // while loop desugar - return None; - } + { + // while loop desugar + return None; } return Some(Self { let_pat, @@ -176,6 +175,12 @@ impl<'hir> IfLetOrMatch<'hir> { ), } } + + pub fn scrutinee(&self) -> &'hir Expr<'hir> { + match self { + Self::Match(scrutinee, _, _) | Self::IfLet(scrutinee, _, _, _, _) => scrutinee, + } + } } /// An `if` or `if let` expression diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index be295b59f609e..fe1fd70a9fa78 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -148,7 +148,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { - (&StmtKind::Let(l), &StmtKind::Let(r)) => { + (StmtKind::Let(l), StmtKind::Let(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { @@ -166,7 +166,7 @@ impl HirEqInterExpr<'_, '_, '_> { && both(l.els.as_ref(), r.els.as_ref(), |l, r| self.eq_block(l, r)) && self.eq_pat(l.pat, r.pat) }, - (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), + (StmtKind::Expr(l), StmtKind::Expr(r)) | (StmtKind::Semi(l), StmtKind::Semi(r)) => self.eq_expr(l, r), _ => false, } } @@ -260,7 +260,7 @@ impl HirEqInterExpr<'_, '_, '_> { fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { macro_backtrace(expr.span).last().is_some_and(|macro_call| { matches!( - &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), + self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::todo_macro | sym::unimplemented_macro) ) }) @@ -301,58 +301,58 @@ impl HirEqInterExpr<'_, '_, '_> { reduce_exprkind(self.inner.cx, &left.kind), reduce_exprkind(self.inner.cx, &right.kind), ) { - (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { + (ExprKind::AddrOf(lb, l_mut, le), ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, - (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), - (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => { + (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) }, - (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => { + (ExprKind::AssignOp(lo, ll, lr), ExprKind::AssignOp(ro, rl, rr)) => { self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, - (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r), - (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => { + (ExprKind::Block(l, _), ExprKind::Block(r, _)) => self.eq_block(l, r), + (ExprKind::Binary(l_op, ll, lr), ExprKind::Binary(r_op, rl, rr)) => { l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) || swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| { l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }) }, - (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { + (ExprKind::Break(li, le), ExprKind::Break(ri, re)) => { both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name) && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { + (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::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)) => { + (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.as_ref(), ri.label.as_ref(), |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)) => { + (ExprKind::DropTemps(le), ExprKind::DropTemps(re)) => self.eq_expr(le, re), + (ExprKind::Field(l_f_exp, l_f_ident), ExprKind::Field(r_f_exp, r_f_ident)) => { l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) }, - (&ExprKind::Index(la, li, _), &ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), - (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { + (ExprKind::Index(la, li, _), ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), + (ExprKind::If(lc, lt, le), ExprKind::If(rc, rt, re)) => { self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Let(l), &ExprKind::Let(r)) => { + (ExprKind::Let(l), ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, - (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { + (ExprKind::Loop(lb, ll, lls, _), ExprKind::Loop(rb, rl, rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => { + (ExprKind::Match(le, la, ls), ExprKind::Match(re, ra, rs)) => { (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_))))) && self.eq_expr(le, re) && over(la, ra, |l, r| { @@ -362,27 +362,27 @@ impl HirEqInterExpr<'_, '_, '_> { }) }, ( - &ExprKind::MethodCall(l_path, l_receiver, l_args, _), - &ExprKind::MethodCall(r_path, r_receiver, r_args, _), + ExprKind::MethodCall(l_path, l_receiver, l_args, _), + ExprKind::MethodCall(r_path, r_receiver, r_args, _), ) => { self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_expr(l_receiver, r_receiver) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) => + (ExprKind::UnsafeBinderCast(lkind, le, None), ExprKind::UnsafeBinderCast(rkind, re, None)) => lkind == rkind && self.eq_expr(le, re), - (&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => + (ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt), - (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { + (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)) => { + (ExprKind::Repeat(le, ll), ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_const_arg(ll, rl) }, (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)), - (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { + (ExprKind::Struct(l_path, lf, lo), ExprKind::Struct(r_path, rf, ro)) => { self.eq_qpath(l_path, r_path) && match (lo, ro) { (StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r), @@ -392,58 +392,58 @@ impl HirEqInterExpr<'_, '_, '_> { } && 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::Use(l_expr, _), &ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr), - (&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::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re), + (ExprKind::Tup(l_tup), ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), + (ExprKind::Use(l_expr, _), ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr), + (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::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::Use(..) - | &ExprKind::Type(..) - | &ExprKind::Unary(..) - | &ExprKind::Yield(..) - | &ExprKind::UnsafeBinderCast(..) + | 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::Use(..) + | ExprKind::Type(..) + | ExprKind::Unary(..) + | ExprKind::Yield(..) + | ExprKind::UnsafeBinderCast(..) // --- 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(..) + | ExprKind::Err(..) // For the time being, we always consider that two closures are unequal. // This behavior may change in the future. - | &ExprKind::Closure(..) + | 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(_) + | ExprKind::InlineAsm(_) , _ ) => false, }; @@ -494,11 +494,11 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { match (&left.kind, &right.kind) { ( - &PatExprKind::Lit { + PatExprKind::Lit { lit: left, negated: left_neg, }, - &PatExprKind::Lit { + PatExprKind::Lit { lit: right, negated: right_neg, }, @@ -512,47 +512,47 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { - (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r), - (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => { + (PatKind::Box(l), PatKind::Box(r)) => self.eq_pat(l, r), + (PatKind::Struct(lp, la, ..), PatKind::Struct(rp, ra, ..)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r)) }, - (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => { + (PatKind::TupleStruct(lp, la, ls), PatKind::TupleStruct(rp, ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, - (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { + (PatKind::Binding(lb, li, _, lp), PatKind::Binding(rb, ri, _, rp)) => { let eq = lb == rb && both(lp.as_ref(), rp.as_ref(), |l, r| self.eq_pat(l, r)); if eq { - self.locals.insert(li, ri); + self.locals.insert(*li, *ri); } eq }, - (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), - (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), - (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { + (PatKind::Expr(l), PatKind::Expr(r)) => self.eq_pat_expr(l, r), + (PatKind::Tuple(l, ls), PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), + (PatKind::Range(ls, le, li), PatKind::Range(rs, re, ri)) => { both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, - (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), - (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => { + (PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re), + (PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => { over(ls, rs, |l, r| self.eq_pat(l, r)) && over(le, re, |l, r| self.eq_pat(l, r)) && both(li.as_ref(), ri.as_ref(), |l, r| self.eq_pat(l, r)) }, - (&PatKind::Wild, &PatKind::Wild) => true, + (PatKind::Wild, PatKind::Wild) => true, _ => false, } } fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { match (left, right) { - (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { + (QPath::Resolved(lty, lpath), QPath::Resolved(rty, rpath)) => { both(lty.as_ref(), rty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath) }, - (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => { + (QPath::TypeRelative(lty, lseg), QPath::TypeRelative(rty, rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, - (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, + (QPath::LangItem(llang_item, ..), QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, _ => false, } } @@ -611,15 +611,15 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { - (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), - (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl), + (TyKind::Slice(l_vec), TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), + (TyKind::Array(lt, ll), TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl), (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), - (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), - (&TyKind::Infer(()), &TyKind::Infer(())) => true, + (TyKind::Tup(l), TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), + (TyKind::Infer(()), TyKind::Infer(())) => true, _ => false, } } @@ -853,9 +853,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&e.kind).hash(&mut self.s); - match e.kind { + match &e.kind { ExprKind::AddrOf(kind, m, e) => { - std::mem::discriminant(&kind).hash(&mut self.s); + std::mem::discriminant(kind).hash(&mut self.s); m.hash(&mut self.s); self.hash_expr(e); }, @@ -871,7 +871,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::AssignOp(ref o, l, r) => { + ExprKind::AssignOp(o, l, r) => { std::mem::discriminant(&o.node).hash(&mut self.s); self.hash_expr(l); self.hash_expr(r); @@ -887,11 +887,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::Break(i, ref j) => { + ExprKind::Break(i, j) => { if let Some(i) = i.label { self.hash_name(i.ident.name); } - if let Some(j) = *j { + if let Some(j) = j { self.hash_expr(j); } }, @@ -903,20 +903,20 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); self.hash_ty(ty); }, - ExprKind::Closure(&Closure { + ExprKind::Closure(Closure { capture_clause, body, .. }) => { - std::mem::discriminant(&capture_clause).hash(&mut self.s); + std::mem::discriminant(capture_clause).hash(&mut self.s); // closures inherit TypeckResults - self.hash_expr(self.cx.tcx.hir_body(body).value); + self.hash_expr(self.cx.tcx.hir_body(*body).value); }, - ExprKind::ConstBlock(ref l_id) => { + ExprKind::ConstBlock(l_id) => { self.hash_body(l_id.body); }, ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { self.hash_expr(e); }, - ExprKind::Field(e, ref f) => { + ExprKind::Field(e, f) => { self.hash_expr(e); self.hash_name(f.name); }, @@ -991,23 +991,23 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Lit(l) => { l.node.hash(&mut self.s); }, - ExprKind::Loop(b, ref i, ..) => { + ExprKind::Loop(b, i, ..) => { self.hash_block(b); - if let Some(i) = *i { + if let Some(i) = i { self.hash_name(i.ident.name); } }, - ExprKind::If(cond, then, ref else_opt) => { + ExprKind::If(cond, then, else_opt) => { self.hash_expr(cond); self.hash_expr(then); - if let Some(e) = *else_opt { + if let Some(e) = else_opt { self.hash_expr(e); } }, - ExprKind::Match(e, arms, ref s) => { + ExprKind::Match(e, arms, s) => { self.hash_expr(e); - for arm in arms { + for arm in *arms { self.hash_pat(arm.pat); if let Some(e) = arm.guard { self.hash_expr(e); @@ -1017,38 +1017,38 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { s.hash(&mut self.s); }, - ExprKind::MethodCall(path, receiver, args, ref _fn_span) => { + ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); self.hash_expr(receiver); self.hash_exprs(args); }, ExprKind::OffsetOf(container, fields) => { self.hash_ty(container); - for field in fields { + for field in *fields { self.hash_name(field.name); } }, - ExprKind::Path(ref qpath) => { + ExprKind::Path(qpath) => { self.hash_qpath(qpath); }, ExprKind::Repeat(e, len) => { self.hash_expr(e); self.hash_const_arg(len); }, - ExprKind::Ret(ref e) => { - if let Some(e) = *e { + ExprKind::Ret(e) => { + if let Some(e) = e { self.hash_expr(e); } }, - ExprKind::Struct(path, fields, ref expr) => { + ExprKind::Struct(path, fields, expr) => { self.hash_qpath(path); - for f in fields { + for f in *fields { self.hash_name(f.ident.name); self.hash_expr(f.expr); } - if let StructTailExpr::Base(e) = *expr { + if let StructTailExpr::Base(e) = expr { self.hash_expr(e); } }, @@ -1059,11 +1059,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(expr); }, ExprKind::Unary(lop, le) => { - std::mem::discriminant(&lop).hash(&mut self.s); + std::mem::discriminant(lop).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { - std::mem::discriminant(&kind).hash(&mut self.s); + std::mem::discriminant(kind).hash(&mut self.s); self.hash_expr(expr); if let Some(ty) = ty { self.hash_ty(ty); @@ -1084,7 +1084,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_qpath(&mut self, p: &QPath<'_>) { - match *p { + match p { QPath::Resolved(_, path) => { self.hash_path(path); }, @@ -1092,7 +1092,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(path.ident.name); }, QPath::LangItem(lang_item, ..) => { - std::mem::discriminant(&lang_item).hash(&mut self.s); + std::mem::discriminant(lang_item).hash(&mut self.s); }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); @@ -1123,11 +1123,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); - match pat.kind { + match &pat.kind { PatKind::Missing => unreachable!(), PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => { - std::mem::discriminant(&by_ref).hash(&mut self.s); - std::mem::discriminant(&mutability).hash(&mut self.s); + std::mem::discriminant(by_ref).hash(&mut self.s); + std::mem::discriminant(mutability).hash(&mut self.s); if let Some(pat) = pat { self.hash_pat(pat); } @@ -1135,7 +1135,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { - for pat in pats { + for pat in *pats { self.hash_pat(pat); } }, @@ -1146,44 +1146,44 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(e) = e { self.hash_pat_expr(e); } - std::mem::discriminant(&i).hash(&mut self.s); + std::mem::discriminant(i).hash(&mut self.s); }, PatKind::Ref(pat, mu) => { self.hash_pat(pat); - std::mem::discriminant(&mu).hash(&mut self.s); + std::mem::discriminant(mu).hash(&mut self.s); }, PatKind::Guard(pat, guard) => { self.hash_pat(pat); self.hash_expr(guard); }, PatKind::Slice(l, m, r) => { - for pat in l { + for pat in *l { self.hash_pat(pat); } if let Some(pat) = m { self.hash_pat(pat); } - for pat in r { + for pat in *r { self.hash_pat(pat); } }, - PatKind::Struct(ref qpath, fields, e) => { + PatKind::Struct(qpath, fields, e) => { self.hash_qpath(qpath); - for f in fields { + for f in *fields { self.hash_name(f.ident.name); self.hash_pat(f.pat); } e.hash(&mut self.s); }, PatKind::Tuple(pats, e) => { - for pat in pats { + for pat in *pats { self.hash_pat(pat); } e.hash(&mut self.s); }, - PatKind::TupleStruct(ref qpath, pats, e) => { + PatKind::TupleStruct(qpath, pats, e) => { self.hash_qpath(qpath); - for pat in pats { + for pat in *pats { self.hash_pat(pat); } e.hash(&mut self.s); @@ -1261,7 +1261,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::Slice(ty) => { self.hash_ty(ty); }, - &TyKind::Array(ty, len) => { + TyKind::Array(ty, len) => { self.hash_ty(ty); self.hash_const_arg(len); }, @@ -1334,11 +1334,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) { for arg in arg_list { - match *arg { + match arg { GenericArg::Lifetime(l) => self.hash_lifetime(l), GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()), GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()), - GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()), + GenericArg::Infer(inf) => self.hash_ty(&inf.to_ty()), } } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index f715fc86e4e5d..264b9b0406d0b 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,10 +1,8 @@ #![feature(array_chunks)] #![feature(box_patterns)] -#![feature(f128)] -#![feature(f16)] #![feature(if_let_guard)] -#![feature(macro_metavar_expr)] #![feature(macro_metavar_expr_concat)] +#![feature(macro_metavar_expr)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_private)] @@ -53,9 +51,6 @@ extern crate rustc_span; extern crate rustc_trait_selection; extern crate smallvec; -#[macro_use] -pub mod sym_helper; - pub mod ast_utils; pub mod attrs; mod check_proc_macro; @@ -108,10 +103,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, - ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, - Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, - TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, + CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, + GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, + Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, + Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -129,6 +124,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span}; +use source::walk_span_to_context; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; @@ -370,10 +366,10 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() + { + return cx.tcx.is_diagnostic_item(diag_item, adt.did()); } false } @@ -460,10 +456,10 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { QPath::Resolved(_, path) => match_path(path, segments), QPath::TypeRelative(ty, segment) => match ty.kind { TyKind::Path(ref inner_path) => { - if let [prefix @ .., end] = segments { - if match_qpath(inner_path, prefix) { - return segment.ident.name.as_str() == *end; - } + if let [prefix @ .., end] = segments + && match_qpath(inner_path, prefix) + { + return segment.ident.name.as_str() == *end; } false }, @@ -526,10 +522,10 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { - if let Res::Local(id) = path.res { - return Some(id); - } + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind + && let Res::Local(id) = path.res + { + return Some(id); } None } @@ -896,16 +892,14 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< sym::BinaryHeap, ]; - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return std_types_symbols.iter().any(|&symbol| { - cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() - }); - } - } - } + if let QPath::TypeRelative(_, method) = path + && method.ident.name == sym::new + && let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() + { + return std_types_symbols.iter().any(|&symbol| { + cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() + }); } false } @@ -1030,6 +1024,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), + ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, } } @@ -1206,12 +1201,10 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { .adjustments() .get(child_id) .map_or(&[][..], |x| &**x) - { - if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = + && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = *adjust.last().map_or(target, |a| a.target).kind() - { - return CaptureKind::Ref(mutability); - } + { + return CaptureKind::Ref(mutability); } match parent { @@ -1739,10 +1732,10 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool /// Checks whether the given expression is a constant literal of the given value. pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { // FIXME: use constant folding - if let ExprKind::Lit(spanned) = expr.kind { - if let LitKind::Int(v, _) = spanned.node { - return v == value; - } + if let ExprKind::Lit(spanned) = expr.kind + && let LitKind::Int(v, _) = spanned.node + { + return v == value; } false } @@ -1779,10 +1772,10 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { - if mac_name.as_str() == name { - return Some(new_span); - } + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind + && mac_name.as_str() == name + { + return Some(new_span); } span = new_span; @@ -1808,10 +1801,10 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { - if mac_name.as_str() == name { - return Some(new_span); - } + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind + && mac_name.as_str() == name + { + return Some(new_span); } } @@ -1832,15 +1825,15 @@ pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ExprKind::Call(fun, _) = expr.kind { - if let ExprKind::Path(ref qp) = fun.kind { - let res = cx.qpath_res(qp, fun.hir_id); - return match res { - Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), - _ => false, - }; - } + if let ExprKind::Call(fun, _) = expr.kind + && let ExprKind::Path(ref qp) = fun.kind + { + let res = cx.qpath_res(qp, fun.hir_id); + return match res { + Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), + _ => false, + }; } false } @@ -1914,10 +1907,10 @@ pub fn is_self(slf: &Param<'_>) -> bool { } pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { - if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind { - if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res { - return true; - } + if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind + && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res + { + return true; } false } @@ -2122,10 +2115,10 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, } // final `else {..}` - if !blocks.is_empty() { - if let ExprKind::Block(block, _) = expr.kind { - blocks.push(block); - } + if !blocks.is_empty() + && let ExprKind::Block(block, _) = expr.kind + { + blocks.push(block); } (conds, blocks) @@ -2140,26 +2133,34 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { } } -/// Peels away all the compiler generated code surrounding the body of an async function, -pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind { - if let ExprKind::Block( +/// Peels away all the compiler generated code surrounding the body of an async closure. +pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Closure(&Closure { + body, + kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)), + .. + }) = expr.kind + && let ExprKind::Block( Block { - stmts: [], expr: Some(Expr { - kind: ExprKind::DropTemps(expr), + kind: ExprKind::DropTemps(inner_expr), .. }), .. }, _, ) = tcx.hir_body(body).value.kind - { - return Some(expr); - } + { + Some(inner_expr) + } else { + None } - None +} + +/// Peels away all the compiler generated code surrounding the body of an async function, +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { + get_async_closure_expr(tcx, body.value) } // check if expr is calling method or function with #[must_use] attribute @@ -2631,17 +2632,19 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> } pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - if let Res::Def(_, def_id) = path.res { - return cx.tcx.has_attr(def_id, sym::cfg_trace) || cx.tcx.has_attr(def_id, sym::cfg_attr); - } + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && let Res::Def(_, def_id) = path.res + { + return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); } false } static TEST_ITEM_NAMES_CACHE: OnceLock>>> = OnceLock::new(); -fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +/// Apply `f()` to the set of test item names. +/// The names are sorted using the default `Symbol` ordering. +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap>> = cache.lock().unwrap(); let value = map.entry(module); @@ -2653,18 +2656,16 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym if matches!(tcx.def_kind(id.owner_id), DefKind::Const) && let item = tcx.hir_item(id) && let ItemKind::Const(ident, ty, _generics, _body) = item.kind - { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind // We could also check for the type name `test::TestDescAndFn` - if let Res::Def(DefKind::Struct, _) = path.res { - let has_test_marker = tcx - .hir_attrs(item.hir_id()) - .iter() - .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker { - names.push(ident.name); - } - } + && let Res::Def(DefKind::Struct, _) = path.res + { + let has_test_marker = tcx + .hir_attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker { + names.push(ident.name); } } } @@ -2685,18 +2686,37 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { // Since you can nest functions we need to collect all until we leave // function scope .any(|(_id, node)| { - if let Node::Item(item) = node { - if let ItemKind::Fn { ident, .. } = item.kind { - // Note that we have sorted the item names in the visitor, - // so the binary_search gets the same as `contains`, but faster. - return names.binary_search(&ident.name).is_ok(); - } + if let Node::Item(item) = node + && let ItemKind::Fn { ident, .. } = item.kind + { + // Note that we have sorted the item names in the visitor, + // so the binary_search gets the same as `contains`, but faster. + return names.binary_search(&ident.name).is_ok(); } false }) }) } +/// Checks if `fn_def_id` has a `#[test]` attribute applied +/// +/// This only checks directly applied attributes. To see if a node has a parent function marked with +/// `#[test]` use [`is_in_test_function`]. +/// +/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function +pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool { + let id = tcx.local_def_id_to_hir_id(fn_def_id); + if let Node::Item(item) = tcx.hir_node(id) + && let ItemKind::Fn { ident, .. } = item.kind + { + with_test_item_names(tcx, tcx.parent_module(id), |names| { + names.binary_search(&ident.name).is_ok() + }) + } else { + false + } +} + /// Checks if `id` has a `#[cfg(test)]` attribute applied /// /// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent @@ -3728,3 +3748,20 @@ pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir:: } hir_ty } + +/// If `expr` is a desugared `.await`, return the original expression if it does not come from a +/// macro expansion. +pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind + && let ExprKind::Call(_, [into_future_arg]) = match_value.kind + && let ctxt = expr.span.ctxt() + && for_each_expr_without_closures(into_future_arg, |e| { + walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(())) + }) + .is_none() + { + Some(into_future_arg) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index ffcfcd240ea5a..9ba644fdd20ec 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -76,7 +76,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { } if matches!( ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move | NonMutatingUseContext::Inspect) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) ) { self.results[i].local_consume_or_mutate_locs.push(loc); diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 86f4f190b950a..19061b574ff88 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,10 +1,10 @@ +use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; - use rustc_attr_parsing::{RustcVersion, parse_version}; use rustc_lint::LateContext; use rustc_session::Session; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use serde::Deserialize; use smallvec::SmallVec; use std::iter::once; @@ -24,10 +24,10 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } 1,85,0 { UINT_FLOAT_MIDPOINT } - 1,84,0 { CONST_OPTION_AS_SLICE } + 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } - 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } + 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } @@ -40,6 +40,7 @@ msrv_aliases! { 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,63,0 { CLONE_INTO } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } + 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } 1,57,0 { MAP_WHILE } @@ -63,6 +64,7 @@ msrv_aliases! { 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } 1,34,0 { TRY_FROM } 1,33,0 { UNDERSCORE_IMPORTS } + 1,32,0 { CONST_IS_POWER_OF_TWO } 1,31,0 { OPTION_REPLACE } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } @@ -182,8 +184,7 @@ impl MsrvStack { } fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option { - let sym_msrv = Symbol::intern("msrv"); - let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); + let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym::msrv])); if let Some(msrv_attr) = msrv_attrs.next() { if let Some(duplicate) = msrv_attrs.next_back() { diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 51d06ad9b1aa5..7f64ebd3b6437 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -30,9 +30,11 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"] pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; +pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"]; // Paths in clippy itself pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; +pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"]; // Paths in external crates #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 80066e9702d34..8645d5730fedb 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -142,7 +142,20 @@ pub trait SpanRangeExt: SpanRange { map_range(cx.sess().source_map(), self.into_range(), f) } - /// Extends the range to include all preceding whitespace characters. + #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] + /// Extends the range to include all preceding whitespace characters, unless there + /// are non-whitespace characters left on the same line after `self`. + /// + /// This extra condition prevents a problem when removing the '}' in: + /// ```ignore + /// ( // There was an opening bracket after the parenthesis, which has been removed + /// // This is a comment + /// }) + /// ``` + /// Removing the whitespaces, including the linefeed, before the '}', would put the + /// closing parenthesis at the end of the `// This is a comment` line, which would + /// make it part of the comment as well. In this case, it is best to keep the span + /// on the '}' alone. fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } @@ -263,10 +276,15 @@ fn map_range( } fn with_leading_whitespace(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp.clone(), |src, range| { - Some(src.get(..range.start)?.trim_end().len()..range.end) + map_range(sm, sp, |src, range| { + let non_blank_after = src.len() - src.get(range.end..)?.trim_start().len(); + if src.get(range.end..non_blank_after)?.contains(['\r', '\n']) { + Some(src.get(..range.start)?.trim_end().len()..range.end) + } else { + Some(range) + } }) - .unwrap_or(sp) + .unwrap() } fn trim_start(sm: &SourceMap, sp: Range) -> Range { @@ -384,10 +402,10 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) { - if snippet.is_empty() { - return false; - } + if let Some(snippet) = snippet_opt(sess, span) + && snippet.is_empty() + { + return false; } true } @@ -408,11 +426,11 @@ pub fn position_before_rarrow(s: &str) -> Option { let mut rpos = rpos; let chars: Vec = s.chars().collect(); while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } + if let Some(c) = chars.get(rpos - 1) + && c.is_whitespace() + { + rpos -= 1; + continue; } break; } diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs index 421b25a77fe8b..f0f82c8dddcf5 100644 --- a/src/tools/clippy/clippy_utils/src/str_utils.rs +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -1,4 +1,4 @@ -/// Dealing with sting indices can be hard, this struct ensures that both the +/// Dealing with string indices can be hard, this struct ensures that both the /// character and byte index are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] pub struct StrIndex { @@ -165,7 +165,7 @@ pub fn camel_case_split(s: &str) -> Vec<&str> { offsets.windows(2).map(|w| &s[w[0]..w[1]]).collect() } -/// Dealing with sting comparison can be complicated, this struct ensures that both the +/// Dealing with string comparison can be complicated, this struct ensures that both the /// character and byte count are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] pub struct StrCount { diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index e92c0c79b2556..93dec113d31a5 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -326,7 +326,7 @@ impl<'a> Sugg<'a> { /// `self` argument of a method call /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`). #[must_use] - pub fn maybe_par(self) -> Self { + pub fn maybe_paren(self) -> Self { match self { Sugg::NonParen(..) => self, // `(x)` and `(x).y()` both don't need additional parens. @@ -494,7 +494,7 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -946,10 +946,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // some items do not need explicit deref, such as array accesses, // so we mark them as already processed // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3` - if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() { - if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) { - projections_handled = true; - } + if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() + && matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) { + projections_handled = true; } }, } @@ -1008,12 +1007,12 @@ mod test { } #[test] - fn binop_maybe_par() { + fn binop_maybe_paren() { let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "1".into(), "1".into()); - assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); + assert_eq!("(1 + 1)", sugg.maybe_paren().to_string()); let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into()); - assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); + assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string()); } #[test] fn not_op() { diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 9cc72a5b3aaaa..1a30b473d1063 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -1,23 +1,69 @@ #![allow(non_upper_case_globals)] -use rustc_span::symbol::{Symbol, PREDEFINED_SYMBOLS_COUNT}; +use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol}; +#[doc(no_inline)] pub use rustc_span::sym::*; +macro_rules! val { + ($name:ident) => { + stringify!($name) + }; + ($name:ident $value:literal) => { + $value + }; +} + macro_rules! generate { - ($($sym:ident,)*) => { + ($($name:ident $(: $value:literal)? ,)*) => { /// To be supplied to `rustc_interface::Config` pub const EXTRA_SYMBOLS: &[&str] = &[ - $(stringify!($sym),)* + $( + val!($name $($value)?), + )* ]; $( - pub const $sym: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); + pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); )* }; } generate! { + as_bytes, + as_deref_mut, + as_deref, + as_mut, + Binary, + Cargo_toml: "Cargo.toml", + CLIPPY_ARGS, + CLIPPY_CONF_DIR, + cloned, + contains, + copied, + Current, + get, + insert, + int_roundings, + IntoIter, + is_empty, + is_ok, + is_some, + LowerExp, + LowerHex, + msrv, + Octal, + or_default, + regex, rustfmt_skip, + Start, + to_owned, unused_extern_crates, + unwrap_err, + unwrap_or_default, + UpperExp, + UpperHex, + V4, + V6, + Weak, } diff --git a/src/tools/clippy/clippy_utils/src/sym_helper.rs b/src/tools/clippy/clippy_utils/src/sym_helper.rs deleted file mode 100644 index f47dc80ebade8..0000000000000 --- a/src/tools/clippy/clippy_utils/src/sym_helper.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[macro_export] -/// Convenience wrapper around rustc's `Symbol::intern` -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index d33e59342a598..8db9cd593b335 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -19,9 +19,9 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, - GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -128,10 +128,10 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. ty::ClauseKind::Projection(projection_predicate) => { - if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { - if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { - return true; - } + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() + && contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) + { + return true; } }, _ => (), @@ -337,20 +337,20 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { - if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { - if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { - return true; - } + if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) + { + return true; } } false }, ty::Dynamic(binder, _, _) => { for predicate in *binder { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { - return true; - } + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() + && cx.tcx.has_attr(trait_ref.def_id, sym::must_use) + { + return true; } } false @@ -1352,7 +1352,7 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n } } -/// Get's the type of a field by name. +/// Gets the type of a field by name. pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option> { match *ty.kind() { ty::Adt(def, args) if def.is_union() || def.is_struct() => def @@ -1376,3 +1376,49 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } } + +/// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if +/// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` +/// types, or `PhantomData` types containing any of the previous. This can be used to check whether +/// skipping iterating over an iterator will change its behavior. +pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool { + fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty) + } + + /// Check if `ty` contains mutable references or equivalent, which includes: + /// - A mutable reference/pointer. + /// - A reference/pointer to a non-`Freeze` type. + /// - A `PhantomData` type containing any of the previous. + fn has_non_owning_mutable_access_inner<'tcx>( + cx: &LateContext<'tcx>, + phantoms: &mut FxHashSet>, + ty: Ty<'tcx>, + ) -> bool { + match ty.kind() { + ty::Adt(adt_def, args) if adt_def.is_phantom_data() => { + phantoms.insert(ty) + && args + .types() + .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)) + }, + ty::Adt(adt_def, args) => adt_def.all_fields().any(|field| { + has_non_owning_mutable_access_inner(cx, phantoms, normalize_ty(cx, field.ty(cx.tcx, args))) + }), + ty::Array(elem_ty, _) | ty::Slice(elem_ty) => has_non_owning_mutable_access_inner(cx, phantoms, *elem_ty), + ty::RawPtr(pointee_ty, mutability) | ty::Ref(_, pointee_ty, mutability) => { + mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env()) + }, + ty::Closure(_, closure_args) => { + matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures)) + }, + ty::Tuple(tuple_args) => tuple_args + .iter() + .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)), + _ => false, + } + } + + let mut phantoms = FxHashSet::default(); + has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty) +} diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index a079fd940c009..1b049b6d12c4c 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -126,10 +126,10 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result { - if let Res::Local(id) = path.res { - if self.binding_ids.contains(&id) { - return ControlFlow::Break(()); - } + if let Res::Local(id) = path.res + && self.binding_ids.contains(&id) + { + return ControlFlow::Break(()); } ControlFlow::Continue(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 63dd00f2de0fb..fc6e30a980476 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -297,10 +297,10 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { - if let ExprKind::Path(p) = &e.kind { - if cx.qpath_res(p, e.hir_id) == res { - return ControlFlow::Break(()); - } + if let ExprKind::Path(p) = &e.kind + && cx.qpath_res(p, e.hir_id) == res + { + return ControlFlow::Break(()); } ControlFlow::Continue(()) }) diff --git a/src/tools/clippy/lintcheck/ci-config/clippy.toml b/src/tools/clippy/lintcheck/ci-config/clippy.toml new file mode 100644 index 0000000000000..9853465c83f00 --- /dev/null +++ b/src/tools/clippy/lintcheck/ci-config/clippy.toml @@ -0,0 +1,7 @@ +# Configuration applied when running lintcheck from the CI +# +# The CI will set the `CLIPPY_CONF_DIR` environment variable +# to `$PWD/lintcheck/ci-config`. + +avoid-breaking-exported-api = false +lint-commented-code = false diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 8d0d41ab9450f..fe488ef89da1f 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -120,14 +120,17 @@ impl Crate { if config.perf { cmd = Command::new("perf"); + let perf_data_filename = get_perf_data_filename(&self.path); cmd.args(&[ "record", "-e", "instructions", // Only count instructions "-g", // Enable call-graph, useful for flamegraphs and produces richer reports "--quiet", // Do not tamper with lintcheck's normal output + "--compression-level=22", + "--freq=3000", // Slow down program to capture all events "-o", - "perf.data", + &perf_data_filename, "--", "cargo", ]); @@ -165,7 +168,7 @@ impl Crate { return Vec::new(); } - if !config.fix { + if !config.fix && !config.perf { cmd.arg("--message-format=json"); } @@ -203,6 +206,11 @@ impl Crate { return Vec::new(); } + // We don't want to keep target directories if benchmarking + if config.perf { + let _ = fs::remove_dir_all(&shared_target_dir); + } + // get all clippy warnings and ICEs let mut entries: Vec = Message::parse_stream(stdout.as_bytes()) .filter_map(|msg| match msg { @@ -441,6 +449,35 @@ fn lintcheck(config: LintcheckConfig) { fs::write(&config.lintcheck_results_path, text).unwrap(); } +/// Traverse a directory looking for `perf.data.` files, and adds one +/// to the most recent of those files, returning the new most recent `perf.data` +/// file name. +fn get_perf_data_filename(source_path: &Path) -> String { + if source_path.join("perf.data").exists() { + let mut max_number = 0; + fs::read_dir(source_path) + .unwrap() + .filter_map(Result::ok) + .filter(|path| { + path.file_name() + .as_os_str() + .to_string_lossy() // We don't care about data loss, as we're checking for equality + .starts_with("perf.data") + }) + .for_each(|path| { + let file_name = path.file_name(); + let file_name = file_name.as_os_str().to_str().unwrap().split('.').next_back().unwrap(); + if let Ok(parsed_file_name) = file_name.parse::() + && parsed_file_name >= max_number + { + max_number = parsed_file_name + 1; + } + }); + return format!("perf.data.{max_number}"); + } + String::from("perf.data") +} + /// Returns the path to the Clippy project directory #[must_use] fn clippy_project_root() -> &'static Path { diff --git a/src/tools/clippy/lintcheck/src/recursive.rs b/src/tools/clippy/lintcheck/src/recursive.rs index 57073f523648e..6406b2dcb643b 100644 --- a/src/tools/clippy/lintcheck/src/recursive.rs +++ b/src/tools/clippy/lintcheck/src/recursive.rs @@ -64,7 +64,7 @@ fn process_stream( // It's 99% likely that dependencies compiled with recursive mode are on crates.io // and therefore on docs.rs. This links to the sources directly, do avoid invalid - // links due to remaped paths. See rust-lang/docs.rs#2551 for more details. + // links due to remapped paths. See rust-lang/docs.rs#2551 for more details. let base_url = format!( "https://docs.rs/crate/{}/{}/source/src/{{file}}#{{line}}", driver_info.package_name, driver_info.version diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain.toml similarity index 85% rename from src/tools/clippy/rust-toolchain rename to src/tools/clippy/rust-toolchain.toml index fcaeedc9a66b5..d2f79da1a541b 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-03-20" +channel = "nightly-2025-04-22" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs index 423154a69fa0b..b45edf2345585 100644 --- a/src/tools/clippy/rustc_tools_util/src/lib.rs +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -121,7 +121,7 @@ fn get_output(cmd: &str, args: &[&str]) -> Option { pub fn rerun_if_git_changes() -> Option<()> { // Make sure we get rerun when the git commit changes. // We want to watch two files: HEAD, which tracks which branch we are on, - // and the file for that branch that tracks which commit is is on. + // and the file for that branch that tracks which commit is checked out. // First, find the `HEAD` file. This should work even with worktrees. let git_head_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", "HEAD"])?); diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index df9c4e8e6ae9a..87ca9c5beddfb 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -14,6 +14,7 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::sym; use rustc_interface::interface; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; @@ -78,7 +79,7 @@ fn track_clippy_args(psess: &mut ParseSess, args_env_var: Option<&str>) { psess .env_depinfo .get_mut() - .insert((Symbol::intern("CLIPPY_ARGS"), args_env_var.map(Symbol::intern))); + .insert((sym::CLIPPY_ARGS, args_env_var.map(Symbol::intern))); } /// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy @@ -89,7 +90,7 @@ fn track_files(psess: &mut ParseSess) { // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` // with the current directory set to `CARGO_MANIFEST_DIR` so a relative path is fine if Path::new("Cargo.toml").exists() { - file_depinfo.insert(Symbol::intern("Cargo.toml")); + file_depinfo.insert(sym::Cargo_toml); } // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()` @@ -145,7 +146,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { // Trigger a rebuild if CLIPPY_CONF_DIR changes. The value must be a valid string so // changes between dirs that are invalid UTF-8 will not trigger rebuilds psess.env_depinfo.get_mut().insert(( - Symbol::intern("CLIPPY_CONF_DIR"), + sym::CLIPPY_CONF_DIR, env::var("CLIPPY_CONF_DIR").ok().map(|dir| Symbol::intern(&dir)), )); })); @@ -158,9 +159,10 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_config::Conf::read(sess, &conf_path); clippy_lints::register_lints(lint_store, conf); - clippy_lints::register_pre_expansion_lints(lint_store, conf); + #[cfg(feature = "internal")] + clippy_lints_internal::register_lints(lint_store); })); - config.extra_symbols = clippy_utils::sym::EXTRA_SYMBOLS.into(); + config.extra_symbols = sym::EXTRA_SYMBOLS.into(); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be // run on the unoptimized MIR. On the other hand this results in some false negatives. If @@ -208,12 +210,12 @@ pub fn main() { // Beside checking for existence of `--sysroot` on the command line, we need to // check for the arg files that are prefixed with @ as well to be consistent with rustc for arg in args.iter() { - if let Some(arg_file_path) = arg.strip_prefix('@') { - if let Ok(arg_file) = read_to_string(arg_file_path) { - let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); - if has_arg(&split_arg_file, "--sysroot") { - return true; - } + if let Some(arg_file_path) = arg.strip_prefix('@') + && let Ok(arg_file) = read_to_string(arg_file_path) + { + let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); + if has_arg(&split_arg_file, "--sysroot") { + return true; } } } @@ -222,10 +224,10 @@ pub fn main() { let sys_root_env = std::env::var("SYSROOT").ok(); let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { - if let Some(sys_root) = sys_root_env { - if !has_sysroot_arg(args) { - args.extend(vec!["--sysroot".into(), sys_root]); - } + if let Some(sys_root) = sys_root_env + && !has_sysroot_arg(args) + { + args.extend(vec!["--sysroot".into(), sys_root]); } }; diff --git a/src/tools/clippy/tests/clippy.toml b/src/tools/clippy/tests/clippy.toml index 5eb7ac0354198..91a2e55180b97 100644 --- a/src/tools/clippy/tests/clippy.toml +++ b/src/tools/clippy/tests/clippy.toml @@ -1 +1,2 @@ # default config for tests, overrides clippy.toml at the project root +lint-commented-code = false diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 956a05288f358..6d391bd622a8d 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -2,6 +2,8 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] +use askama::Template; +use askama::filters::Safe; use cargo_metadata::Message; use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use clippy_config::ClippyConfiguration; @@ -9,11 +11,10 @@ use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; use pulldown_cmark::{Options, Parser, html}; -use rinja::Template; -use rinja::filters::Safe; use serde::Deserialize; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; +use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; @@ -86,13 +87,13 @@ fn extern_flags() -> Vec { let name = name.strip_prefix("lib").unwrap_or(name); Some((name, path_str)) }; - if let Some((name, path)) = parse_name_path() { - if TEST_DEPENDENCIES.contains(&name) { - // A dependency may be listed twice if it is available in sysroot, - // and the sysroot dependencies are listed first. As of the writing, - // this only seems to apply to if_chain. - crates.insert(name, path); - } + if let Some((name, path)) = parse_name_path() + && TEST_DEPENDENCIES.contains(&name) + { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); } } let not_found: Vec<&str> = TEST_DEPENDENCIES @@ -147,11 +148,16 @@ impl TestContext { .map(|filters| filters.split(',').map(str::to_string).collect()) .unwrap_or_default(), target: None, - bless_command: Some("cargo uibless".into()), + bless_command: Some(if IS_RUSTC_TEST_SUITE { + "./x test src/tools/clippy --bless".into() + } else { + "cargo uibless".into() + }), out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; let defaults = config.comment_defaults.base(); + defaults.set_custom("edition", Edition("2024".into())); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 858be389a9e6e..16a1a415102c4 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -36,6 +36,7 @@ fn dogfood() { for package in [ "./", "clippy_dev", + "clippy_lints_internal", "clippy_lints", "clippy_utils", "clippy_config", @@ -80,11 +81,9 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { command.arg("--").args(args); command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + command.args(["-D", "clippy::dbg_macro"]); - if cfg!(feature = "internal") { - // internal lints only exist if we build with the internal feature - command.args(["-D", "clippy::internal"]); - } else { + if !cfg!(feature = "internal") { // running a clippy built without internal lints on the clippy source // that contains e.g. `allow(clippy::invalid_paths)` command.args(["-A", "unknown_lints"]); diff --git a/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs b/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs index 52fcaec4df32e..f730f564a09cf 100644 --- a/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs +++ b/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs @@ -1,2 +1,4 @@ +#![allow(clippy::unnecessary_def_path)] + pub static OPTION: [&str; 3] = ["core", "option", "Option"]; pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs index e5f6001b74d09..897002949e67e 100644 --- a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs +++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs @@ -1,5 +1,5 @@ -#![deny(clippy::internal)] #![feature(rustc_private)] +#![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] #[macro_use] extern crate rustc_middle; @@ -86,6 +86,15 @@ mod internal_clippy_lints { } use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE; -declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]); +declare_lint_pass!(Pass2 => [ + VALID_ONE, + VALID_TWO, + VALID_THREE, + INVALID_ONE, + INVALID_TWO, + MISSING_ATTRIBUTE_ONE, + MISSING_ATTRIBUTE_TWO, + ALLOW_MISSING_ATTRIBUTE_ONE, +]); fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr index 1129c35d1d01b..952bc94403033 100644 --- a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr @@ -12,11 +12,10 @@ LL | | } | = help: please use a valid semantic version, see `doc/adding_lints.md` note: the lint level is defined here - --> tests/ui-internal/check_clippy_version_attribute.rs:1:9 + --> tests/ui-internal/check_clippy_version_attribute.rs:2:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute @@ -47,7 +46,11 @@ LL | | } | |_^ | = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` - = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` +note: the lint level is defined here + --> tests/ui-internal/check_clippy_version_attribute.rs:2:51 + | +LL | #![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.rs b/src/tools/clippy/tests/ui-internal/check_formulation.rs index 8265a78769d16..bcbb0d783198e 100644 --- a/src/tools/clippy/tests/ui-internal/check_formulation.rs +++ b/src/tools/clippy/tests/ui-internal/check_formulation.rs @@ -1,4 +1,5 @@ -#![warn(clippy::almost_standard_lint_formulation)] +#![deny(clippy::almost_standard_lint_formulation)] +#![allow(clippy::lint_without_lint_pass)] #![feature(rustc_private)] #[macro_use] diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.stderr b/src/tools/clippy/tests/ui-internal/check_formulation.stderr index b16e1bf868737..9aeb9e1f2d49c 100644 --- a/src/tools/clippy/tests/ui-internal/check_formulation.stderr +++ b/src/tools/clippy/tests/ui-internal/check_formulation.stderr @@ -1,15 +1,18 @@ error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:23:5 + --> tests/ui-internal/check_formulation.rs:24:5 | LL | /// Check for lint formulations that are correct | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using `Checks for` - = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` +note: the lint level is defined here + --> tests/ui-internal/check_formulation.rs:1:9 + | +LL | #![deny(clippy::almost_standard_lint_formulation)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:34:5 + --> tests/ui-internal/check_formulation.rs:35:5 | LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed index 918e33345a779..76f68686ee2a8 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::collapsible_span_lint_calls)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs index 2f289ae2b4819..214c8783a6690 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::collapsible_span_lint_calls)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr index a2be1f1cd367d..9c83538947cab 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -10,9 +10,8 @@ LL | | }); note: the lint level is defined here --> tests/ui-internal/collapsible_span_lint_calls.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::collapsible_span_lint_calls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call is collapsible --> tests/ui-internal/collapsible_span_lint_calls.rs:39:9 diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs index 71819fe370701..c7e92b1bf164f 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs @@ -6,7 +6,7 @@ //@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc running on " //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" -#![deny(clippy::internal)] +#![deny(clippy::produce_ice)] #![allow(clippy::missing_clippy_version_attribute)] fn it_looks_like_you_are_trying_to_kill_clippy() {} diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index 589e1190a907e..884d3d035a29d 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -8,7 +8,7 @@ error: internal compiler error: Would you like some help with that? LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: delayed at clippy_lints/src/utils/internal_lints/produce_ice.rs - disabled backtrace +note: delayed at clippy_lints_internal/src/produce_ice.rs - disabled backtrace --> tests/ui-internal/custom_ice_message.rs:12:1 | LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} diff --git a/src/tools/clippy/tests/ui-internal/default_lint.rs b/src/tools/clippy/tests/ui-internal/default_lint.rs index 959bfd27e3899..809f2c4d080dc 100644 --- a/src/tools/clippy/tests/ui-internal/default_lint.rs +++ b/src/tools/clippy/tests/ui-internal/default_lint.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::default_lint)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/default_lint.stderr b/src/tools/clippy/tests/ui-internal/default_lint.stderr index 9d4c2e15349f6..2c700ec82dcd4 100644 --- a/src/tools/clippy/tests/ui-internal/default_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/default_lint.stderr @@ -13,9 +13,8 @@ LL | | } note: the lint level is defined here --> tests/ui-internal/default_lint.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::default_lint)] + | ^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs index 3fed38cab64d4..36e4158f6e688 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![deny(clippy::disallowed_methods)] extern crate rustc_errors; extern crate rustc_hir; diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index 9a7a7ecbbff92..f03a745963e00 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -1,15 +1,18 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> tests/ui-internal/disallow_span_lint.rs:14:8 + --> tests/ui-internal/disallow_span_lint.rs:15:8 | LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` +note: the lint level is defined here + --> tests/ui-internal/disallow_span_lint.rs:2:9 + | +LL | #![deny(clippy::disallowed_methods)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:21:9 + --> tests/ui-internal/disallow_span_lint.rs:22:9 | LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed deleted file mode 100644 index 92d3b1537e0c8..0000000000000 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed +++ /dev/null @@ -1,40 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] -#![feature(rustc_private)] - -extern crate rustc_span; - -use rustc_span::symbol::Symbol; - -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} - -fn main() { - // Direct use of Symbol::intern - let _ = rustc_span::sym::f32; - //~^ interning_defined_symbol - - // Using a sym macro - let _ = rustc_span::sym::f32; - //~^ interning_defined_symbol - - // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::sym::proc_dash_macro; - //~^ interning_defined_symbol - - // interning a keyword - let _ = rustc_span::kw::SelfLower; - //~^ interning_defined_symbol - - // Interning a symbol that is not defined - let _ = Symbol::intern("xyz123"); - let _ = sym!(xyz123); - - // Using a different `intern` function - let _ = intern("f32"); -} - -fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs deleted file mode 100644 index d1e6f9cb1c416..0000000000000 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs +++ /dev/null @@ -1,40 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] -#![feature(rustc_private)] - -extern crate rustc_span; - -use rustc_span::symbol::Symbol; - -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} - -fn main() { - // Direct use of Symbol::intern - let _ = Symbol::intern("f32"); - //~^ interning_defined_symbol - - // Using a sym macro - let _ = sym!(f32); - //~^ interning_defined_symbol - - // Correct suggestion when symbol isn't stringified constant name - let _ = Symbol::intern("proc-macro"); - //~^ interning_defined_symbol - - // interning a keyword - let _ = Symbol::intern("self"); - //~^ interning_defined_symbol - - // Interning a symbol that is not defined - let _ = Symbol::intern("xyz123"); - let _ = sym!(xyz123); - - // Using a different `intern` function - let _ = intern("f32"); -} - -fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr deleted file mode 100644 index c84a566436a8e..0000000000000 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:17:13 - | -LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` - | -note: the lint level is defined here - --> tests/ui-internal/interning_defined_symbol.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:21:13 - | -LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::sym::f32` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:25:13 - | -LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:29:13 - | -LL | let _ = Symbol::intern("self"); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::kw::SelfLower` - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.fixed b/src/tools/clippy/tests/ui-internal/interning_literals.fixed new file mode 100644 index 0000000000000..03e97768b996f --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals.fixed @@ -0,0 +1,31 @@ +#![allow(clippy::let_unit_value)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn main() { + let _ = sym::f32; + //~^ interning_literals + + // Correct suggestion when symbol isn't stringified constant name + let _ = sym::proc_dash_macro; + //~^ interning_literals + + // Interning a keyword + let _ = kw::SelfLower; + //~^ interning_literals + + // Defined in clippy_utils + let _ = sym::msrv; + //~^ interning_literals + let _ = sym::Cargo_toml; + //~^ interning_literals + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.rs b/src/tools/clippy/tests/ui-internal/interning_literals.rs new file mode 100644 index 0000000000000..561fd5702a59f --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals.rs @@ -0,0 +1,31 @@ +#![allow(clippy::let_unit_value)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn main() { + let _ = Symbol::intern("f32"); + //~^ interning_literals + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + //~^ interning_literals + + // Interning a keyword + let _ = Symbol::intern("self"); + //~^ interning_literals + + // Defined in clippy_utils + let _ = Symbol::intern("msrv"); + //~^ interning_literals + let _ = Symbol::intern("Cargo.toml"); + //~^ interning_literals + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.stderr b/src/tools/clippy/tests/ui-internal/interning_literals.stderr new file mode 100644 index 0000000000000..628b97eff84da --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals.stderr @@ -0,0 +1,64 @@ +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:10:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::interning-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` +help: use the preinterned symbol + | +LL - let _ = Symbol::intern("f32"); +LL + let _ = sym::f32; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:14:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - let _ = Symbol::intern("proc-macro"); +LL + let _ = sym::proc_dash_macro; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:18:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - let _ = Symbol::intern("self"); +LL + let _ = kw::SelfLower; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:22:13 + | +LL | let _ = Symbol::intern("msrv"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - let _ = Symbol::intern("msrv"); +LL + let _ = sym::msrv; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:24:13 + | +LL | let _ = Symbol::intern("Cargo.toml"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the preinterned symbol + | +LL - let _ = Symbol::intern("Cargo.toml"); +LL + let _ = sym::Cargo_toml; + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.rs b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.rs new file mode 100644 index 0000000000000..43872e95a5854 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.rs @@ -0,0 +1,16 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn main() { + // Not yet defined + let _ = Symbol::intern("xyz123"); + //~^ interning_literals + let _ = Symbol::intern("with-dash"); + //~^ interning_literals + let _ = Symbol::intern("with.dot"); + //~^ interning_literals +} diff --git a/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr new file mode 100644 index 0000000000000..8294453a8f945 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr @@ -0,0 +1,40 @@ +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:10:13 + | +LL | let _ = Symbol::intern("xyz123"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::interning-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - let _ = Symbol::intern("xyz123"); +LL + let _ = sym::xyz123; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:12:13 + | +LL | let _ = Symbol::intern("with-dash"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - let _ = Symbol::intern("with-dash"); +LL + let _ = sym::with_dash; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:14:13 + | +LL | let _ = Symbol::intern("with.dot"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add the symbol to `clippy_utils/src/sym.rs` and use it + | +LL - let _ = Symbol::intern("with.dot"); +LL + let _ = sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed index 6804e2bbae83c..238ef9ae6d0ac 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::missing_msrv_attr_impl)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs index c625a5d9a4590..7753dcaad7139 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::missing_msrv_attr_impl)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr index 0a7636313eff2..d5928d8c0c2de 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr +++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr @@ -7,9 +7,8 @@ LL | impl EarlyLintPass for Pass { note: the lint level is defined here --> tests/ui-internal/invalid_msrv_attr_impl.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::missing_msrv_attr_impl)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::missing_msrv_attr_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `extract_msrv_attr!()` to the `EarlyLintPass` implementation | LL ~ impl EarlyLintPass for Pass { diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.rs b/src/tools/clippy/tests/ui-internal/invalid_paths.rs index abfb111f938e4..7317abc2185a3 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.rs +++ b/src/tools/clippy/tests/ui-internal/invalid_paths.rs @@ -1,4 +1,4 @@ -#![warn(clippy::internal)] +#![deny(clippy::invalid_paths)] #![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] mod paths { diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr index 7bde37667be42..7b7b25ce8d8db 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr +++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr @@ -4,8 +4,11 @@ error: invalid path LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::invalid-paths` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` +note: the lint level is defined here + --> tests/ui-internal/invalid_paths.rs:1:9 + | +LL | #![deny(clippy::invalid_paths)] + | ^^^^^^^^^^^^^^^^^^^^^ error: invalid path --> tests/ui-internal/invalid_paths.rs:19:5 diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs index 69591523432c8..6b649132aca31 100644 --- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs +++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::lint_without_lint_pass)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr index 9cca96ca16020..3798293f4c111 100644 --- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr +++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr @@ -13,9 +13,8 @@ LL | | } note: the lint level is defined here --> tests/ui-internal/lint_without_lint_pass.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::lint_without_lint_pass)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed index cb7680b8bb142..900ca5b2ab9d8 100644 --- a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed +++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::outer_expn_expn_data)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs index 41d735110b5a0..bcfc42aa2ac75 100644 --- a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs +++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::outer_expn_expn_data)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr index 33ac91e4fb0de..b86138a5d45d2 100644 --- a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr +++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr @@ -7,9 +7,8 @@ LL | let _ = expr.span.ctxt().outer_expn().expn_data(); note: the lint level is defined here --> tests/ui-internal/outer_expn_data.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::outer_expn_expn_data)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed deleted file mode 100644 index 2cbd646a0fd5d..0000000000000 --- a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed +++ /dev/null @@ -1,24 +0,0 @@ -#![feature(rustc_private)] -#![warn(clippy::slow_symbol_comparisons)] - -extern crate rustc_span; - -use clippy_utils::sym; -use rustc_span::Symbol; - -fn main() { - let symbol = sym!(example); - let other_symbol = sym!(other_example); - - // Should lint - let slow_comparison = symbol.as_str() == "example"; - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_macro = symbol.as_str() == "example"; - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_backwards = symbol.as_str() == "example"; - //~^ error: comparing `Symbol` via `Symbol::intern` - - // Should not lint - let faster_comparison = symbol.as_str() == "other_example"; - let preinterned_comparison = symbol == other_symbol; -} diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs deleted file mode 100644 index 0cea3c3fcff9f..0000000000000 --- a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![feature(rustc_private)] -#![warn(clippy::slow_symbol_comparisons)] - -extern crate rustc_span; - -use clippy_utils::sym; -use rustc_span::Symbol; - -fn main() { - let symbol = sym!(example); - let other_symbol = sym!(other_example); - - // Should lint - let slow_comparison = symbol == Symbol::intern("example"); - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_macro = symbol == sym!(example); - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_backwards = sym!(example) == symbol; - //~^ error: comparing `Symbol` via `Symbol::intern` - - // Should not lint - let faster_comparison = symbol.as_str() == "other_example"; - let preinterned_comparison = symbol == other_symbol; -} diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr deleted file mode 100644 index 72cb20a7fed90..0000000000000 --- a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: comparing `Symbol` via `Symbol::intern` - --> tests/ui-internal/slow_symbol_comparisons.rs:14:27 - | -LL | let slow_comparison = symbol == Symbol::intern("example"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` - | - = note: `-D clippy::slow-symbol-comparisons` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::slow_symbol_comparisons)]` - -error: comparing `Symbol` via `Symbol::intern` - --> tests/ui-internal/slow_symbol_comparisons.rs:16:33 - | -LL | let slow_comparison_macro = symbol == sym!(example); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` - -error: comparing `Symbol` via `Symbol::intern` - --> tests/ui-internal/slow_symbol_comparisons.rs:18:37 - | -LL | let slow_comparison_backwards = sym!(example) == symbol; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed index 577fad9341b64..89902ebe4e54e 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed @@ -1,5 +1,5 @@ //@aux-build:paths.rs -#![deny(clippy::internal)] +#![deny(clippy::unnecessary_def_path)] #![feature(rustc_private)] #![allow(clippy::unnecessary_map_or)] diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs index d4deb3626d0b6..cfca15267c195 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs @@ -1,5 +1,5 @@ //@aux-build:paths.rs -#![deny(clippy::internal)] +#![deny(clippy::unnecessary_def_path)] #![feature(rustc_private)] #![allow(clippy::unnecessary_map_or)] diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr index 0053ba321bbe7..d7fb4ea551e1d 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr @@ -7,9 +7,8 @@ LL | let _ = match_type(cx, ty, &OPTION); note: the lint level is defined here --> tests/ui-internal/unnecessary_def_path.rs:2:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::unnecessary_def_path)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a def path to a diagnostic item --> tests/ui-internal/unnecessary_def_path.rs:39:13 diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs index 4801d76bd2685..bd7a55114acbc 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -1,6 +1,6 @@ #![feature(rustc_private)] #![allow(unused)] -#![warn(clippy::unnecessary_def_path)] +#![deny(clippy::unnecessary_def_path)] extern crate rustc_hir; diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index b938395193234..88fdf6f1c1888 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -5,8 +5,11 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: convert all references to use `sym::Deref` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` +note: the lint level is defined here + --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:3:9 + | +LL | #![deny(clippy::unnecessary_def_path)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: hardcoded path to a language item --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40 diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed deleted file mode 100644 index dc564daef8293..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::internal)] -#![allow( - clippy::slow_symbol_comparisons, - clippy::borrow_deref_ref, - clippy::unnecessary_operation, - unused_must_use, - clippy::missing_clippy_version_attribute -)] - -extern crate rustc_span; - -use rustc_span::symbol::{Ident, Symbol}; - -fn main() { - Symbol::intern("foo") == rustc_span::sym::clippy; - //~^ unnecessary_symbol_str - Symbol::intern("foo") == rustc_span::kw::SelfLower; - //~^ unnecessary_symbol_str - Symbol::intern("foo") != rustc_span::kw::SelfUpper; - //~^ unnecessary_symbol_str - Ident::empty().name == rustc_span::sym::clippy; - //~^ unnecessary_symbol_str - rustc_span::sym::clippy == Ident::empty().name; - //~^ unnecessary_symbol_str -} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs deleted file mode 100644 index d74262d1294b7..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::internal)] -#![allow( - clippy::slow_symbol_comparisons, - clippy::borrow_deref_ref, - clippy::unnecessary_operation, - unused_must_use, - clippy::missing_clippy_version_attribute -)] - -extern crate rustc_span; - -use rustc_span::symbol::{Ident, Symbol}; - -fn main() { - Symbol::intern("foo").as_str() == "clippy"; - //~^ unnecessary_symbol_str - Symbol::intern("foo").to_string() == "self"; - //~^ unnecessary_symbol_str - Symbol::intern("foo").to_ident_string() != "Self"; - //~^ unnecessary_symbol_str - &*Ident::empty().as_str() == "clippy"; - //~^ unnecessary_symbol_str - "clippy" == Ident::empty().to_string(); - //~^ unnecessary_symbol_str -} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr deleted file mode 100644 index 517a395e93f2c..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:16:5 - | -LL | Symbol::intern("foo").as_str() == "clippy"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` - | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_symbol_str.rs:2:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 - | -LL | Symbol::intern("foo").to_string() == "self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::kw::SelfLower` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 - | -LL | Symbol::intern("foo").to_ident_string() != "Self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::kw::SelfUpper` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:22:5 - | -LL | &*Ident::empty().as_str() == "clippy"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:24:5 - | -LL | "clippy" == Ident::empty().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` - -error: aborting due to 5 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs index b43791521cb5a..694ef45c75b08 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs @@ -16,6 +16,7 @@ //@[bad_conf_4] error-in-other-file: //@[bad_conf_5] error-in-other-file: //@[bad_conf_6] error-in-other-file: +//@compile-flags: --test #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr index 7fc216b30d508..fcd7864c6677d 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr @@ -1,16 +1,16 @@ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:36:5 | LL | a: bool, | ^ | note: should be placed before `b` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 | LL | b: bool, | ^ note: the lint level is defined here - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:33:8 | LL | #[deny(clippy::arbitrary_source_item_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr index 1f75f5099ecc1..81c35ff778b7f 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr @@ -1,33 +1,33 @@ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:25:8 | LL | struct OrderedChecked { | ^^^^^^^^^^^^^^ | note: should be placed before `Unordered` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:19:8 | LL | struct Unordered { | ^^^^^^^^^ note: the lint level is defined here - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:10:9 | LL | #![deny(clippy::arbitrary_source_item_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:36:5 | LL | a: bool, | ^ | note: should be placed before `b` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 | LL | b: bool, | ^ note: the lint level is defined here - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:33:8 | LL | #[deny(clippy::arbitrary_source_item_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr index 8027f55add673..09ede57f295e8 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr @@ -1,16 +1,16 @@ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:25:8 | LL | struct OrderedChecked { | ^^^^^^^^^^^^^^ | note: should be placed before `Unordered` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:19:8 | LL | struct Unordered { | ^^^^^^^^^ note: the lint level is defined here - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:10:9 | LL | #![deny(clippy::arbitrary_source_item_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr index 333a601f6a952..7c515f050c127 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr @@ -1,48 +1,60 @@ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:24:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:25:8 | LL | struct OrderedChecked { | ^^^^^^^^^^^^^^ | note: should be placed before `Unordered` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:18:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:19:8 | LL | struct Unordered { | ^^^^^^^^^ note: the lint level is defined here - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:9:9 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:10:9 | LL | #![deny(clippy::arbitrary_source_item_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:45:4 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:46:4 | LL | fn before_main() {} | ^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:41:4 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:42:4 | LL | fn main() { | ^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:36:5 | LL | a: bool, | ^ | note: should be placed before `b` - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:34:5 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 | LL | b: bool, | ^ note: the lint level is defined here - --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:32:8 + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:33:8 | LL | #[deny(clippy::arbitrary_source_item_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:52:11 + | +LL | const A: i8 = 0; + | ^ + | +note: should be placed before `B` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:51:11 + | +LL | const B: i8 = 1; + | ^ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs index e32b921dd9659..cb6d0170b8f97 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs @@ -4,6 +4,7 @@ //@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within //@[ord_in_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_2 //@[ord_in_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_3 +//@compile-flags: --test #![allow(dead_code)] #![deny(clippy::arbitrary_source_item_ordering)] @@ -44,3 +45,10 @@ fn main() { fn before_main() {} //~[ord_within]^ arbitrary_source_item_ordering + +#[cfg(test)] +mod test { + const B: i8 = 1; + const A: i8 = 0; + //~[ord_within]^ arbitrary_source_item_ordering +} diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr index 86e30409af068..d0fce3614a145 100644 --- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr +++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr @@ -1,11 +1,8 @@ error: error reading Clippy's configuration file: replacement not allowed for this configuration - --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:1:31 + --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:2:5 | -LL | await-holding-invalid-types = [ - | _______________________________^ -LL | | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, -LL | | ] - | |_^ +LL | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/clippy.toml b/src/tools/clippy/tests/ui-toml/collapsible_if/clippy.toml new file mode 100644 index 0000000000000..592cea90cff5c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/clippy.toml @@ -0,0 +1 @@ +lint-commented-code = true diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed new file mode 100644 index 0000000000000..6f5cc47ba6c75 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed @@ -0,0 +1,34 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" + // Comment must be kept + && y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_if + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" // Inner comment + && y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_if + + if x == "hello" + /* Inner comment */ + && y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_if + + if x == "hello" /* Inner comment */ + && y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.rs b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.rs new file mode 100644 index 0000000000000..868b4adcde502 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.rs @@ -0,0 +1,38 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" { + // Comment must be kept + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_if + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" { // Inner comment + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_if + + if x == "hello" { + /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_if + + if x == "hello" { /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr new file mode 100644 index 0000000000000..357ce4ad32deb --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr @@ -0,0 +1,80 @@ +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:8:5 + | +LL | / if x == "hello" { +LL | | // Comment must be kept +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if x == "hello" +LL | // Comment must be kept +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:17:5 + | +LL | / if x == "hello" { // Inner comment +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" // Inner comment +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:24:5 + | +LL | / if x == "hello" { +LL | | /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" +LL | /* Inner comment */ +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:32:5 + | +LL | / if x == "hello" { /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" /* Inner comment */ +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed new file mode 100644 index 0000000000000..f12273954c6dd --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed @@ -0,0 +1,25 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) + // with comment + && let Some(b) = Some(4) { + let _ = a + b; + } + //~^^^^^^ collapsible_if + + if let Some(a) = Some(3) + // with comment + && a + 1 == 4 { + let _ = a; + } + //~^^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) + // with comment + && let Some(b) = Some(4) { + let _ = b; + } + //~^^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs new file mode 100644 index 0000000000000..5a984d7a3cbee --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs @@ -0,0 +1,28 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment + if let Some(b) = Some(4) { + let _ = a + b; + } + } + //~^^^^^^ collapsible_if + + if let Some(a) = Some(3) { + // with comment + if a + 1 == 4 { + let _ = a; + } + } + //~^^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) { + // with comment + if let Some(b) = Some(4) { + let _ = b; + } + } + //~^^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr new file mode 100644 index 0000000000000..c22a65a447301 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr @@ -0,0 +1,64 @@ +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:5:5 + | +LL | / if let Some(a) = Some(3) { +LL | | // with comment +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL | // with comment +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:13:5 + | +LL | / if let Some(a) = Some(3) { +LL | | // with comment +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL | // with comment +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:21:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | // with comment +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL | // with comment +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs index 2465fe45645f1..d3d5b0c103e7f 100644 --- a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -251,6 +251,16 @@ pub mod issue13219 { } } +#[macro_export] +macro_rules! issue14488 { + ($e:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $e + } + }; +} + fn main() { allow_works!(1); simple!(1); @@ -271,4 +281,10 @@ fn main() { multiple_unsafe_blocks!(1, 1, 1); unsafe_from_root_ctxt!(unsafe { 1 }); nested_macros!(1, 1); + + // These two invocations lead to two expanded unsafe blocks, each with an `#[expect]` on it. + // Only of them gets a warning, which used to result in an unfulfilled expectation for the other + // expanded unsafe block. + issue14488!(1); + issue14488!(2); } diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 36540bf1dcf73..2877871d0bf4c 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index da76bb20fd961..f958b92a102a3 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,3 +1,4 @@ +#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index 022deb330e6e3..e1a8941e102f5 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs index 08a8e1186d5cb..13e19e9fe14bf 100644 --- a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs +++ b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs @@ -29,7 +29,7 @@ unsafe impl Send for MyOption {} //~^ non_send_fields_in_send_ty // All fields are disallowed when raw pointer heuristic is off -extern "C" { +unsafe extern "C" { type NonSend; } diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml index f43c9d97e825d..3cb8523562a8f 100644 --- a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -1 +1 @@ -lint-inconsistent-struct-field-initializers = true +check-inconsistent-struct-field-initializers = true diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml new file mode 100644 index 0000000000000..6d0d732a92237 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml @@ -0,0 +1,14 @@ +[[disallowed-types]] +path = "std::result::Result::Err" + +[[disallowed-macros]] +path = "bool" + +[[disallowed-methods]] +path = "std::process::current_exe" + +# negative test + +[[disallowed-methods]] +path = "std::current_exe" +allow-invalid = true diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs new file mode 100644 index 0000000000000..c152038270348 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs @@ -0,0 +1,5 @@ +//@error-in-other-file: expected a macro, found a primitive type +//@error-in-other-file: `std::process::current_exe` does not refer to an existing function +//@error-in-other-file: expected a type, found a tuple variant + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr new file mode 100644 index 0000000000000..82550108eba53 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -0,0 +1,23 @@ +warning: expected a macro, found a primitive type + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + | +LL | / [[disallowed-macros]] +LL | | path = "bool" + | |_____________^ + +warning: `std::process::current_exe` does not refer to an existing function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + | +LL | / [[disallowed-methods]] +LL | | path = "std::process::current_exe" + | |__________________________________^ + +warning: expected a type, found a tuple variant + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 + | +LL | / [[disallowed-types]] +LL | | path = "std::result::Result::Err" + | |_________________________________^ + +warning: 3 warnings emitted + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index fee5b01b68982..f2eaa66a4ae41 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -29,12 +29,11 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -49,7 +48,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold - lint-inconsistent-struct-field-initializers + lint-commented-code literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -122,12 +121,11 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -142,7 +140,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold - lint-inconsistent-struct-field-initializers + lint-commented-code literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -215,12 +213,11 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -235,7 +232,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold - lint-inconsistent-struct-field-initializers + lint-commented-code literal-representation-threshold matches-for-let-else max-fn-params-bools diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index af72d6be0e096..20511cbed165e 100644 --- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -15,7 +15,7 @@ mod my_crate { } } -use utils::{BAR, print}; +pub use utils::{BAR, print}; //~^ ERROR: usage of wildcard import use my_crate::utils::my_util_fn; //~^ ERROR: usage of wildcard import diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs index 91009dd8835f8..8d05910f471ba 100644 --- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -15,7 +15,7 @@ mod my_crate { } } -use utils::*; +pub use utils::*; //~^ ERROR: usage of wildcard import use my_crate::utils::*; //~^ ERROR: usage of wildcard import diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr index 3d3be965aa411..5e624dd6c3cdc 100644 --- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -1,8 +1,8 @@ error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:9 | -LL | use utils::*; - | ^^^^^^^^ help: try: `utils::{BAR, print}` +LL | pub use utils::*; + | ^^^^^^^^ help: try: `utils::{BAR, print}` | = note: `-D clippy::wildcard-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` diff --git a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs index edcd5247f18ce..361bc2033934a 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs +++ b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs @@ -8,9 +8,11 @@ mod dont_warn { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - asm!("", options()); - asm!("", options(nostack)); + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + } } global_asm!(""); diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_x86.rs index 4e91f27cd3189..30401c9a0448a 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_x86.rs +++ b/src/tools/clippy/tests/ui/asm_syntax_x86.rs @@ -5,17 +5,19 @@ mod warn_intel { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - //~^ inline_asm_x86_intel_syntax + unsafe { + asm!(""); + //~^ inline_asm_x86_intel_syntax - asm!("", options()); - //~^ inline_asm_x86_intel_syntax + asm!("", options()); + //~^ inline_asm_x86_intel_syntax - asm!("", options(nostack)); - //~^ inline_asm_x86_intel_syntax + asm!("", options(nostack)); + //~^ inline_asm_x86_intel_syntax - asm!("", options(att_syntax)); - asm!("", options(nostack, att_syntax)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } } global_asm!(""); @@ -32,14 +34,16 @@ mod warn_att { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - asm!("", options()); - asm!("", options(nostack)); - asm!("", options(att_syntax)); - //~^ inline_asm_x86_att_syntax - - asm!("", options(nostack, att_syntax)); - //~^ inline_asm_x86_att_syntax + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + //~^ inline_asm_x86_att_syntax + + asm!("", options(nostack, att_syntax)); + //~^ inline_asm_x86_att_syntax + } } global_asm!(""); diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.stderr b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr index 2dcd955f03479..8e068cf2349cd 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_x86.stderr +++ b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr @@ -1,31 +1,31 @@ error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:8:9 + --> tests/ui/asm_syntax_x86.rs:9:13 | -LL | asm!(""); - | ^^^^^^^^ +LL | asm!(""); + | ^^^^^^^^ | = help: use AT&T x86 assembly syntax = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]` error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:11:9 + --> tests/ui/asm_syntax_x86.rs:12:13 | -LL | asm!("", options()); - | ^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:14:9 + --> tests/ui/asm_syntax_x86.rs:15:13 | -LL | asm!("", options(nostack)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:21:5 + --> tests/ui/asm_syntax_x86.rs:23:5 | LL | global_asm!(""); | ^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | global_asm!(""); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:24:5 + --> tests/ui/asm_syntax_x86.rs:26:5 | LL | global_asm!("", options()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,25 +41,25 @@ LL | global_asm!("", options()); = help: use AT&T x86 assembly syntax error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:38:9 + --> tests/ui/asm_syntax_x86.rs:41:13 | -LL | asm!("", options(att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]` error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:41:9 + --> tests/ui/asm_syntax_x86.rs:44:13 | -LL | asm!("", options(nostack, att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:47:5 + --> tests/ui/asm_syntax_x86.rs:51:5 | LL | global_asm!("", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs index 59bc9f5bfa5c2..abefc34cf6b3e 100644 --- a/src/tools/clippy/tests/ui/author/if.rs +++ b/src/tools/clippy/tests/ui/author/if.rs @@ -1,6 +1,6 @@ //@ check-pass -#[allow(clippy::all)] +#![allow(clippy::all)] fn main() { #[clippy::author] diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.rs b/src/tools/clippy/tests/ui/author/macro_in_closure.rs index 8a02f38fad87b..373f0148d475a 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.rs +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.rs @@ -1,5 +1,7 @@ //@ check-pass +#![allow(clippy::uninlined_format_args)] + fn main() { #[clippy::author] let print_text = |x| println!("{}", x); diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.rs b/src/tools/clippy/tests/ui/author/macro_in_loop.rs index 84ffe416e839b..f68275fefaaa3 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.rs +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.rs @@ -1,6 +1,7 @@ //@ check-pass #![feature(stmt_expr_attributes)] +#![allow(clippy::uninlined_format_args)] fn main() { #[clippy::author] diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index 1a2a4ec231143..7a4cc4fa9ee8e 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -131,12 +131,12 @@ fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Resul pub fn make_it_big(input: TokenStream) -> TokenStream { let mut expr_repeat = syn::parse_macro_input!(input as syn::ExprRepeat); let len_span = expr_repeat.len.span(); - if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len { - if let syn::Lit::Int(lit_int) = &expr_lit.lit { - let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); - let new_val = orig_val.saturating_mul(10); - expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); - } + if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len + && let syn::Lit::Int(lit_int) = &expr_lit.lit + { + let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); + let new_val = orig_val.saturating_mul(10); + expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); } quote::quote!(#expr_repeat).into() } diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index cd307e803d0c9..e696896538e54 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -3,6 +3,7 @@ #![warn(clippy::blocks_in_conditions)] #![allow( unused, + unnecessary_transmutes, clippy::let_and_return, clippy::needless_if, clippy::missing_transmute_annotations @@ -71,28 +72,6 @@ fn block_in_assert() { ); } -// issue #11814 -fn block_in_match_expr(num: i32) -> i32 { - let res = { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - let opt = Some(2); - opt - }; match res { - Some(0) => 1, - Some(n) => num * 2, - None => 0, - }; - - match unsafe { - let hearty_hearty_hearty = vec![240, 159, 146, 150]; - String::from_utf8_unchecked(hearty_hearty_hearty).as_str() - } { - "💖" => 1, - "what" => 2, - _ => 3, - } -} - // issue #12162 macro_rules! timed { ($name:expr, $body:expr $(,)?) => {{ diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 6a211c8edfd4f..8c8f3249b8a74 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -3,6 +3,7 @@ #![warn(clippy::blocks_in_conditions)] #![allow( unused, + unnecessary_transmutes, clippy::let_and_return, clippy::needless_if, clippy::missing_transmute_annotations @@ -71,28 +72,6 @@ fn block_in_assert() { ); } -// issue #11814 -fn block_in_match_expr(num: i32) -> i32 { - match { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - let opt = Some(2); - opt - } { - Some(0) => 1, - Some(n) => num * 2, - None => 0, - }; - - match unsafe { - let hearty_hearty_hearty = vec![240, 159, 146, 150]; - String::from_utf8_unchecked(hearty_hearty_hearty).as_str() - } { - "💖" => 1, - "what" => 2, - _ => 3, - } -} - // issue #12162 macro_rules! timed { ($name:expr, $body:expr $(,)?) => {{ diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr index da21344a84289..41ff59c683e84 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:30:5 + --> tests/ui/blocks_in_conditions.rs:31:5 | LL | / if { LL | | @@ -20,13 +20,13 @@ LL ~ }; if res { | error: omit braces around single expression condition - --> tests/ui/blocks_in_conditions.rs:42:8 + --> tests/ui/blocks_in_conditions.rs:43:8 | LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> tests/ui/blocks_in_conditions.rs:48:8 + --> tests/ui/blocks_in_conditions.rs:49:8 | LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` @@ -34,24 +34,5 @@ LL | if true && x == 3 { 6 } else { 10 } = note: `-D clippy::nonminimal-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` -error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:76:5 - | -LL | / match { -LL | | -LL | | let opt = Some(2); -LL | | opt -LL | | } { - | |_____^ - | -help: try - | -LL ~ let res = { -LL + -LL + let opt = Some(2); -LL + opt -LL ~ }; match res { - | - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_2021.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.fixed new file mode 100644 index 0000000000000..c7cc643dba679 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.fixed @@ -0,0 +1,25 @@ +//@edition: 2021 + +#![allow(clippy::let_and_return)] + +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + let res = { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + }; match res { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_2021.rs b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.rs new file mode 100644 index 0000000000000..a911237f5f795 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.rs @@ -0,0 +1,25 @@ +//@edition: 2021 + +#![allow(clippy::let_and_return)] + +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + match { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + } { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_2021.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.stderr new file mode 100644 index 0000000000000..497ee9d679dde --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.stderr @@ -0,0 +1,23 @@ +error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> tests/ui/blocks_in_conditions_2021.rs:7:5 + | +LL | / match { +LL | | +LL | | let opt = Some(2); +LL | | opt +LL | | } { + | |_____^ + | + = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` +help: try + | +LL ~ let res = { +LL + +LL + let opt = Some(2); +LL + opt +LL ~ }; match res { + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed index 0080801d46b78..ed6141244b409 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed @@ -117,3 +117,27 @@ fn if_let(a: Enum, b: Enum) { 0 }; } + +fn issue14628() { + macro_rules! mac { + (if $cond:expr, $then:expr, $else:expr) => { + if $cond { $then } else { $else } + }; + (zero) => { + 0 + }; + (one) => { + 1 + }; + } + + let _ = i32::from(dbg!(4 > 0)); + //~^ bool_to_int_with_if + + let _ = dbg!(i32::from(4 > 0)); + //~^ bool_to_int_with_if + + let _ = mac!(if 4 > 0, 1, 0); + let _ = if 4 > 0 { mac!(one) } else { 0 }; + let _ = if 4 > 0 { 1 } else { mac!(zero) }; +} diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs index 72c7e2c71c560..3f1f1c766e460 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs @@ -157,3 +157,27 @@ fn if_let(a: Enum, b: Enum) { 0 }; } + +fn issue14628() { + macro_rules! mac { + (if $cond:expr, $then:expr, $else:expr) => { + if $cond { $then } else { $else } + }; + (zero) => { + 0 + }; + (one) => { + 1 + }; + } + + let _ = if dbg!(4 > 0) { 1 } else { 0 }; + //~^ bool_to_int_with_if + + let _ = dbg!(if 4 > 0 { 1 } else { 0 }); + //~^ bool_to_int_with_if + + let _ = mac!(if 4 > 0, 1, 0); + let _ = if 4 > 0 { mac!(one) } else { 0 }; + let _ = if 4 > 0 { 1 } else { mac!(zero) }; +} diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr index 415e80f8d73d1..94089bc6dc8ef 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr @@ -114,5 +114,21 @@ LL | if a { 1 } else { 0 } | = note: `a as u8` or `a.into()` can also be valid options -error: aborting due to 9 previous errors +error: boolean to int conversion using if + --> tests/ui/bool_to_int_with_if.rs:174:13 + | +LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(dbg!(4 > 0))` + | + = note: `dbg!(4 > 0) as i32` or `dbg!(4 > 0).into()` can also be valid options + +error: boolean to int conversion using if + --> tests/ui/bool_to_int_with_if.rs:177:18 + | +LL | let _ = dbg!(if 4 > 0 { 1 } else { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(4 > 0)` + | + = note: `(4 > 0) as i32` or `(4 > 0).into()` can also be valid options + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 3dca06fce4b8d..3ba2eea59f0b0 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -29,3 +29,21 @@ fn issue_13882() { let _raw = (&raw mut x[1]).wrapping_offset(-1); //~^ borrow_as_ptr } + +fn implicit_cast() { + let val = 1; + let p: *const i32 = &raw const val; + //~^ borrow_as_ptr + + let mut val = 1; + let p: *mut i32 = &raw mut val; + //~^ borrow_as_ptr + + let mut val = 1; + // Only lint the leftmost argument, the rightmost is ref to a temporary + core::ptr::eq(&raw const val, &1); + //~^ borrow_as_ptr + + // Do not lint references to temporaries + core::ptr::eq(&0i32, &1i32); +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index 3559dc23d0185..8cdd0512da5f7 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -29,3 +29,21 @@ fn issue_13882() { let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); //~^ borrow_as_ptr } + +fn implicit_cast() { + let val = 1; + let p: *const i32 = &val; + //~^ borrow_as_ptr + + let mut val = 1; + let p: *mut i32 = &mut val; + //~^ borrow_as_ptr + + let mut val = 1; + // Only lint the leftmost argument, the rightmost is ref to a temporary + core::ptr::eq(&val, &1); + //~^ borrow_as_ptr + + // Do not lint references to temporaries + core::ptr::eq(&0i32, &1i32); +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr index 4a9f2ed4aa003..b1fcce49403c8 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -25,5 +25,38 @@ error: borrow as raw pointer LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` -error: aborting due to 4 previous errors +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:35:25 + | +LL | let p: *const i32 = &val; + | ^^^^ + | +help: use a raw pointer instead + | +LL | let p: *const i32 = &raw const val; + | +++++++++ + +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:39:23 + | +LL | let p: *mut i32 = &mut val; + | ^^^^^^^^ + | +help: use a raw pointer instead + | +LL | let p: *mut i32 = &raw mut val; + | +++ + +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:44:19 + | +LL | core::ptr::eq(&val, &1); + | ^^^^ + | +help: use a raw pointer instead + | +LL | core::ptr::eq(&raw const val, &1); + | +++++++++ + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed index 17c224f10bfea..765dd75fceb92 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed @@ -81,3 +81,46 @@ fn issue_13584() { let p = &raw const *s; let _ = p as *const i8; } + +mod issue_9905 { + use std::{fs, io}; + + pub enum File { + Stdio, + File(fs::File), + } + + impl io::Read for &'_ File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + File::Stdio => io::stdin().read(buf), + File::File(file) => (&*file).read(buf), + } + } + } +} + +mod issue_11346 { + struct Struct; + + impl Struct { + fn foo(self: &mut &Self) {} + } + + trait Trait { + fn bar(&mut self) {} + } + + impl Trait for &Struct {} + + fn bar() { + let s = &Struct; + (&*s).foo(); + (&*s).bar(); + + let mut s = &Struct; + s.foo(); // To avoid a warning about `s` not needing to be mutable + s.foo(); + //~^ borrow_deref_ref + } +} diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs index 130ed2903dc61..8ee66bfa881ab 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.rs +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs @@ -81,3 +81,46 @@ fn issue_13584() { let p = &raw const *s; let _ = p as *const i8; } + +mod issue_9905 { + use std::{fs, io}; + + pub enum File { + Stdio, + File(fs::File), + } + + impl io::Read for &'_ File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + File::Stdio => io::stdin().read(buf), + File::File(file) => (&*file).read(buf), + } + } + } +} + +mod issue_11346 { + struct Struct; + + impl Struct { + fn foo(self: &mut &Self) {} + } + + trait Trait { + fn bar(&mut self) {} + } + + impl Trait for &Struct {} + + fn bar() { + let s = &Struct; + (&*s).foo(); + (&*s).bar(); + + let mut s = &Struct; + s.foo(); // To avoid a warning about `s` not needing to be mutable + (&*s).foo(); + //~^ borrow_deref_ref + } +} diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr index f5868aa874900..3d55da25b9b20 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr @@ -19,5 +19,11 @@ error: deref on an immutable reference LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` -error: aborting due to 3 previous errors +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:123:9 + | +LL | (&*s).foo(); + | ^^^^^ help: if you would like to reborrow, try removing `&*`: `s` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/box_collection.rs b/src/tools/clippy/tests/ui/box_collection.rs index 0f7d3c74ddd07..7ae5446924fa0 100644 --- a/src/tools/clippy/tests/ui/box_collection.rs +++ b/src/tools/clippy/tests/ui/box_collection.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![allow( clippy::boxed_local, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/box_collection.stderr b/src/tools/clippy/tests/ui/box_collection.stderr index ebbc3d92b57f7..d730e2dcc1145 100644 --- a/src/tools/clippy/tests/ui/box_collection.stderr +++ b/src/tools/clippy/tests/ui/box_collection.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `Box>`. Consider using just `Vec<..>` - --> tests/ui/box_collection.rs:21:15 + --> tests/ui/box_collection.rs:20:15 | LL | fn test1(foo: Box>) {} | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn test1(foo: Box>) {} = help: to override `-D warnings` add `#[allow(clippy::box_collection)]` error: you seem to be trying to use `Box`. Consider using just `String` - --> tests/ui/box_collection.rs:29:15 + --> tests/ui/box_collection.rs:28:15 | LL | fn test3(foo: Box) {} | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | fn test3(foo: Box) {} = help: `String` is already on the heap, `Box` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `HashMap<..>` - --> tests/ui/box_collection.rs:32:15 + --> tests/ui/box_collection.rs:31:15 | LL | fn test4(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | fn test4(foo: Box>) {} = help: `HashMap<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `HashSet<..>` - --> tests/ui/box_collection.rs:35:15 + --> tests/ui/box_collection.rs:34:15 | LL | fn test5(foo: Box>) {} | ^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | fn test5(foo: Box>) {} = help: `HashSet<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `VecDeque<..>` - --> tests/ui/box_collection.rs:38:15 + --> tests/ui/box_collection.rs:37:15 | LL | fn test6(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | fn test6(foo: Box>) {} = help: `VecDeque<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `LinkedList<..>` - --> tests/ui/box_collection.rs:41:15 + --> tests/ui/box_collection.rs:40:15 | LL | fn test7(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn test7(foo: Box>) {} = help: `LinkedList<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `BTreeMap<..>` - --> tests/ui/box_collection.rs:44:15 + --> tests/ui/box_collection.rs:43:15 | LL | fn test8(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | fn test8(foo: Box>) {} = help: `BTreeMap<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `BTreeSet<..>` - --> tests/ui/box_collection.rs:47:15 + --> tests/ui/box_collection.rs:46:15 | LL | fn test9(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | fn test9(foo: Box>) {} = help: `BTreeSet<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `BinaryHeap<..>` - --> tests/ui/box_collection.rs:50:16 + --> tests/ui/box_collection.rs:49:16 | LL | fn test10(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed index bf7635fdf09bf..0c9d21243546d 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -1,5 +1,4 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] -#![allow(clippy::unnecessary_map_or)] use std::string::String; @@ -13,7 +12,7 @@ impl TestStruct { fn is_rust_file(filename: &str) -> bool { std::path::Path::new(filename) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + .is_some_and(|ext| ext.eq_ignore_ascii_case("rs")) //~^ case_sensitive_file_extension_comparisons } @@ -21,18 +20,18 @@ fn main() { // std::string::String and &str should trigger the lint failure with .ext12 let _ = std::path::Path::new(&String::new()) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons // The fixup should preserve the indentation level { let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons } @@ -42,11 +41,11 @@ fn main() { // std::string::String and &str should trigger the lint failure with .EXT12 let _ = std::path::Path::new(&String::new()) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); //~^ case_sensitive_file_extension_comparisons let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); //~^ case_sensitive_file_extension_comparisons // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase @@ -76,3 +75,11 @@ fn main() { let _ = "str".ends_with(".123"); TestStruct {}.ends_with(".123"); } + +#[clippy::msrv = "1.69"] +fn msrv_check() { + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + //~^ case_sensitive_file_extension_comparisons +} diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs index 0c4070a42d4b0..f8a947aa827b9 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,5 +1,4 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] -#![allow(clippy::unnecessary_map_or)] use std::string::String; @@ -64,3 +63,9 @@ fn main() { let _ = "str".ends_with(".123"); TestStruct {}.ends_with(".123"); } + +#[clippy::msrv = "1.69"] +fn msrv_check() { + let _ = String::new().ends_with(".ext12"); + //~^ case_sensitive_file_extension_comparisons +} diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr index e035534d26996..93bee8e766719 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,5 +1,5 @@ error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:14:5 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:13:5 | LL | filename.ends_with(".rs") | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,11 +11,11 @@ help: use std::path::Path | LL ~ std::path::Path::new(filename) LL + .extension() -LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) +LL + .is_some_and(|ext| ext.eq_ignore_ascii_case("rs")) | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:20:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,11 +25,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new(&String::new()) LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:22:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:21:13 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,11 +39,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:27:17 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:26:17 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,11 +53,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:35:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:34:13 | LL | let _ = String::new().ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,11 +67,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new(&String::new()) LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:37:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:36:13 | LL | let _ = "str".ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,8 +81,22 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); + | + +error: case-sensitive file extension comparison + --> tests/ui/case_sensitive_file_extension_comparisons.rs:69:13 + | +LL | let _ = String::new().ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed new file mode 100644 index 0000000000000..04c8f6782c51e --- /dev/null +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed @@ -0,0 +1,65 @@ +#![feature(round_char_boundary)] +#![warn(clippy::char_indices_as_byte_indices)] + +trait StrExt { + fn use_index(&self, _: usize); +} +impl StrExt for str { + fn use_index(&self, _: usize) {} +} + +fn bad(prim: &str, string: String) { + for (idx, _) in prim.char_indices() { + let _ = prim[..idx]; + //~^ char_indices_as_byte_indices + prim.split_at(idx); + //~^ char_indices_as_byte_indices + + // This won't panic, but it can still return a wrong substring + let _ = prim[..prim.floor_char_boundary(idx)]; + //~^ char_indices_as_byte_indices + + // can't use #[expect] here because the .fixed file will still have the attribute and create an + // unfulfilled expectation, but make sure lint level attributes work on the use expression: + #[allow(clippy::char_indices_as_byte_indices)] + let _ = prim[..idx]; + } + + for c in prim.char_indices() { + let _ = prim[..c.0]; + //~^ char_indices_as_byte_indices + prim.split_at(c.0); + //~^ char_indices_as_byte_indices + } + + for (idx, _) in string.char_indices() { + let _ = string[..idx]; + //~^ char_indices_as_byte_indices + string.split_at(idx); + //~^ char_indices_as_byte_indices + } +} + +fn good(prim: &str, prim2: &str) { + for (idx, _) in prim.chars().enumerate() { + // Indexing into a different string + let _ = prim2[..idx]; + + // Unknown use + std::hint::black_box(idx); + + // Method call to user defined extension trait + prim.use_index(idx); + + // str method taking a usize that doesn't represent a byte index + prim.splitn(idx, prim2); + } + + let mut string = "äa".to_owned(); + for (idx, _) in string.clone().chars().enumerate() { + // Even though the receiver is the same expression, it should not be treated as the same value. + string.clone().remove(idx); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs new file mode 100644 index 0000000000000..773a4fc65f12f --- /dev/null +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs @@ -0,0 +1,65 @@ +#![feature(round_char_boundary)] +#![warn(clippy::char_indices_as_byte_indices)] + +trait StrExt { + fn use_index(&self, _: usize); +} +impl StrExt for str { + fn use_index(&self, _: usize) {} +} + +fn bad(prim: &str, string: String) { + for (idx, _) in prim.chars().enumerate() { + let _ = prim[..idx]; + //~^ char_indices_as_byte_indices + prim.split_at(idx); + //~^ char_indices_as_byte_indices + + // This won't panic, but it can still return a wrong substring + let _ = prim[..prim.floor_char_boundary(idx)]; + //~^ char_indices_as_byte_indices + + // can't use #[expect] here because the .fixed file will still have the attribute and create an + // unfulfilled expectation, but make sure lint level attributes work on the use expression: + #[allow(clippy::char_indices_as_byte_indices)] + let _ = prim[..idx]; + } + + for c in prim.chars().enumerate() { + let _ = prim[..c.0]; + //~^ char_indices_as_byte_indices + prim.split_at(c.0); + //~^ char_indices_as_byte_indices + } + + for (idx, _) in string.chars().enumerate() { + let _ = string[..idx]; + //~^ char_indices_as_byte_indices + string.split_at(idx); + //~^ char_indices_as_byte_indices + } +} + +fn good(prim: &str, prim2: &str) { + for (idx, _) in prim.chars().enumerate() { + // Indexing into a different string + let _ = prim2[..idx]; + + // Unknown use + std::hint::black_box(idx); + + // Method call to user defined extension trait + prim.use_index(idx); + + // str method taking a usize that doesn't represent a byte index + prim.splitn(idx, prim2); + } + + let mut string = "äa".to_owned(); + for (idx, _) in string.clone().chars().enumerate() { + // Even though the receiver is the same expression, it should not be treated as the same value. + string.clone().remove(idx); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr new file mode 100644 index 0000000000000..e2b4c1db78cf4 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr @@ -0,0 +1,130 @@ +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/char_indices_as_byte_indices.rs:13:24 + | +LL | let _ = prim[..idx]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ + = note: `-D clippy::char-indices-as-byte-indices` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::char_indices_as_byte_indices)]` +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:15:23 + | +LL | prim.split_at(idx); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:19:49 + | +LL | let _ = prim[..prim.floor_char_boundary(idx)]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | + +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/char_indices_as_byte_indices.rs:29:24 + | +LL | let _ = prim[..c.0]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:28:9 + | +LL | for c in prim.chars().enumerate() { + | ^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for c in prim.chars().enumerate() { +LL + for c in prim.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:31:23 + | +LL | prim.split_at(c.0); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:28:9 + | +LL | for c in prim.chars().enumerate() { + | ^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for c in prim.chars().enumerate() { +LL + for c in prim.char_indices() { + | + +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/char_indices_as_byte_indices.rs:36:26 + | +LL | let _ = string[..idx]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:35:10 + | +LL | for (idx, _) in string.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in string.chars().enumerate() { +LL + for (idx, _) in string.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:38:25 + | +LL | string.split_at(idx); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:35:10 + | +LL | for (idx, _) in string.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in string.chars().enumerate() { +LL + for (idx, _) in string.char_indices() { + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr index c17eaef2326b3..bdac1e42309d8 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -244,8 +244,7 @@ LL | if X.is_some() { | = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = note: `-D static-mut-refs` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(static_mut_refs)]` + = note: `#[deny(static_mut_refs)]` on by default error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed index eb01633a25fd5..85d0991bef05d 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed @@ -74,3 +74,12 @@ impl ToOwned for Baz { Baz } } + +fn issue_8103() { + let foo1 = String::from("foo"); + let _ = foo1 == "foo"; + //~^ cmp_owned + let foo2 = "foo"; + let _ = foo1 == foo2; + //~^ cmp_owned +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs index 82409f27b129c..2393757d76f2b 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs @@ -74,3 +74,12 @@ impl ToOwned for Baz { Baz } } + +fn issue_8103() { + let foo1 = String::from("foo"); + let _ = foo1 == "foo".to_owned(); + //~^ cmp_owned + let foo2 = "foo"; + let _ = foo1 == foo2.to_owned(); + //~^ cmp_owned +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr index ca2ab44847274..dd9ffa70897ab 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr @@ -37,5 +37,17 @@ error: this creates an owned instance just for comparison LL | "abc".chars().filter(|c| c.to_owned() != 'X'); | ^^^^^^^^^^^^ help: try: `*c` -error: aborting due to 6 previous errors +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:80:21 + | +LL | let _ = foo1 == "foo".to_owned(); + | ^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:83:21 + | +LL | let _ = foo1 == foo2.to_owned(); + | ^^^^^^^^^^^^^^^ help: try: `foo2` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.rs b/src/tools/clippy/tests/ui/cognitive_complexity.rs index 2dbec955f63fe..8080c6775e0be 100644 --- a/src/tools/clippy/tests/ui/cognitive_complexity.rs +++ b/src/tools/clippy/tests/ui/cognitive_complexity.rs @@ -1,6 +1,11 @@ -#![allow(clippy::all)] #![warn(clippy::cognitive_complexity)] -#![allow(unused, unused_crate_dependencies)] +#![allow( + clippy::eq_op, + clippy::needless_borrows_for_generic_args, + clippy::needless_return, + clippy::nonminimal_bool, + clippy::uninlined_format_args +)] #[rustfmt::skip] fn main() { @@ -448,3 +453,22 @@ mod issue9300 { } } } + +#[clippy::cognitive_complexity = "1"] +mod issue14422 { + fn foo() { + //~^ cognitive_complexity + for _ in 0..10 { + println!("hello there"); + } + } + + fn bar() { + //~^ cognitive_complexity + for _ in 0..10 { + println!("hello there"); + } + return; + return; + } +} diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.stderr b/src/tools/clippy/tests/ui/cognitive_complexity.stderr index 52607b87c60ef..67ef4e5655bd6 100644 --- a/src/tools/clippy/tests/ui/cognitive_complexity.stderr +++ b/src/tools/clippy/tests/ui/cognitive_complexity.stderr @@ -1,5 +1,5 @@ error: the function has a cognitive complexity of (28/25) - --> tests/ui/cognitive_complexity.rs:6:4 + --> tests/ui/cognitive_complexity.rs:11:4 | LL | fn main() { | ^^^^ @@ -9,7 +9,7 @@ LL | fn main() { = help: to override `-D warnings` add `#[allow(clippy::cognitive_complexity)]` error: the function has a cognitive complexity of (7/1) - --> tests/ui/cognitive_complexity.rs:93:4 + --> tests/ui/cognitive_complexity.rs:98:4 | LL | fn kaboom() { | ^^^^^^ @@ -17,7 +17,7 @@ LL | fn kaboom() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:153:4 + --> tests/ui/cognitive_complexity.rs:158:4 | LL | fn baa() { | ^^^ @@ -25,7 +25,7 @@ LL | fn baa() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:156:13 + --> tests/ui/cognitive_complexity.rs:161:13 | LL | let x = || match 99 { | ^^ @@ -33,7 +33,7 @@ LL | let x = || match 99 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:174:4 + --> tests/ui/cognitive_complexity.rs:179:4 | LL | fn bar() { | ^^^ @@ -41,7 +41,7 @@ LL | fn bar() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:187:4 + --> tests/ui/cognitive_complexity.rs:192:4 | LL | fn dont_warn_on_tests() { | ^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn dont_warn_on_tests() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:197:4 + --> tests/ui/cognitive_complexity.rs:202:4 | LL | fn barr() { | ^^^^ @@ -57,7 +57,7 @@ LL | fn barr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:209:4 + --> tests/ui/cognitive_complexity.rs:214:4 | LL | fn barr2() { | ^^^^^ @@ -65,7 +65,7 @@ LL | fn barr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:227:4 + --> tests/ui/cognitive_complexity.rs:232:4 | LL | fn barrr() { | ^^^^^ @@ -73,7 +73,7 @@ LL | fn barrr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:239:4 + --> tests/ui/cognitive_complexity.rs:244:4 | LL | fn barrr2() { | ^^^^^^ @@ -81,7 +81,7 @@ LL | fn barrr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:257:4 + --> tests/ui/cognitive_complexity.rs:262:4 | LL | fn barrrr() { | ^^^^^^ @@ -89,7 +89,7 @@ LL | fn barrrr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:269:4 + --> tests/ui/cognitive_complexity.rs:274:4 | LL | fn barrrr2() { | ^^^^^^^ @@ -97,7 +97,7 @@ LL | fn barrrr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:287:4 + --> tests/ui/cognitive_complexity.rs:292:4 | LL | fn cake() { | ^^^^ @@ -105,7 +105,7 @@ LL | fn cake() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (4/1) - --> tests/ui/cognitive_complexity.rs:299:8 + --> tests/ui/cognitive_complexity.rs:304:8 | LL | pub fn read_file(input_path: &str) -> String { | ^^^^^^^^^ @@ -113,7 +113,7 @@ LL | pub fn read_file(input_path: &str) -> String { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:332:4 + --> tests/ui/cognitive_complexity.rs:337:4 | LL | fn void(void: Void) { | ^^^^ @@ -121,7 +121,7 @@ LL | fn void(void: Void) { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (8/1) - --> tests/ui/cognitive_complexity.rs:385:4 + --> tests/ui/cognitive_complexity.rs:390:4 | LL | fn early_ret() -> i32 { | ^^^^^^^^^ @@ -129,7 +129,7 @@ LL | fn early_ret() -> i32 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:408:13 + --> tests/ui/cognitive_complexity.rs:413:13 | LL | let x = |a: i32, b: i32| -> i32 { | ^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL | let x = |a: i32, b: i32| -> i32 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:423:8 + --> tests/ui/cognitive_complexity.rs:428:8 | LL | fn moo(&self) { | ^^^ @@ -145,7 +145,7 @@ LL | fn moo(&self) { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:434:14 + --> tests/ui/cognitive_complexity.rs:439:14 | LL | async fn a() { | ^ @@ -153,12 +153,28 @@ LL | async fn a() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:443:22 + --> tests/ui/cognitive_complexity.rs:448:22 | LL | pub async fn async_method() { | ^^^^^^^^^^^^ | = help: you could split it up into multiple smaller functions -error: aborting due to 20 previous errors +error: the function has a cognitive complexity of (2/1) + --> tests/ui/cognitive_complexity.rs:459:8 + | +LL | fn foo() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> tests/ui/cognitive_complexity.rs:466:8 + | +LL | fn bar() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index 6e994018aef01..e1ceb04f9cb89 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -12,34 +12,40 @@ fn main() { let x = "hello"; let y = "world"; - if x == "hello" && y == "world" { - println!("Hello world!"); - } + if x == "hello" + && y == "world" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if (x == "hello" || x == "world") && (y == "world" || y == "hello") { - println!("Hello world!"); - } + if (x == "hello" || x == "world") + && (y == "world" || y == "hello") { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if x == "hello" && x == "world" && (y == "world" || y == "hello") { - println!("Hello world!"); - } + if x == "hello" && x == "world" + && (y == "world" || y == "hello") { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if (x == "hello" || x == "world") && y == "world" && y == "hello" { - println!("Hello world!"); - } + if (x == "hello" || x == "world") + && y == "world" && y == "hello" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if x == "hello" && x == "world" && y == "world" && y == "hello" { - println!("Hello world!"); - } + if x == "hello" && x == "world" + && y == "world" && y == "hello" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if 42 == 1337 && 'a' != 'A' { - println!("world!") - } + if 42 == 1337 + && 'a' != 'A' { + println!("world!") + } //~^^^^^ collapsible_if // Works because any if with an else statement cannot be collapsed. @@ -71,37 +77,17 @@ fn main() { assert!(true); // assert! is just an `if` } - - // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" {// Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { - // Not collapsible - if y == "world" { + if x == "hello" + && y == "world" { // Collapsible println!("Hello world!"); } - } - - if x == "hello" && y == "world" { // Collapsible - println!("Hello world!"); - } //~^^^^^ collapsible_if if x == "hello" { print!("Hello "); } else { // Not collapsible - if y == "world" { + if let Some(42) = Some(42) { println!("world!") } } @@ -110,21 +96,8 @@ fn main() { print!("Hello "); } else { // Not collapsible - if let Some(42) = Some(42) { - println!("world!") - } - } - - if x == "hello" { - /* Not collapsible */ - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { /* Not collapsible */ if y == "world" { - println!("Hello world!"); + println!("world!") } } @@ -150,11 +123,13 @@ fn main() { } // Fix #5962 - if matches!(true, true) && matches!(true, true) {} + if matches!(true, true) + && matches!(true, true) {} //~^^^ collapsible_if // Issue #9375 - if matches!(true, true) && truth() && matches!(true, true) {} + if matches!(true, true) && truth() + && matches!(true, true) {} //~^^^ collapsible_if if true { @@ -163,4 +138,27 @@ fn main() { println!("Hello world!"); } } + + if true + && true { + println!("No comment, linted"); + } + //~^^^^^ collapsible_if + + if true { + // Do not collapse because of this comment + if true { + println!("Hello world!"); + } + } +} + +#[rustfmt::skip] +fn layout_check() -> u32 { + if true + && true { + } + // This is a comment, do not collapse code to it + ; 3 + //~^^^^^ collapsible_if } diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index 5cf591a658c7a..0b996dca22e85 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -83,27 +83,6 @@ fn main() { assert!(true); // assert! is just an `if` } - - // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" {// Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { - // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - if x == "hello" { if y == "world" { // Collapsible println!("Hello world!"); @@ -115,7 +94,7 @@ fn main() { print!("Hello "); } else { // Not collapsible - if y == "world" { + if let Some(42) = Some(42) { println!("world!") } } @@ -124,21 +103,8 @@ fn main() { print!("Hello "); } else { // Not collapsible - if let Some(42) = Some(42) { - println!("world!") - } - } - - if x == "hello" { - /* Not collapsible */ if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { /* Not collapsible */ - if y == "world" { - println!("Hello world!"); + println!("world!") } } @@ -181,4 +147,28 @@ fn main() { println!("Hello world!"); } } + + if true { + if true { + println!("No comment, linted"); + } + } + //~^^^^^ collapsible_if + + if true { + // Do not collapse because of this comment + if true { + println!("Hello world!"); + } + } +} + +#[rustfmt::skip] +fn layout_check() -> u32 { + if true { + if true { + } + // This is a comment, do not collapse code to it + }; 3 + //~^^^^^ collapsible_if } diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr index 3cc3fe5534f25..532811462393b 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -12,9 +12,10 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` help: collapse nested if block | -LL ~ if x == "hello" && y == "world" { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -29,9 +30,10 @@ LL | | } | help: collapse nested if block | -LL ~ if (x == "hello" || x == "world") && (y == "world" || y == "hello") { -LL + println!("Hello world!"); -LL + } +LL ~ if (x == "hello" || x == "world") { +LL ~ && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -46,9 +48,10 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && x == "world" && (y == "world" || y == "hello") { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" && x == "world" +LL ~ && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -63,9 +66,10 @@ LL | | } | help: collapse nested if block | -LL ~ if (x == "hello" || x == "world") && y == "world" && y == "hello" { -LL + println!("Hello world!"); -LL + } +LL ~ if (x == "hello" || x == "world") { +LL ~ && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -80,9 +84,10 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && x == "world" && y == "world" && y == "hello" { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" && x == "world" +LL ~ && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -97,13 +102,14 @@ LL | | } | help: collapse nested if block | -LL ~ if 42 == 1337 && 'a' != 'A' { -LL + println!("world!") -LL + } +LL ~ if 42 == 1337 +LL ~ && 'a' != 'A' { +LL | println!("world!") +LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:107:5 + --> tests/ui/collapsible_if.rs:86:5 | LL | / if x == "hello" { LL | | if y == "world" { // Collapsible @@ -114,26 +120,75 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && y == "world" { // Collapsible -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" +LL ~ && y == "world" { // Collapsible +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:167:5 + --> tests/ui/collapsible_if.rs:133:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} LL | | } - | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + | |_____^ + | +help: collapse nested if block + | +LL ~ if matches!(true, true) +LL ~ && matches!(true, true) {} + | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:173:5 + --> tests/ui/collapsible_if.rs:139:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} LL | | } - | |_____^ help: collapse nested if block: `if matches!(true, true) && truth() && matches!(true, true) {}` + | |_____^ + | +help: collapse nested if block + | +LL ~ if matches!(true, true) && truth() +LL ~ && matches!(true, true) {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:151:5 + | +LL | / if true { +LL | | if true { +LL | | println!("No comment, linted"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | println!("No comment, linted"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:168:5 + | +LL | / if true { +LL | | if true { +... | +LL | | }; 3 + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | } +LL | // This is a comment, do not collapse code to it +LL ~ ; 3 + | -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.fixed b/src/tools/clippy/tests/ui/collapsible_if_let_chains.fixed new file mode 100644 index 0000000000000..3dd9498a4c9f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.fixed @@ -0,0 +1,29 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + if let Some(a) = Some(3) + && let Some(b) = Some(4) { + let _ = a + b; + } + //~^^^^^ collapsible_if + + if let Some(a) = Some(3) + && a + 1 == 4 { + let _ = a; + } + //~^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) + && let Some(b) = Some(4) { + let _ = b; + } + //~^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs b/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs new file mode 100644 index 0000000000000..064b9a0be4847 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs @@ -0,0 +1,32 @@ +#![feature(let_chains)] +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + if let Some(a) = Some(3) { + if let Some(b) = Some(4) { + let _ = a + b; + } + } + //~^^^^^ collapsible_if + + if let Some(a) = Some(3) { + if a + 1 == 4 { + let _ = a; + } + } + //~^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) { + if let Some(b) = Some(4) { + let _ = b; + } + } + //~^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.stderr b/src/tools/clippy/tests/ui/collapsible_if_let_chains.stderr new file mode 100644 index 0000000000000..64a88114c47a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.stderr @@ -0,0 +1,58 @@ +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:12:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:19:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:26:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 796cabd4b669a..55ef55844957a 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -303,6 +303,18 @@ pub fn test_2(x: Issue9647) { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs index bbcd599f6d0b8..3352e822ef848 100644 --- a/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs +++ b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs @@ -1,8 +1,5 @@ //@ check-pass -#![deny(clippy::all)] -#![allow(unused_imports)] - use std::*; fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.fixed b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed index 181e1ebbe5a3a..c49a419f0d4ba 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed @@ -12,7 +12,7 @@ fn main() { // needless_collect trait Helper<'a>: Iterator {} +// Should not be linted because we have no idea whether the iterator has side effects fn x(w: &mut dyn for<'a> Helper<'a>) { - w.next().is_none(); - //~^ needless_collect + w.collect::>().is_empty(); } diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs index fb05dc781bc0d..f66b7e961c889 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs @@ -12,7 +12,7 @@ fn main() { // needless_collect trait Helper<'a>: Iterator {} +// Should not be linted because we have no idea whether the iterator has side effects fn x(w: &mut dyn for<'a> Helper<'a>) { w.collect::>().is_empty(); - //~^ needless_collect } diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.stderr b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr index b4a3f67081aec..91d59121ac4ed 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr @@ -7,14 +7,5 @@ LL | for v in A.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` -error: avoid using `collect()` when not needed - --> tests/ui/crashes/ice-11230.rs:16:7 - | -LL | w.collect::>().is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` - | - = note: `-D clippy::needless-collect` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/crashes/ice-13544-original.rs b/src/tools/clippy/tests/ui/crashes/ice-13544-original.rs new file mode 100644 index 0000000000000..1709eaeb365e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13544-original.rs @@ -0,0 +1,45 @@ +//@ check-pass +#![warn(clippy::significant_drop_tightening)] + +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; + +trait Scopable: Sized { + type SubType: Scopable; +} + +struct Subtree(ManuallyDrop>>); + +impl Drop for Subtree { + fn drop(&mut self) { + // SAFETY: The field cannot be used after we drop + unsafe { ManuallyDrop::drop(&mut self.0) } + } +} + +impl Deref for Subtree { + type Target = Tree; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Subtree { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +enum Tree { + Group(Vec>), + Subtree(Subtree), + Leaf(T), +} + +impl Tree { + fn foo(self) -> Self { + self + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-13544-reduced.rs b/src/tools/clippy/tests/ui/crashes/ice-13544-reduced.rs new file mode 100644 index 0000000000000..9266e71f5d0e0 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13544-reduced.rs @@ -0,0 +1,16 @@ +//@ check-pass +#![warn(clippy::significant_drop_tightening)] +#![allow(unused, clippy::no_effect)] + +use std::marker::PhantomData; + +trait Trait { + type Assoc: Trait; +} +struct S(*const S, PhantomData); + +fn f(x: &mut S) { + &mut x.0; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs index 3ccd33052cd6c..422c29b66cf68 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1588.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(clippy::all)] +#![expect(clippy::no_effect)] // Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs index 4a1886c08af6a..776b0a93bf7c7 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1782.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(dead_code, unused_variables, invalid_null_arguments)] +#![allow(dead_code, unused_variables, invalid_null_arguments, unnecessary_transmutes)] #![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs index 34ff725d71176..813972a046f75 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1969.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs @@ -1,7 +1,5 @@ //@ check-pass -#![allow(clippy::all)] - // Test for https://github.com/rust-lang/rust-clippy/issues/1969 fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs index 4ce484917ae2f..e06eccdf142ca 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3462.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs @@ -1,8 +1,6 @@ //@ check-pass -#![warn(clippy::all)] -#![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)] -#![allow(unused)] +#![expect(clippy::disallowed_names)] // Test for https://github.com/rust-lang/rust-clippy/issues/3462 diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs index aa3bf493c201c..ca82f638b0bba 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-700.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs @@ -1,7 +1,5 @@ //@ check-pass -#![deny(clippy::all)] - // Test for https://github.com/rust-lang/rust-clippy/issues/700 fn core() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-7012.rs b/src/tools/clippy/tests/ui/crashes/ice-7012.rs index d76995adadf1a..48c1c5a98d40a 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7012.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7012.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(clippy::all)] +#![expect(clippy::single_match)] enum _MyOption { None, diff --git a/src/tools/clippy/tests/ui/crashes/ice-7423.rs b/src/tools/clippy/tests/ui/crashes/ice-7423.rs index a03981842fcc4..fbf5d6520ed64 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7423.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7423.rs @@ -6,7 +6,7 @@ pub trait Trait { impl Trait for usize { fn f() { - extern "C" { + unsafe extern "C" { fn g() -> usize; } } diff --git a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs index cb4685e78e22f..0aa55cd0fac61 100644 --- a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs +++ b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs @@ -1,10 +1,7 @@ //@ check-pass -#![deny(clippy::all)] - // Test for https://github.com/rust-lang/rust-clippy/issues/1336 -#[allow(dead_code)] struct Foo; impl Iterator for Foo { diff --git a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs index 68e39531682a0..1b3b0290d909e 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs +++ b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs @@ -1,6 +1,5 @@ //@ check-pass -#[deny(clippy::all)] #[derive(Debug)] pub enum Error { Type(&'static str), diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed index e09a913ef06cd..9d977e9eddc84 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed @@ -3,7 +3,6 @@ use core::panic::PanicInfo; -#[warn(clippy::all)] pub fn main() { let mut a = 42; let mut b = 1337; diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs index 66ca97690c175..0967efe2ed8dc 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs @@ -3,7 +3,6 @@ use core::panic::PanicInfo; -#[warn(clippy::all)] pub fn main() { let mut a = 42; let mut b = 1337; diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr index 3e37bd95ef340..e16b53b51a810 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are trying to swap `a` and `b` - --> tests/ui/crate_level_checks/no_std_swap.rs:11:5 + --> tests/ui/crate_level_checks/no_std_swap.rs:10:5 | LL | / a = b; ... | @@ -7,8 +7,7 @@ LL | | b = a; | |_________^ help: try: `core::mem::swap(&mut a, &mut b)` | = note: or maybe you should use `core::mem::replace`? - = note: `-D clippy::almost-swapped` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_swapped)]` + = note: `#[deny(clippy::almost_swapped)]` on by default error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed index fd1a0d8934b3c..3b9dee81898ab 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed @@ -1,5 +1,10 @@ +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unit_arg, + clippy::unnecessary_operation +)] #![warn(clippy::dbg_macro)] -#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] fn foo(n: u32) -> u32 { if let Some(n) = n.checked_sub(4) { n } else { n } diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs index c96e2c7251c29..1dbbc6fe98456 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs @@ -1,5 +1,10 @@ +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unit_arg, + clippy::unnecessary_operation +)] #![warn(clippy::dbg_macro)] -#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index cd6dce584a2fa..f1412023cc897 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr @@ -1,5 +1,5 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:5:22 + --> tests/ui/dbg_macro/dbg_macro.rs:10:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let Some(n) = n.checked_sub(4) { n } else { n } | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:11:8 + --> tests/ui/dbg_macro/dbg_macro.rs:16:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if n <= 1 { | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:14:9 + --> tests/ui/dbg_macro/dbg_macro.rs:19:9 | LL | dbg!(1) | ^^^^^^^ @@ -37,7 +37,7 @@ LL + 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:17:9 + --> tests/ui/dbg_macro/dbg_macro.rs:22:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:23:5 + --> tests/ui/dbg_macro/dbg_macro.rs:28:5 | LL | dbg!(42); | ^^^^^^^^ @@ -61,7 +61,7 @@ LL + 42; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:26:14 + --> tests/ui/dbg_macro/dbg_macro.rs:31:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + foo(3) + factorial(4); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:29:5 + --> tests/ui/dbg_macro/dbg_macro.rs:34:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + (1, 2, 3, 4, 5); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:51:5 + --> tests/ui/dbg_macro/dbg_macro.rs:56:5 | LL | dbg!(); | ^^^^^^ @@ -96,7 +96,7 @@ LL - dbg!(); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:55:13 + --> tests/ui/dbg_macro/dbg_macro.rs:60:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -108,7 +108,7 @@ LL + let _ = (); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:58:9 + --> tests/ui/dbg_macro/dbg_macro.rs:63:9 | LL | bar(dbg!()); | ^^^^^^ @@ -120,7 +120,7 @@ LL + bar(()); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:61:10 + --> tests/ui/dbg_macro/dbg_macro.rs:66:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -132,7 +132,7 @@ LL + foo!(()); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:64:16 + --> tests/ui/dbg_macro/dbg_macro.rs:69:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -144,7 +144,7 @@ LL + foo2!(foo!(())); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:46:13 + --> tests/ui/dbg_macro/dbg_macro.rs:51:13 | LL | dbg!(); | ^^^^^^ @@ -159,7 +159,7 @@ LL - dbg!(); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:87:9 + --> tests/ui/dbg_macro/dbg_macro.rs:92:9 | LL | dbg!(2); | ^^^^^^^ @@ -171,7 +171,7 @@ LL + 2; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:94:5 + --> tests/ui/dbg_macro/dbg_macro.rs:99:5 | LL | dbg!(1); | ^^^^^^^ @@ -183,7 +183,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:100:5 + --> tests/ui/dbg_macro/dbg_macro.rs:105:5 | LL | dbg!(1); | ^^^^^^^ @@ -195,7 +195,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:107:9 + --> tests/ui/dbg_macro/dbg_macro.rs:112:9 | LL | dbg!(1); | ^^^^^^^ @@ -207,7 +207,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:114:31 + --> tests/ui/dbg_macro/dbg_macro.rs:119:31 | LL | println!("dbg: {:?}", dbg!(s)); | ^^^^^^^ @@ -219,7 +219,7 @@ LL + println!("dbg: {:?}", s); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:117:22 + --> tests/ui/dbg_macro/dbg_macro.rs:122:22 | LL | print!("{}", dbg!(s)); | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs index 03f5ca31f5f07..40f40f7ea0960 100644 --- a/src/tools/clippy/tests/ui/def_id_nocore.rs +++ b/src/tools/clippy/tests/ui/def_id_nocore.rs @@ -5,7 +5,7 @@ #![allow(clippy::missing_safety_doc)] #[link(name = "c")] -extern "C" {} +unsafe extern "C" {} #[lang = "sized"] pub trait Sized {} diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed index fa4d55177823e..1ca9be0ceddc7 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed @@ -161,3 +161,17 @@ fn main() { let _ = ::default(); } + +fn issue12654() { + #[derive(Default)] + struct G; + + fn f(_g: G) {} + + f(<_>::default()); + f(G); + //~^ default_constructed_unit_structs + + // No lint because `as Default` hides the singleton + f(::default()); +} diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs index 291cd89da0b79..99eb8913fc3ce 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs @@ -161,3 +161,17 @@ fn main() { let _ = ::default(); } + +fn issue12654() { + #[derive(Default)] + struct G; + + fn f(_g: G) {} + + f(<_>::default()); + f(::default()); + //~^ default_constructed_unit_structs + + // No lint because `as Default` hides the singleton + f(::default()); +} diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr index 6d4e1bdc2cc81..97fad792e4f7a 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr @@ -1,41 +1,65 @@ error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:11:13 + --> tests/ui/default_constructed_unit_structs.rs:11:9 | LL | Self::default() - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^----------- + | | + | help: remove this call to `default` | = note: `-D clippy::default-constructed-unit-structs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::default_constructed_unit_structs)]` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:54:31 + --> tests/ui/default_constructed_unit_structs.rs:54:20 | LL | inner: PhantomData::default(), - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:128:33 + --> tests/ui/default_constructed_unit_structs.rs:128:13 | LL | let _ = PhantomData::::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:130:42 + --> tests/ui/default_constructed_unit_structs.rs:130:31 | LL | let _: PhantomData = PhantomData::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:132:55 + --> tests/ui/default_constructed_unit_structs.rs:132:31 | LL | let _: PhantomData = std::marker::PhantomData::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^^^^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:134:23 + --> tests/ui/default_constructed_unit_structs.rs:134:13 | LL | let _ = UnitStruct::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^----------- + | | + | help: remove this call to `default` -error: aborting due to 6 previous errors +error: use of `default` to create a unit struct + --> tests/ui/default_constructed_unit_structs.rs:172:7 + | +LL | f(::default()); + | ^^^^^^^^^^^^^^ + | +help: remove this call to `default` + | +LL - f(::default()); +LL + f(G); + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 35646e1c23919..2787f6406fe39 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -16,5 +16,6 @@ #![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` #![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` #![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` +#![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index d7be1e583b08b..604732405c370 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -85,5 +85,11 @@ error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_ LL | #![warn(clippy::option_map_or_err_ok)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` + --> tests/ui/deprecated.rs:19:9 + | +LL | #![warn(clippy::match_on_vec_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index 707a9ff058576..e334203c7b2a7 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -6,6 +6,8 @@ dead_code )] #![warn(clippy::expl_impl_clone_on_copy)] +#![expect(incomplete_features)] // `unsafe_fields` is incomplete for the time being +#![feature(unsafe_fields)] // `clone()` cannot be derived automatically on unsafe fields #[derive(Copy)] @@ -113,4 +115,19 @@ impl Clone for Packed { } } +fn issue14558() { + pub struct Valid { + pub unsafe actual: (), + } + + unsafe impl Copy for Valid {} + + impl Clone for Valid { + #[inline] + fn clone(&self) -> Self { + *self + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index 20278d4f4e4a4..9004ced6849e5 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:14:1 + --> tests/ui/derive.rs:16:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:14:1 + --> tests/ui/derive.rs:16:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:40:1 + --> tests/ui/derive.rs:42:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:40:1 + --> tests/ui/derive.rs:42:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:53:1 + --> tests/ui/derive.rs:55:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:53:1 + --> tests/ui/derive.rs:55:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:66:1 + --> tests/ui/derive.rs:68:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:66:1 + --> tests/ui/derive.rs:68:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:88:1 + --> tests/ui/derive.rs:90:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:88:1 + --> tests/ui/derive.rs:90:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 5f2b697f88b02..8cf20d8b1a11c 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -1,4 +1,3 @@ - //! This file tests for the `DOC_MARKDOWN` lint. #![allow(dead_code, incomplete_features)] @@ -272,7 +271,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern "C" { +unsafe extern "C" { /// `foo()` //~^ doc_markdown fn in_extern(); diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index ed3925694c67e..5b6f2bd8330c5 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -1,4 +1,3 @@ - //! This file tests for the `DOC_MARKDOWN` lint. #![allow(dead_code, incomplete_features)] @@ -272,7 +271,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern "C" { +unsafe extern "C" { /// foo() //~^ doc_markdown fn in_extern(); diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index d67da75a230ca..98c26e6bec2eb 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -1,5 +1,5 @@ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:9:9 + --> tests/ui/doc/doc-fixable.rs:8:9 | LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) | ^^^^^^^ @@ -13,7 +13,7 @@ LL + /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:9:51 + --> tests/ui/doc/doc-fixable.rs:8:51 | LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) | ^^^^^^^^ @@ -25,7 +25,7 @@ LL + /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:12:83 + --> tests/ui/doc/doc-fixable.rs:11:83 | LL | /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun | ^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. B | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:15:13 + --> tests/ui/doc/doc-fixable.rs:14:13 | LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though. | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + /// Here be `::a::global:path`, and _::another::global::path_. :: is not a | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:15:36 + --> tests/ui/doc/doc-fixable.rs:14:36 | LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though. | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + /// Here be ::a::global:path, and _`::another::global::path`_. :: is not a | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:18:25 + --> tests/ui/doc/doc-fixable.rs:17:25 | LL | /// Import an item from ::awesome::global::blob:: (Intended postfix) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + /// Import an item from `::awesome::global::blob::` (Intended postfix) | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:20:31 + --> tests/ui/doc/doc-fixable.rs:19:31 | LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted) | ^^^^^ @@ -85,7 +85,7 @@ LL + /// These are the options for `::Cat`: (Intended trailing single colon, sho | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:22:22 + --> tests/ui/doc/doc-fixable.rs:21:22 | LL | /// That's not code ~NotInCodeBlock~. | ^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + /// That's not code ~`NotInCodeBlock`~. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:24:5 + --> tests/ui/doc/doc-fixable.rs:23:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:39:5 + --> tests/ui/doc/doc-fixable.rs:38:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:47:5 + --> tests/ui/doc/doc-fixable.rs:46:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:62:5 + --> tests/ui/doc/doc-fixable.rs:61:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:91:5 + --> tests/ui/doc/doc-fixable.rs:90:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:109:5 + --> tests/ui/doc/doc-fixable.rs:108:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:118:8 + --> tests/ui/doc/doc-fixable.rs:117:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:122:7 + --> tests/ui/doc/doc-fixable.rs:121:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:125:22 + --> tests/ui/doc/doc-fixable.rs:124:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:127:5 + --> tests/ui/doc/doc-fixable.rs:126:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:135:5 + --> tests/ui/doc/doc-fixable.rs:134:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:149:5 + --> tests/ui/doc/doc-fixable.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:161:43 + --> tests/ui/doc/doc-fixable.rs:160:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:5 + --> tests/ui/doc/doc-fixable.rs:165:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:167:1 + --> tests/ui/doc/doc-fixable.rs:166:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:175:43 + --> tests/ui/doc/doc-fixable.rs:174:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:5 + --> tests/ui/doc/doc-fixable.rs:179:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:181:1 + --> tests/ui/doc/doc-fixable.rs:180:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:195:5 + --> tests/ui/doc/doc-fixable.rs:194:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:215:22 + --> tests/ui/doc/doc-fixable.rs:214:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:240:34 + --> tests/ui/doc/doc-fixable.rs:239:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:22 + --> tests/ui/doc/doc-fixable.rs:263:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:30 + --> tests/ui/doc/doc-fixable.rs:263:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:269:5 + --> tests/ui/doc/doc-fixable.rs:268:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:276:9 + --> tests/ui/doc/doc-fixable.rs:275:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:281:5 + --> tests/ui/doc/doc-fixable.rs:280:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs index 1bdf01e4e22e9..7146fd7941ab0 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.rs +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -103,7 +103,7 @@ macro_rules! very_unsafe { /// /// Please keep the seat belt fastened pub unsafe fn drive() { - whee() + unsafe { whee() } } }; } diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed index 17d0d71a88545..2ce0c04c3017d 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed @@ -52,28 +52,35 @@ fn main() { let _ = CustomLast.last(); } +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn issue_14139() { let mut index = [true, true, false, false, false, true].iter(); - let mut subindex = index.by_ref().take(3); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let subindex = index.by_ref().take(3); + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); - let (mut subindex, _) = (index.by_ref().take(3), 42); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let (subindex, _) = (index.by_ref().take(3), 42); + let _ = subindex.last(); + let _ = index.next(); } fn drop_order() { @@ -90,3 +97,14 @@ fn drop_order() { //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); } + +fn issue_14444() { + let mut squares = vec![]; + let last_square = [1, 2, 3] + .into_iter() + .map(|x| { + squares.push(x * x); + Some(x * x) + }) + .last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs index 41bc669b1719f..a4eb9b3337b9d 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs @@ -52,28 +52,35 @@ fn main() { let _ = CustomLast.last(); } +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn issue_14139() { let mut index = [true, true, false, false, false, true].iter(); let subindex = index.by_ref().take(3); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let (subindex, _) = (index.by_ref().take(3), 42); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); } fn drop_order() { @@ -90,3 +97,14 @@ fn drop_order() { //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); } + +fn issue_14444() { + let mut squares = vec![]; + let last_square = [1, 2, 3] + .into_iter() + .map(|x| { + squares.push(x * x); + Some(x * x) + }) + .last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr index 1702a24d7a055..fe8cf2dcb2594 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr @@ -18,55 +18,7 @@ LL | let _ = DeIterator.last(); | help: try: `next_back()` error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:58:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^^^^^^^ - | -help: try - | -LL ~ let mut subindex = index.by_ref().take(3); -LL ~ let _ = subindex.next_back(); - | - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:62:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:67:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:72:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:76:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^^^^^^^ - | -help: try - | -LL ~ let (mut subindex, _) = (index.by_ref().take(3), 42); -LL ~ let _ = subindex.next_back(); - | - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:89:36 + --> tests/ui/double_ended_iterator_last.rs:96:36 | LL | println!("Last element is {}", v.last().unwrap().0); | ^^^^^^^^ @@ -78,5 +30,5 @@ LL ~ let mut v = v.into_iter(); LL ~ println!("Last element is {}", v.next_back().unwrap().0); | -error: aborting due to 8 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs index 3f125c7f20c1e..7c5de8832d698 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs @@ -1,10 +1,13 @@ //@no-rustfix #![warn(clippy::double_ended_iterator_last)] +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn main() { let mut index = [true, true, false, false, false, true].iter(); let subindex = (index.by_ref().take(3), 42); - let _ = subindex.0.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.0.last(); + let _ = index.next(); } fn drop_order() { diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr index f4be757d00d29..845afc11f0422 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr @@ -1,21 +1,5 @@ error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13 - | -LL | let _ = subindex.0.last(); - | ^^^^^^^^^^^------ - | | - | help: try: `next_back()` - | -note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13 - | -LL | let _ = subindex.0.last(); - | ^^^^^^^^^^ - = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:23:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^^------ @@ -24,10 +8,12 @@ LL | println!("Last element is {}", v.0.last().unwrap().0); | = note: this change will alter drop order which may be undesirable note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:23:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^ + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/eager_transmute.fixed b/src/tools/clippy/tests/ui/eager_transmute.fixed index 14cbb6113e62f..47a32ec836cc9 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.fixed +++ b/src/tools/clippy/tests/ui/eager_transmute.fixed @@ -71,8 +71,10 @@ fn f(op: u8, op2: Data, unrelated: u8) { } unsafe fn f2(op: u8) { - (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); - //~^ eager_transmute + unsafe { + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); + //~^ eager_transmute + } } #[rustc_layout_scalar_valid_range_end(254)] diff --git a/src/tools/clippy/tests/ui/eager_transmute.rs b/src/tools/clippy/tests/ui/eager_transmute.rs index 48d7d50cdaef8..906cd7bccc86f 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.rs +++ b/src/tools/clippy/tests/ui/eager_transmute.rs @@ -71,8 +71,10 @@ fn f(op: u8, op2: Data, unrelated: u8) { } unsafe fn f2(op: u8) { - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); - //~^ eager_transmute + unsafe { + (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); + //~^ eager_transmute + } } #[rustc_layout_scalar_valid_range_end(254)] diff --git a/src/tools/clippy/tests/ui/eager_transmute.stderr b/src/tools/clippy/tests/ui/eager_transmute.stderr index 54850d110eb26..c719ca8adc12e 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.stderr +++ b/src/tools/clippy/tests/ui/eager_transmute.stderr @@ -157,19 +157,19 @@ LL + let _: Option = (..=3).contains(&op).then(|| unsafe { std::mem: | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:74:24 + --> tests/ui/eager_transmute.rs:75:28 | -LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider using `bool::then` to only transmute if the condition holds | -LL - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); -LL + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); +LL - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); +LL + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:104:62 + --> tests/ui/eager_transmute.rs:106:62 | LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _: Option> = (v1 > 0).then(|| unsafe { std::mem::transm | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:111:86 + --> tests/ui/eager_transmute.rs:113:86 | LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + let _: Option = (v2 < NonZero::new(255u8).unwrap()).then(|| u | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:118:93 + --> tests/ui/eager_transmute.rs:120:93 | LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/empty_docs.rs b/src/tools/clippy/tests/ui/empty_docs.rs index d7768e07901ae..57f8976cd6a79 100644 --- a/src/tools/clippy/tests/ui/empty_docs.rs +++ b/src/tools/clippy/tests/ui/empty_docs.rs @@ -84,7 +84,7 @@ mod issue_12377 { use proc_macro_attr::with_empty_docs; #[with_empty_docs] - extern "C" { + unsafe extern "C" { type Test; } diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed index 885f6a50025e9..abdf6ca5cb61e 100644 --- a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed @@ -6,8 +6,7 @@ pub enum PublicTestEnum { NonEmptyParentheses(i32, i32), // No error EmptyBraces, //~^ empty_enum_variants_with_brackets - EmptyParentheses, - //~^ empty_enum_variants_with_brackets + EmptyParentheses(), // No error as enum is pub } enum TestEnum { @@ -20,6 +19,67 @@ enum TestEnum { AnotherEnum, // No error } +mod issue12551 { + enum EvenOdd { + // Used as functions -> no error + Even(), + Odd(), + // Not used as a function + Unknown, + //~^ empty_enum_variants_with_brackets + } + + fn even_odd(x: i32) -> EvenOdd { + (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) + } + + fn natural_number(x: i32) -> NaturalOrNot { + (x > 0) + .then(NaturalOrNot::Natural) + .unwrap_or_else(NaturalOrNot::NotNatural) + } + + enum NaturalOrNot { + // Used as functions -> no error + Natural(), + NotNatural(), + // Not used as a function + Unknown, + //~^ empty_enum_variants_with_brackets + } + + enum RedundantParenthesesFunctionCall { + // Used as a function call but with redundant parentheses + Parentheses, + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } + + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall::Parentheses; + RedundantParenthesesFunctionCall::NoParentheses; + } + + // Same test as above but with usage of the enum occurring before the definition. + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call_2() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall2::Parentheses; + RedundantParenthesesFunctionCall2::NoParentheses; + } + + enum RedundantParenthesesFunctionCall2 { + // Used as a function call but with redundant parentheses + Parentheses, + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } +} + enum TestEnumWithFeatures { NonEmptyBraces { #[cfg(feature = "thisisneverenabled")] @@ -28,4 +88,18 @@ enum TestEnumWithFeatures { NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error } +#[derive(Clone)] +enum Foo { + Variant1(i32), + Variant2, + Variant3, //~ ERROR: enum variant has empty brackets +} + +#[derive(Clone)] +pub enum PubFoo { + Variant1(i32), + Variant2, + Variant3(), +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs index 092712ee2ead4..63a5a8e9143e6 100644 --- a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs @@ -6,8 +6,7 @@ pub enum PublicTestEnum { NonEmptyParentheses(i32, i32), // No error EmptyBraces {}, //~^ empty_enum_variants_with_brackets - EmptyParentheses(), - //~^ empty_enum_variants_with_brackets + EmptyParentheses(), // No error as enum is pub } enum TestEnum { @@ -20,6 +19,67 @@ enum TestEnum { AnotherEnum, // No error } +mod issue12551 { + enum EvenOdd { + // Used as functions -> no error + Even(), + Odd(), + // Not used as a function + Unknown(), + //~^ empty_enum_variants_with_brackets + } + + fn even_odd(x: i32) -> EvenOdd { + (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) + } + + fn natural_number(x: i32) -> NaturalOrNot { + (x > 0) + .then(NaturalOrNot::Natural) + .unwrap_or_else(NaturalOrNot::NotNatural) + } + + enum NaturalOrNot { + // Used as functions -> no error + Natural(), + NotNatural(), + // Not used as a function + Unknown(), + //~^ empty_enum_variants_with_brackets + } + + enum RedundantParenthesesFunctionCall { + // Used as a function call but with redundant parentheses + Parentheses(), + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } + + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall::Parentheses(); + RedundantParenthesesFunctionCall::NoParentheses; + } + + // Same test as above but with usage of the enum occurring before the definition. + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call_2() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall2::Parentheses(); + RedundantParenthesesFunctionCall2::NoParentheses; + } + + enum RedundantParenthesesFunctionCall2 { + // Used as a function call but with redundant parentheses + Parentheses(), + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } +} + enum TestEnumWithFeatures { NonEmptyBraces { #[cfg(feature = "thisisneverenabled")] @@ -28,4 +88,18 @@ enum TestEnumWithFeatures { NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error } +#[derive(Clone)] +enum Foo { + Variant1(i32), + Variant2, + Variant3(), //~ ERROR: enum variant has empty brackets +} + +#[derive(Clone)] +pub enum PubFoo { + Variant1(i32), + Variant2, + Variant3(), +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr index a9ae3b476dd68..7fe85e829a351 100644 --- a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr @@ -9,7 +9,15 @@ LL | EmptyBraces {}, = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:9:21 + --> tests/ui/empty_enum_variants_with_brackets.rs:15:16 + | +LL | EmptyBraces {}, + | ^^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:17:21 | LL | EmptyParentheses(), | ^^ @@ -17,20 +25,58 @@ LL | EmptyParentheses(), = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:16:16 + --> tests/ui/empty_enum_variants_with_brackets.rs:28:16 | -LL | EmptyBraces {}, - | ^^^ +LL | Unknown(), + | ^^ | = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:18:21 + --> tests/ui/empty_enum_variants_with_brackets.rs:47:16 | -LL | EmptyParentheses(), - | ^^ +LL | Unknown(), + | ^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:53:20 + | +LL | Parentheses(), + | ^^ + | +help: remove the brackets + | +LL ~ Parentheses, +LL | +... +LL | // The parentheses in the below line are redundant. +LL ~ RedundantParenthesesFunctionCall::Parentheses; + | + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:76:20 + | +LL | Parentheses(), + | ^^ + | +help: remove the brackets + | +LL ~ RedundantParenthesesFunctionCall2::Parentheses; +LL | RedundantParenthesesFunctionCall2::NoParentheses; +... +LL | // Used as a function call but with redundant parentheses +LL ~ Parentheses, + | + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:95:13 + | +LL | Variant3(), + | ^^ | = help: remove the brackets -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed index e4ba09ea1d478..70ab235b694f5 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed @@ -142,4 +142,9 @@ impl Foo for LineComment { fn bar() {} } +//~v empty_line_after_doc_comments +/// Docs for this item. +// fn some_item() {} +impl LineComment {} // or any other nameless item kind + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed index a20f9bc20eb56..87c636c6ad2c7 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed @@ -152,4 +152,10 @@ impl Foo for LineComment { fn bar() {} } +//~v empty_line_after_doc_comments +// /// Docs for this item. +// fn some_item() {} + +impl LineComment {} // or any other nameless item kind + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs index 9e3ddfd5abe11..91e9c1ac0b6d3 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs @@ -155,4 +155,10 @@ impl Foo for LineComment { fn bar() {} } +//~v empty_line_after_doc_comments +/// Docs for this item. +// fn some_item() {} + +impl LineComment {} // or any other nameless item kind + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr index fe25ba9afcb90..ae8cb91ba12f7 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr @@ -87,7 +87,7 @@ LL | fn new_code() {} | ----------- the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `new_code` comment it out +help: if the doc comment should not document function `new_code` then comment it out | LL | // /// docs for `old_code` | ++ @@ -107,7 +107,7 @@ LL | struct Multiple; | --------------- the comment documents this struct | = help: if the empty lines are unintentional, remove them -help: if the doc comment should not document `Multiple` comment it out +help: if the doc comment should not document struct `Multiple` then comment it out | LL ~ // /// Docs LL ~ // /// for OldA @@ -149,7 +149,7 @@ LL | fn new_code() {} | ----------- the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `new_code` comment it out +help: if the doc comment should not document function `new_code` then comment it out | LL - /** LL + /* @@ -167,7 +167,7 @@ LL | fn new_code2() {} | ------------ the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `new_code2` comment it out +help: if the doc comment should not document function `new_code2` then comment it out | LL | // /// Docs for `old_code2` | ++ @@ -183,10 +183,26 @@ LL | fn bar() {} | ------ the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `bar` comment it out +help: if the doc comment should not document function `bar` then comment it out | LL | // /// comment on assoc item | ++ -error: aborting due to 11 previous errors +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:159:1 + | +LL | / /// Docs for this item. +LL | | // fn some_item() {} +LL | | + | |_^ +LL | impl LineComment {} // or any other nameless item kind + | - the comment documents this implementation + | + = help: if the empty line is unintentional, remove it +help: if the doc comment should not document the following item then comment it out + | +LL | // /// Docs for this item. + | ++ + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed index 69452a8d9a671..f2df9f0204ea6 100644 --- a/src/tools/clippy/tests/ui/entry.fixed +++ b/src/tools/clippy/tests/ui/entry.fixed @@ -226,4 +226,26 @@ fn issue11976() { } } +mod issue14449 { + use std::collections::BTreeMap; + + pub struct Meow { + map: BTreeMap, + } + + impl Meow { + fn pet(&self, _key: &str, _v: u32) -> u32 { + 42 + } + } + + pub fn f(meow: &Meow, x: String) { + if meow.map.contains_key(&x) { + let _ = meow.pet(&x, 1); + } else { + let _ = meow.pet(&x, 0); + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs index 3578324f01c58..166eea417ac23 100644 --- a/src/tools/clippy/tests/ui/entry.rs +++ b/src/tools/clippy/tests/ui/entry.rs @@ -232,4 +232,26 @@ fn issue11976() { } } +mod issue14449 { + use std::collections::BTreeMap; + + pub struct Meow { + map: BTreeMap, + } + + impl Meow { + fn pet(&self, _key: &str, _v: u32) -> u32 { + 42 + } + } + + pub fn f(meow: &Meow, x: String) { + if meow.map.contains_key(&x) { + let _ = meow.pet(&x, 1); + } else { + let _ = meow.pet(&x, 0); + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed index 7235f7d5b82af..ec6bed152e797 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed @@ -59,7 +59,7 @@ fn f_str_t(_: &str, _: T) {} fn f_box_t(_: &Box) {} -extern "C" { +unsafe extern "C" { fn var(_: u32, ...); } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs index c4d2b28ff4b5f..ca58c650d9ce9 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs @@ -59,7 +59,7 @@ fn f_str_t(_: &str, _: T) {} fn f_box_t(_: &Box) {} -extern "C" { +unsafe extern "C" { fn var(_: u32, ...); } diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs new file mode 100644 index 0000000000000..68294292502ac --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs @@ -0,0 +1,63 @@ +#![allow(clippy::question_mark, unused)] +#![warn(clippy::filter_map_bool_then)] +//@no-rustfix + +fn issue11617() { + let mut x: Vec = vec![0; 10]; + let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { + //~^ filter_map_bool_then + (x[i] != *v).then(|| { + x[i] = i; + i + }) + }); +} + +mod issue14368 { + + fn do_something(_: ()) -> bool { + true + } + + fn option_with_early_return(x: &[Option]) { + let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + //~^ filter_map_bool_then + let _ = x + .iter() + .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); + //~^ filter_map_bool_then + let _ = x.iter().filter_map(|&x| { + //~^ filter_map_bool_then + match x { + Some(x) => x, + None => return None, + } + .then(|| do_something(())) + }); + } + + #[derive(Copy, Clone)] + enum Foo { + One(bool), + Two, + Three(Option), + } + + fn nested_type_with_early_return(x: &[Foo]) { + let _ = x.iter().filter_map(|&x| { + //~^ filter_map_bool_then + match x { + Foo::One(x) => x, + Foo::Two => return None, + Foo::Three(inner) => { + if inner? == 0 { + return Some(false); + } else { + true + } + }, + } + .then(|| do_something(())) + }); + } +} diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr new file mode 100644 index 0000000000000..2025958136ba6 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr @@ -0,0 +1,65 @@ +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:7:48 + | +LL | let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { + | ________________________________________________^ +LL | | +LL | | (x[i] != *v).then(|| { +LL | | x[i] = i; +LL | | i +LL | | }) +LL | | }); + | |______^ + | + = help: consider using `filter` then `map` instead + = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:23:26 + | +LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:27:14 + | +LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:29:26 + | +LL | let _ = x.iter().filter_map(|&x| { + | __________________________^ +LL | | +LL | | match x { +LL | | Some(x) => x, +... | +LL | | .then(|| do_something(())) +LL | | }); + | |__________^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:47:26 + | +LL | let _ = x.iter().filter_map(|&x| { + | __________________________^ +LL | | +LL | | match x { +LL | | Foo::One(x) => x, +... | +LL | | .then(|| do_something(())) +LL | | }); + | |__________^ + | + = help: consider using `filter` then `map` instead + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_map_next.rs b/src/tools/clippy/tests/ui/filter_map_next.rs index 2a2237ed16cf2..5414e01c87006 100644 --- a/src/tools/clippy/tests/ui/filter_map_next.rs +++ b/src/tools/clippy/tests/ui/filter_map_next.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::pedantic)] +#![warn(clippy::filter_map_next)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed index 285863ef340db..09c416041a4e9 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(unused)] +#![warn(clippy::filter_map_next)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs index af911689b7c72..3d686ef41d917 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(unused)] +#![warn(clippy::filter_map_next)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr index 707dec8687b1f..1002837732b86 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr @@ -1,5 +1,5 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> tests/ui/filter_map_next_fixable.rs:7:32 + --> tests/ui/filter_map_next_fixable.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.iter().find_map(|s| s.parse().ok())` @@ -8,7 +8,7 @@ LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next = help: to override `-D warnings` add `#[allow(clippy::filter_map_next)]` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> tests/ui/filter_map_next_fixable.rs:21:26 + --> tests/ui/filter_map_next_fixable.rs:20:26 | LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.iter().find_map(|s| s.parse().ok())` diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs index aba1f2cbe581e..a9a8508d5b7db 100644 --- a/src/tools/clippy/tests/ui/find_map.rs +++ b/src/tools/clippy/tests/ui/find_map.rs @@ -1,6 +1,5 @@ //@ check-pass -#![warn(clippy::all, clippy::pedantic)] #![allow(clippy::useless_vec)] #[derive(Debug, Copy, Clone)] diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs index cc18708d25faf..25d25663d1e4d 100644 --- a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs @@ -1,7 +1,7 @@ #![warn(clippy::fn_params_excessive_bools)] #![allow(clippy::too_many_arguments)] -extern "C" { +unsafe extern "C" { // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } @@ -14,8 +14,8 @@ macro_rules! foo { foo!(); -#[no_mangle] -extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} +#[unsafe(no_mangle)] +unsafe extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: bool) {} //~^ ERROR: more than 3 bools in function parameters fn h(_: bool, _: bool, _: bool) {} @@ -39,8 +39,8 @@ impl S { fn f(&self, _: bool, _: bool, _: bool, _: bool) {} //~^ ERROR: more than 3 bools in function parameters fn g(&self, _: bool, _: bool, _: bool) {} - #[no_mangle] - extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} + #[unsafe(no_mangle)] + unsafe extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} } impl Trait for S { diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs index 4e84dcf7d5b70..009815633d759 100644 --- a/src/tools/clippy/tests/ui/formatting.rs +++ b/src/tools/clippy/tests/ui/formatting.rs @@ -1,6 +1,3 @@ -#![warn(clippy::all)] -#![allow(unused_variables)] -#![allow(unused_assignments)] #![allow(clippy::if_same_then_else)] #![allow(clippy::deref_addrof)] #![allow(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr index 972bd3a6a2e65..d9dc2a55f5b62 100644 --- a/src/tools/clippy/tests/ui/formatting.stderr +++ b/src/tools/clippy/tests/ui/formatting.stderr @@ -1,5 +1,5 @@ error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` - --> tests/ui/formatting.rs:16:6 + --> tests/ui/formatting.rs:13:6 | LL | a =- 35; | ^^^^ @@ -9,7 +9,7 @@ LL | a =- 35; = help: to override `-D warnings` add `#[allow(clippy::suspicious_assignment_formatting)]` error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` - --> tests/ui/formatting.rs:20:6 + --> tests/ui/formatting.rs:17:6 | LL | a =* &191; | ^^^^ @@ -17,7 +17,7 @@ LL | a =* &191; = note: to remove this lint, use either `*=` or `= *` error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` - --> tests/ui/formatting.rs:26:6 + --> tests/ui/formatting.rs:23:6 | LL | b =! false; | ^^^^ @@ -25,17 +25,16 @@ LL | b =! false; = note: to remove this lint, use either `!=` or `= !` error: possibly missing a comma here - --> tests/ui/formatting.rs:38:19 + --> tests/ui/formatting.rs:35:19 | LL | -1, -2, -3 // <= no comma here | ^ | = note: to remove this lint, add a comma or write the expr in a single line - = note: `-D clippy::possible-missing-comma` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::possible_missing_comma)]` + = note: `#[deny(clippy::possible_missing_comma)]` on by default error: possibly missing a comma here - --> tests/ui/formatting.rs:45:19 + --> tests/ui/formatting.rs:42:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -43,7 +42,7 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> tests/ui/formatting.rs:85:11 + --> tests/ui/formatting.rs:82:11 | LL | -1 | ^ diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed index 8618004efb89f..be98b22779584 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed @@ -73,3 +73,46 @@ fn main() { for _i in [1, 2, 3].iter().collect::>() {} //~^ from_iter_instead_of_collect } + +fn issue14581() { + let nums = [0, 1, 2]; + let _ = &nums.iter().map(|&num| char::from_u32(num).unwrap()).collect::(); + //~^ from_iter_instead_of_collect +} + +fn test_implicit_generic_args(iter: impl Iterator + Copy) { + struct S<'l, T = i32, const A: usize = 3, const B: usize = 3> { + a: [&'l T; A], + b: [&'l T; B], + } + + impl<'l, T, const A: usize, const B: usize> FromIterator<&'l T> for S<'l, T, A, B> { + fn from_iter>(_: I) -> Self { + todo!() + } + } + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::(); + //~^ from_iter_instead_of_collect +} diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs index c46397e8ff560..ce20fef2ac337 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs @@ -73,3 +73,46 @@ fn main() { for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} //~^ from_iter_instead_of_collect } + +fn issue14581() { + let nums = [0, 1, 2]; + let _ = &String::from_iter(nums.iter().map(|&num| char::from_u32(num).unwrap())); + //~^ from_iter_instead_of_collect +} + +fn test_implicit_generic_args(iter: impl Iterator + Copy) { + struct S<'l, T = i32, const A: usize = 3, const B: usize = 3> { + a: [&'l T; A], + b: [&'l T; B], + } + + impl<'l, T, const A: usize, const B: usize> FromIterator<&'l T> for S<'l, T, A, B> { + fn from_iter>(_: I) -> Self { + todo!() + } + } + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = ::from_iter(iter); + //~^ from_iter_instead_of_collect +} diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr index b46d97af152f6..ec11a375c0d87 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr @@ -91,5 +91,59 @@ error: usage of `FromIterator::from_iter` LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` -error: aborting due to 15 previous errors +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:79:14 + | +LL | let _ = &String::from_iter(nums.iter().map(|&num| char::from_u32(num).unwrap())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `nums.iter().map(|&num| char::from_u32(num).unwrap()).collect::()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:95:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:98:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:101:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:104:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:107:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:110:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:113:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:116:13 + | +LL | let _ = ::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::()` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/functions.rs b/src/tools/clippy/tests/ui/functions.rs index 9c1ca8bf93009..ceaba392dc200 100644 --- a/src/tools/clippy/tests/ui/functions.rs +++ b/src/tools/clippy/tests/ui/functions.rs @@ -1,5 +1,3 @@ -#![warn(clippy::all)] -#![allow(dead_code, unused_unsafe)] #![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)] // TOO_MANY_ARGUMENTS diff --git a/src/tools/clippy/tests/ui/functions.stderr b/src/tools/clippy/tests/ui/functions.stderr index c8770023f77a0..65cc627cc44c1 100644 --- a/src/tools/clippy/tests/ui/functions.stderr +++ b/src/tools/clippy/tests/ui/functions.stderr @@ -1,5 +1,5 @@ error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:8:1 + --> tests/ui/functions.rs:6:1 | LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:12:1 + --> tests/ui/functions.rs:10:1 | LL | / fn bad_multiline( LL | | @@ -20,88 +20,87 @@ LL | | ) { | |_^ error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:48:5 + --> tests/ui/functions.rs:46:5 | LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:58:5 + --> tests/ui/functions.rs:56:5 | LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:68:34 + --> tests/ui/functions.rs:66:34 | LL | println!("{}", unsafe { *p }); | ^ | - = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::not_unsafe_ptr_arg_deref)]` + = note: `#[deny(clippy::not_unsafe_ptr_arg_deref)]` on by default error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:71:35 + --> tests/ui/functions.rs:69:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:74:33 + --> tests/ui/functions.rs:72:33 | LL | unsafe { std::ptr::read(p) }; | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:86:30 + --> tests/ui/functions.rs:84:30 | LL | println!("{}", unsafe { *p }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:89:31 + --> tests/ui/functions.rs:87:31 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:92:29 + --> tests/ui/functions.rs:90:29 | LL | unsafe { std::ptr::read(p) }; | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:99:30 + --> tests/ui/functions.rs:97:30 | LL | println!("{}", unsafe { *p }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:102:31 + --> tests/ui/functions.rs:100:31 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:105:29 + --> tests/ui/functions.rs:103:29 | LL | unsafe { std::ptr::read(p) }; | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:115:34 + --> tests/ui/functions.rs:113:34 | LL | println!("{}", unsafe { *p }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:118:35 + --> tests/ui/functions.rs:116:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:121:33 + --> tests/ui/functions.rs:119:33 | LL | unsafe { std::ptr::read(p) }; | ^ diff --git a/src/tools/clippy/tests/ui/if_not_else.fixed b/src/tools/clippy/tests/ui/if_not_else.fixed index d26a15156cd89..4e6f43e5671e8 100644 --- a/src/tools/clippy/tests/ui/if_not_else.fixed +++ b/src/tools/clippy/tests/ui/if_not_else.fixed @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![warn(clippy::if_not_else)] fn foo() -> bool { diff --git a/src/tools/clippy/tests/ui/if_not_else.rs b/src/tools/clippy/tests/ui/if_not_else.rs index 6171cf1164955..6cd2e3bd63fe9 100644 --- a/src/tools/clippy/tests/ui/if_not_else.rs +++ b/src/tools/clippy/tests/ui/if_not_else.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![warn(clippy::if_not_else)] fn foo() -> bool { diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr index f44dd0aabc863..824837bd52bb1 100644 --- a/src/tools/clippy/tests/ui/if_not_else.stderr +++ b/src/tools/clippy/tests/ui/if_not_else.stderr @@ -1,5 +1,5 @@ error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:12:5 + --> tests/ui/if_not_else.rs:11:5 | LL | / if !bla() { LL | | @@ -24,7 +24,7 @@ LL + } | error: unnecessary `!=` operation - --> tests/ui/if_not_else.rs:19:5 + --> tests/ui/if_not_else.rs:18:5 | LL | / if 4 != 5 { LL | | @@ -47,7 +47,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:34:5 + --> tests/ui/if_not_else.rs:33:5 | LL | / if !(foo() && bla()) { LL | | @@ -79,7 +79,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:53:5 + --> tests/ui/if_not_else.rs:52:5 | LL | / if !foo() { LL | | @@ -102,7 +102,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:61:5 + --> tests/ui/if_not_else.rs:60:5 | LL | / if !bla() { LL | | @@ -125,7 +125,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:72:5 + --> tests/ui/if_not_else.rs:71:5 | LL | / if !foo() { LL | | diff --git a/src/tools/clippy/tests/ui/ignore_without_reason.rs b/src/tools/clippy/tests/ui/ignore_without_reason.rs new file mode 100644 index 0000000000000..53ac34c27248e --- /dev/null +++ b/src/tools/clippy/tests/ui/ignore_without_reason.rs @@ -0,0 +1,14 @@ +#![warn(clippy::ignore_without_reason)] + +fn main() {} + +#[test] +fn unignored_test() {} + +#[test] +#[ignore = "Some good reason"] +fn ignored_with_reason() {} + +#[test] +#[ignore] //~ ignore_without_reason +fn ignored_without_reason() {} diff --git a/src/tools/clippy/tests/ui/ignore_without_reason.stderr b/src/tools/clippy/tests/ui/ignore_without_reason.stderr new file mode 100644 index 0000000000000..4c0210c2bbc08 --- /dev/null +++ b/src/tools/clippy/tests/ui/ignore_without_reason.stderr @@ -0,0 +1,12 @@ +error: `#[ignore]` without reason + --> tests/ui/ignore_without_reason.rs:13:1 + | +LL | #[ignore] + | ^^^^^^^^^ + | + = help: add a reason with `= ".."` + = note: `-D clippy::ignore-without-reason` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ignore_without_reason)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed index 1cb639b60a9af..728c6e015c155 100644 --- a/src/tools/clippy/tests/ui/implicit_return.fixed +++ b/src/tools/clippy/tests/ui/implicit_return.fixed @@ -165,3 +165,46 @@ with_span!( x } ); + +fn desugared_closure_14446() { + let _ = async || return 0; + //~^ implicit_return + #[rustfmt::skip] + let _ = async || -> i32 { return 0 }; + //~^ implicit_return + let _ = async |a: i32| return a; + //~^ implicit_return + #[rustfmt::skip] + let _ = async |a: i32| { return a }; + //~^ implicit_return + + let _ = async || return 0; + let _ = async || -> i32 { return 0 }; + let _ = async |a: i32| return a; + #[rustfmt::skip] + let _ = async |a: i32| { return a; }; + + let _ = async || return foo().await; + //~^ implicit_return + let _ = async || { + foo().await; + return foo().await + }; + //~^^ implicit_return + #[rustfmt::skip] + let _ = async || { return foo().await }; + //~^ implicit_return + let _ = async || -> bool { return foo().await }; + //~^ implicit_return + + let _ = async || return foo().await; + let _ = async || { + foo().await; + return foo().await; + }; + #[rustfmt::skip] + let _ = async || { return foo().await; }; + let _ = async || -> bool { + return foo().await; + }; +} diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs index 99d75e4987e47..3381fffb6e450 100644 --- a/src/tools/clippy/tests/ui/implicit_return.rs +++ b/src/tools/clippy/tests/ui/implicit_return.rs @@ -165,3 +165,46 @@ with_span!( x } ); + +fn desugared_closure_14446() { + let _ = async || 0; + //~^ implicit_return + #[rustfmt::skip] + let _ = async || -> i32 { 0 }; + //~^ implicit_return + let _ = async |a: i32| a; + //~^ implicit_return + #[rustfmt::skip] + let _ = async |a: i32| { a }; + //~^ implicit_return + + let _ = async || return 0; + let _ = async || -> i32 { return 0 }; + let _ = async |a: i32| return a; + #[rustfmt::skip] + let _ = async |a: i32| { return a; }; + + let _ = async || foo().await; + //~^ implicit_return + let _ = async || { + foo().await; + foo().await + }; + //~^^ implicit_return + #[rustfmt::skip] + let _ = async || { foo().await }; + //~^ implicit_return + let _ = async || -> bool { foo().await }; + //~^ implicit_return + + let _ = async || return foo().await; + let _ = async || { + foo().await; + return foo().await; + }; + #[rustfmt::skip] + let _ = async || { return foo().await; }; + let _ = async || -> bool { + return foo().await; + }; +} diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr index 02044df47ac3c..05cd7f62583b1 100644 --- a/src/tools/clippy/tests/ui/implicit_return.stderr +++ b/src/tools/clippy/tests/ui/implicit_return.stderr @@ -183,5 +183,93 @@ help: add `return` as shown LL | return true | ++++++ -error: aborting due to 16 previous errors +error: missing `return` statement + --> tests/ui/implicit_return.rs:170:22 + | +LL | let _ = async || 0; + | ^ + | +help: add `return` as shown + | +LL | let _ = async || return 0; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:173:31 + | +LL | let _ = async || -> i32 { 0 }; + | ^ + | +help: add `return` as shown + | +LL | let _ = async || -> i32 { return 0 }; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:175:28 + | +LL | let _ = async |a: i32| a; + | ^ + | +help: add `return` as shown + | +LL | let _ = async |a: i32| return a; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:178:30 + | +LL | let _ = async |a: i32| { a }; + | ^ + | +help: add `return` as shown + | +LL | let _ = async |a: i32| { return a }; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:187:22 + | +LL | let _ = async || foo().await; + | ^^^^^ + | +help: add `return` as shown + | +LL | let _ = async || return foo().await; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:191:9 + | +LL | foo().await + | ^^^^^ + | +help: add `return` as shown + | +LL | return foo().await + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:195:24 + | +LL | let _ = async || { foo().await }; + | ^^^^^ + | +help: add `return` as shown + | +LL | let _ = async || { return foo().await }; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:197:32 + | +LL | let _ = async || -> bool { foo().await }; + | ^^^^^ + | +help: add `return` as shown + | +LL | let _ = async || -> bool { return foo().await }; + | ++++++ + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed index f036b368a6676..c00d6440f1c6a 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::items_after_test_module)] fn main() {} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs index de0cbb120330e..23d191e3b13b0 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::items_after_test_module)] fn main() {} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr index bed8d4bd5a00c..952489ff5ef9a 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr @@ -1,5 +1,5 @@ error: items after a test module - --> tests/ui/items_after_test_module/root_module.rs:12:1 + --> tests/ui/items_after_test_module/root_module.rs:11:1 | LL | mod tests { | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed index e9fb44e89598e..231fac7cdde7d 100644 --- a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed @@ -29,3 +29,30 @@ fn main() { let _: Vec = v.to_vec(); //~^ iter_cloned_collect } + +mod issue9119 { + + use std::iter; + + #[derive(Clone)] + struct Example(u16); + + impl iter::FromIterator for Vec { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + iter.into_iter().flat_map(|e| e.0.to_le_bytes()).collect() + } + } + + fn foo() { + let examples = [Example(1), Example(0x1234)]; + let encoded: Vec = examples.iter().cloned().collect(); + assert_eq!(encoded, vec![0x01, 0x00, 0x34, 0x12]); + + let a = [&&String::new()]; + let v: Vec<&&String> = a.to_vec(); + //~^ iter_cloned_collect + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs index c9b8abcc9a0d0..e73b6ecae8021 100644 --- a/src/tools/clippy/tests/ui/iter_cloned_collect.rs +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs @@ -33,3 +33,30 @@ fn main() { let _: Vec = v.iter().copied().collect(); //~^ iter_cloned_collect } + +mod issue9119 { + + use std::iter; + + #[derive(Clone)] + struct Example(u16); + + impl iter::FromIterator for Vec { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + iter.into_iter().flat_map(|e| e.0.to_le_bytes()).collect() + } + } + + fn foo() { + let examples = [Example(1), Example(0x1234)]; + let encoded: Vec = examples.iter().cloned().collect(); + assert_eq!(encoded, vec![0x01, 0x00, 0x34, 0x12]); + + let a = [&&String::new()]; + let v: Vec<&&String> = a.iter().cloned().collect(); + //~^ iter_cloned_collect + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr index 119698cb46343..f8a507943270d 100644 --- a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr @@ -36,5 +36,11 @@ error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling LL | let _: Vec = v.iter().copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` -error: aborting due to 5 previous errors +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> tests/ui/iter_cloned_collect.rs:59:33 + | +LL | let v: Vec<&&String> = a.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/iter_kv_map.fixed b/src/tools/clippy/tests/ui/iter_kv_map.fixed index 7fcab6592e26c..874f749b33d02 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.fixed +++ b/src/tools/clippy/tests/ui/iter_kv_map.fixed @@ -166,3 +166,18 @@ fn msrv_1_54() { let _ = map.values().map(|v| v + 2).collect::>(); //~^ iter_kv_map } + +fn issue14595() { + pub struct Foo(BTreeMap); + + impl AsRef> for Foo { + fn as_ref(&self) -> &BTreeMap { + &self.0 + } + } + + let map = Foo(BTreeMap::default()); + + let _ = map.as_ref().values().copied().collect::>(); + //~^ iter_kv_map +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.rs b/src/tools/clippy/tests/ui/iter_kv_map.rs index b590aef7b8031..f570e3c32cb67 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.rs +++ b/src/tools/clippy/tests/ui/iter_kv_map.rs @@ -170,3 +170,18 @@ fn msrv_1_54() { let _ = map.iter().map(|(_, v)| v + 2).collect::>(); //~^ iter_kv_map } + +fn issue14595() { + pub struct Foo(BTreeMap); + + impl AsRef> for Foo { + fn as_ref(&self) -> &BTreeMap { + &self.0 + } + } + + let map = Foo(BTreeMap::default()); + + let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); + //~^ iter_kv_map +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.stderr b/src/tools/clippy/tests/ui/iter_kv_map.stderr index 00d566ed14a28..31ee76c25b7a5 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.stderr +++ b/src/tools/clippy/tests/ui/iter_kv_map.stderr @@ -263,5 +263,11 @@ error: iterating on a map's values LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` -error: aborting due to 38 previous errors +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:185:13 + | +LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` + +error: aborting due to 39 previous errors diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 9999126902903..b0e548f179093 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -59,7 +59,7 @@ fn main() { iter: impl Iterator + 'a, target: String, ) -> impl Iterator + 'a { - iter.filter(move |&(&a, b)| a == 1 && b == &target).cloned() + iter.filter(move |&&(&a, ref b)| a == 1 && b == &target).cloned() //~^ iter_overeager_cloned } diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 6a860dad5afd8..cedf62a6b4730 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -60,7 +60,7 @@ fn main() { iter: impl Iterator + 'a, target: String, ) -> impl Iterator + 'a { - iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) + iter.cloned().filter(move |&(&a, ref b)| a == 1 && b == &target) //~^ iter_overeager_cloned } diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index f3239b59582e3..1616dec95b792 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -120,10 +120,10 @@ LL | let _ = vec.iter().cloned().find(f); error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:63:9 | -LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) - | ^^^^------------------------------------------------------- +LL | iter.cloned().filter(move |&(&a, ref b)| a == 1 && b == &target) + | ^^^^------------------------------------------------------------ | | - | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` + | help: try: `.filter(move |&&(&a, ref b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:75:13 diff --git a/src/tools/clippy/tests/ui/large_futures.fixed b/src/tools/clippy/tests/ui/large_futures.fixed index c2159c58de1ec..4c7215f0abeb0 100644 --- a/src/tools/clippy/tests/ui/large_futures.fixed +++ b/src/tools/clippy/tests/ui/large_futures.fixed @@ -1,7 +1,10 @@ +#![allow( + clippy::future_not_send, + clippy::manual_async_fn, + clippy::never_loop, + clippy::uninlined_format_args +)] #![warn(clippy::large_futures)] -#![allow(clippy::never_loop)] -#![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs index 567f6344afeac..2b5860583f5ec 100644 --- a/src/tools/clippy/tests/ui/large_futures.rs +++ b/src/tools/clippy/tests/ui/large_futures.rs @@ -1,7 +1,10 @@ +#![allow( + clippy::future_not_send, + clippy::manual_async_fn, + clippy::never_loop, + clippy::uninlined_format_args +)] #![warn(clippy::large_futures)] -#![allow(clippy::never_loop)] -#![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr index fd6ba4e3563de..4280c9e2af284 100644 --- a/src/tools/clippy/tests/ui/large_futures.stderr +++ b/src/tools/clippy/tests/ui/large_futures.stderr @@ -1,5 +1,5 @@ error: large future with a size of 16385 bytes - --> tests/ui/large_futures.rs:10:9 + --> tests/ui/large_futures.rs:13:9 | LL | big_fut([0u8; 1024 * 16]).await; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))` @@ -8,37 +8,37 @@ LL | big_fut([0u8; 1024 * 16]).await; = help: to override `-D warnings` add `#[allow(clippy::large_futures)]` error: large future with a size of 16386 bytes - --> tests/ui/large_futures.rs:13:5 + --> tests/ui/large_futures.rs:16:5 | LL | f.await | ^ help: consider `Box::pin` on it: `Box::pin(f)` error: large future with a size of 16387 bytes - --> tests/ui/large_futures.rs:18:9 + --> tests/ui/large_futures.rs:21:9 | LL | wait().await; | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` error: large future with a size of 16387 bytes - --> tests/ui/large_futures.rs:24:13 + --> tests/ui/large_futures.rs:27:13 | LL | wait().await; | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:32:5 + --> tests/ui/large_futures.rs:35:5 | LL | foo().await; | ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())` error: large future with a size of 49159 bytes - --> tests/ui/large_futures.rs:35:5 + --> tests/ui/large_futures.rs:38:5 | LL | calls_fut(fut).await; | ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))` error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:48:5 + --> tests/ui/large_futures.rs:51:5 | LL | / async { LL | | @@ -61,7 +61,7 @@ LL + }) | error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:61:13 + --> tests/ui/large_futures.rs:64:13 | LL | / async { LL | | diff --git a/src/tools/clippy/tests/ui/len_without_is_empty_expect.rs b/src/tools/clippy/tests/ui/len_without_is_empty_expect.rs new file mode 100644 index 0000000000000..9d1245e2d02ad --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty_expect.rs @@ -0,0 +1,28 @@ +//@no-rustfix +#![allow(clippy::len_without_is_empty)] + +// Check that the lint expectation is fulfilled even if the lint is allowed at the type level. +pub struct Empty; + +impl Empty { + #[expect(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + 0 + } +} + +// Check that the lint expectation is not triggered if it should not +pub struct Empty2; + +impl Empty2 { + #[expect(clippy::len_without_is_empty)] //~ ERROR: this lint expectation is unfulfilled + pub fn len(&self) -> usize { + 0 + } + + pub fn is_empty(&self) -> bool { + false + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/len_without_is_empty_expect.stderr b/src/tools/clippy/tests/ui/len_without_is_empty_expect.stderr new file mode 100644 index 0000000000000..e96870f054e43 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty_expect.stderr @@ -0,0 +1,11 @@ +error: this lint expectation is unfulfilled + --> tests/ui/len_without_is_empty_expect.rs:18:14 + | +LL | #[expect(clippy::len_without_is_empty)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.fixed b/src/tools/clippy/tests/ui/manual_abs_diff.fixed new file mode 100644 index 0000000000000..f1b1278ea6d22 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_abs_diff.fixed @@ -0,0 +1,106 @@ +#![warn(clippy::manual_abs_diff)] + +use std::time::Duration; + +fn main() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + let _ = a.abs_diff(b); + //~^ manual_abs_diff + let _ = b.abs_diff(a); + //~^ manual_abs_diff + + let _ = b.abs_diff(5); + //~^ manual_abs_diff + let _ = b.abs_diff(5); + //~^ manual_abs_diff + + let _ = a.abs_diff(b); + //~^ manual_abs_diff + let _ = b.abs_diff(a); + //~^ manual_abs_diff + + #[allow(arithmetic_overflow)] + { + let _ = if a > b { b - a } else { a - b }; + let _ = if a < b { a - b } else { b - a }; + } + + let _ = (a + b).abs_diff(c + d); + let _ = (c + d).abs_diff(a + b); + + const A: usize = 5; + const B: usize = 3; + // check const context + const _: usize = A.abs_diff(B); + //~^ manual_abs_diff + + let a = Duration::from_secs(3); + let b = Duration::from_secs(5); + let _ = a.abs_diff(b); + //~^ manual_abs_diff + + let a: i32 = 3; + let b: i32 = -5; + let _ = if a > b { a - b } else { b - a }; + let _ = a.abs_diff(b); + //~^ manual_abs_diff +} + +// FIXME: bunch of patterns that should be linted +fn fixme() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + { + let out; + if a > b { + out = a - b; + } else { + out = b - a; + } + } + + { + let mut out = 0; + if a > b { + out = a - b; + } else if a < b { + out = b - a; + } + } + + #[allow(clippy::implicit_saturating_sub)] + let _ = if a > b { + a - b + } else if a < b { + b - a + } else { + 0 + }; + + let a: i32 = 3; + let b: i32 = 5; + let _: u32 = if a > b { a - b } else { b - a } as u32; +} + +fn non_primitive_ty() { + #[derive(Eq, PartialEq, PartialOrd)] + struct S(i32); + + impl std::ops::Sub for S { + type Output = S; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } + } + + let (a, b) = (S(10), S(20)); + let _ = if a < b { b - a } else { a - b }; +} diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.rs b/src/tools/clippy/tests/ui/manual_abs_diff.rs new file mode 100644 index 0000000000000..60ef819c12d30 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_abs_diff.rs @@ -0,0 +1,116 @@ +#![warn(clippy::manual_abs_diff)] + +use std::time::Duration; + +fn main() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + let _ = if a > b { a - b } else { b - a }; + //~^ manual_abs_diff + let _ = if a < b { b - a } else { a - b }; + //~^ manual_abs_diff + + let _ = if 5 > b { 5 - b } else { b - 5 }; + //~^ manual_abs_diff + let _ = if b > 5 { b - 5 } else { 5 - b }; + //~^ manual_abs_diff + + let _ = if a >= b { a - b } else { b - a }; + //~^ manual_abs_diff + let _ = if a <= b { b - a } else { a - b }; + //~^ manual_abs_diff + + #[allow(arithmetic_overflow)] + { + let _ = if a > b { b - a } else { a - b }; + let _ = if a < b { a - b } else { b - a }; + } + + let _ = if (a + b) > (c + d) { + //~^ manual_abs_diff + (a + b) - (c + d) + } else { + (c + d) - (a + b) + }; + let _ = if (a + b) < (c + d) { + //~^ manual_abs_diff + (c + d) - (a + b) + } else { + (a + b) - (c + d) + }; + + const A: usize = 5; + const B: usize = 3; + // check const context + const _: usize = if A > B { A - B } else { B - A }; + //~^ manual_abs_diff + + let a = Duration::from_secs(3); + let b = Duration::from_secs(5); + let _ = if a > b { a - b } else { b - a }; + //~^ manual_abs_diff + + let a: i32 = 3; + let b: i32 = -5; + let _ = if a > b { a - b } else { b - a }; + let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; + //~^ manual_abs_diff +} + +// FIXME: bunch of patterns that should be linted +fn fixme() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + { + let out; + if a > b { + out = a - b; + } else { + out = b - a; + } + } + + { + let mut out = 0; + if a > b { + out = a - b; + } else if a < b { + out = b - a; + } + } + + #[allow(clippy::implicit_saturating_sub)] + let _ = if a > b { + a - b + } else if a < b { + b - a + } else { + 0 + }; + + let a: i32 = 3; + let b: i32 = 5; + let _: u32 = if a > b { a - b } else { b - a } as u32; +} + +fn non_primitive_ty() { + #[derive(Eq, PartialEq, PartialOrd)] + struct S(i32); + + impl std::ops::Sub for S { + type Output = S; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } + } + + let (a, b) = (S(10), S(20)); + let _ = if a < b { b - a } else { a - b }; +} diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.stderr b/src/tools/clippy/tests/ui/manual_abs_diff.stderr new file mode 100644 index 0000000000000..c14c1dc830fbd --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_abs_diff.stderr @@ -0,0 +1,83 @@ +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:11:13 + | +LL | let _ = if a > b { a - b } else { b - a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + | + = note: `-D clippy::manual-abs-diff` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_abs_diff)]` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:13:13 + | +LL | let _ = if a < b { b - a } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(a)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:16:13 + | +LL | let _ = if 5 > b { 5 - b } else { b - 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(5)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:18:13 + | +LL | let _ = if b > 5 { b - 5 } else { 5 - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(5)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:21:13 + | +LL | let _ = if a >= b { a - b } else { b - a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:23:13 + | +LL | let _ = if a <= b { b - a } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(a)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:32:13 + | +LL | let _ = if (a + b) > (c + d) { + | _____________^ +LL | | +LL | | (a + b) - (c + d) +LL | | } else { +LL | | (c + d) - (a + b) +LL | | }; + | |_____^ help: replace with `abs_diff`: `(a + b).abs_diff(c + d)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:38:13 + | +LL | let _ = if (a + b) < (c + d) { + | _____________^ +LL | | +LL | | (c + d) - (a + b) +LL | | } else { +LL | | (a + b) - (c + d) +LL | | }; + | |_____^ help: replace with `abs_diff`: `(c + d).abs_diff(a + b)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:48:22 + | +LL | const _: usize = if A > B { A - B } else { B - A }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `A.abs_diff(B)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:53:13 + | +LL | let _ = if a > b { a - b } else { b - a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:59:13 + | +LL | let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed index ad0266d39e982..a284ca9f62530 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.fixed +++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed @@ -75,7 +75,7 @@ impl S { async fn elided(_: &i32) -> i32 { 42 } // should be ignored -fn elided_not_bound(_: &i32) -> impl Future { +fn elided_not_bound(_: &i32) -> impl Future + use<> { async { 42 } } @@ -84,7 +84,7 @@ async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } // should be ignored #[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + use<> { async { 42 } } @@ -94,7 +94,7 @@ mod issue_5765 { struct A; impl A { - fn f(&self) -> impl Future { + fn f(&self) -> impl Future + use<> { async {} } } diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs index fe367b4bc7b9b..188f8a4982c36 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.rs +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -102,7 +102,7 @@ fn elided(_: &i32) -> impl Future + '_ { } // should be ignored -fn elided_not_bound(_: &i32) -> impl Future { +fn elided_not_bound(_: &i32) -> impl Future + use<> { async { 42 } } @@ -114,7 +114,7 @@ fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + // should be ignored #[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + use<> { async { 42 } } @@ -124,7 +124,7 @@ mod issue_5765 { struct A; impl A { - fn f(&self) -> impl Future { + fn f(&self) -> impl Future + use<> { async {} } } diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed b/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed new file mode 100644 index 0000000000000..b6afe7898906c --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = std::ptr::dangling(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling_mut::(); + //~^ manual_dangling_ptr + + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.rs b/src/tools/clippy/tests/ui/manual_dangling_ptr.rs new file mode 100644 index 0000000000000..581ad50113e28 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.rs @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = 1 as *const _; + //~^ manual_dangling_ptr + let _ = 2 as *const u32; + //~^ manual_dangling_ptr + let _ = 4 as *mut f32; + //~^ manual_dangling_ptr + + let _ = mem::align_of::() as *const u8; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const u32; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const usize; + //~^ manual_dangling_ptr + + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr b/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr new file mode 100644 index 0000000000000..e3bc9b16b0d93 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr @@ -0,0 +1,65 @@ +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:7:24 + | +LL | let _: *const u8 = 1 as *const _; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + | + = note: `-D clippy::manual-dangling-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:9:13 + | +LL | let _ = 2 as *const u32; + | ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:11:13 + | +LL | let _ = 4 as *mut f32; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:14:13 + | +LL | let _ = mem::align_of::() as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:16:13 + | +LL | let _ = mem::align_of::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:18:13 + | +LL | let _ = mem::align_of::() as *const usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_find.rs b/src/tools/clippy/tests/ui/manual_find.rs index 20b557f21d141..7b9846cfe429d 100644 --- a/src/tools/clippy/tests/ui/manual_find.rs +++ b/src/tools/clippy/tests/ui/manual_find.rs @@ -23,4 +23,32 @@ fn tuple(arr: Vec<(String, i32)>) -> Option { None } +mod issue9521 { + fn condition(x: u32, y: u32) -> Result { + todo!() + } + + fn find_with_early_return(v: Vec) -> Option { + for x in v { + if condition(x, 10).ok()? { + return Some(x); + } + } + None + } + + fn find_with_early_break(v: Vec) -> Option { + for x in v { + if if x < 3 { + break; + } else { + x < 10 + } { + return Some(x); + } + } + None + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed index c1c929585cfd3..cd7adc20b127e 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed @@ -1,5 +1,11 @@ -#![allow(clippy::all)] -#![deny(clippy::manual_ignore_case_cmp)] +#![warn(clippy::manual_ignore_case_cmp)] +#![allow( + clippy::deref_addrof, + clippy::op_ref, + clippy::ptr_arg, + clippy::short_circuit_statement, + clippy::unnecessary_operation +)] use std::ffi::{OsStr, OsString}; diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs index ca401e595fe97..85f6719827c93 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs @@ -1,5 +1,11 @@ -#![allow(clippy::all)] -#![deny(clippy::manual_ignore_case_cmp)] +#![warn(clippy::manual_ignore_case_cmp)] +#![allow( + clippy::deref_addrof, + clippy::op_ref, + clippy::ptr_arg, + clippy::short_circuit_statement, + clippy::unnecessary_operation +)] use std::ffi::{OsStr, OsString}; diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr index 47378a65799fc..fa7fadd910760 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr @@ -1,14 +1,11 @@ error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:9:8 + --> tests/ui/manual_ignore_case_cmp.rs:15:8 | LL | if a.to_ascii_lowercase() == b.to_ascii_lowercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/manual_ignore_case_cmp.rs:2:9 - | -LL | #![deny(clippy::manual_ignore_case_cmp)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::manual-ignore-case-cmp` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_ignore_case_cmp)]` help: consider using `.eq_ignore_ascii_case()` instead | LL - if a.to_ascii_lowercase() == b.to_ascii_lowercase() { @@ -16,7 +13,7 @@ LL + if a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:13:8 + --> tests/ui/manual_ignore_case_cmp.rs:19:8 | LL | if a.to_ascii_uppercase() == b.to_ascii_uppercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +25,7 @@ LL + if a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:17:13 + --> tests/ui/manual_ignore_case_cmp.rs:23:13 | LL | let r = a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +37,7 @@ LL + let r = a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:19:18 + --> tests/ui/manual_ignore_case_cmp.rs:25:18 | LL | let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +49,7 @@ LL + let r = r || a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:21:10 + --> tests/ui/manual_ignore_case_cmp.rs:27:10 | LL | r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +61,7 @@ LL + r && a.eq_ignore_ascii_case(&b.to_uppercase()); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:24:8 + --> tests/ui/manual_ignore_case_cmp.rs:30:8 | LL | if a.to_ascii_lowercase() != b.to_ascii_lowercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,7 +73,7 @@ LL + if !a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:28:8 + --> tests/ui/manual_ignore_case_cmp.rs:34:8 | LL | if a.to_ascii_uppercase() != b.to_ascii_uppercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +85,7 @@ LL + if !a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:32:13 + --> tests/ui/manual_ignore_case_cmp.rs:38:13 | LL | let r = a.to_ascii_lowercase() != b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +97,7 @@ LL + let r = !a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:34:18 + --> tests/ui/manual_ignore_case_cmp.rs:40:18 | LL | let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +109,7 @@ LL + let r = r || !a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:36:10 + --> tests/ui/manual_ignore_case_cmp.rs:42:10 | LL | r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +121,7 @@ LL + r && !a.eq_ignore_ascii_case(&b.to_uppercase()); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:48:5 + --> tests/ui/manual_ignore_case_cmp.rs:54:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +133,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:52:5 + --> tests/ui/manual_ignore_case_cmp.rs:58:5 | LL | a.to_ascii_lowercase() == 'a'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,7 +145,7 @@ LL + a.eq_ignore_ascii_case(&'a'); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:54:5 + --> tests/ui/manual_ignore_case_cmp.rs:60:5 | LL | 'a' == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,7 +157,7 @@ LL + 'a'.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:58:5 + --> tests/ui/manual_ignore_case_cmp.rs:64:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -172,7 +169,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:60:5 + --> tests/ui/manual_ignore_case_cmp.rs:66:5 | LL | a.to_ascii_lowercase() == b'a'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -184,7 +181,7 @@ LL + a.eq_ignore_ascii_case(&b'a'); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:62:5 + --> tests/ui/manual_ignore_case_cmp.rs:68:5 | LL | b'a' == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -196,7 +193,7 @@ LL + b'a'.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:66:5 + --> tests/ui/manual_ignore_case_cmp.rs:72:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -208,7 +205,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:68:5 + --> tests/ui/manual_ignore_case_cmp.rs:74:5 | LL | a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +217,7 @@ LL + a.to_uppercase().eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:70:5 + --> tests/ui/manual_ignore_case_cmp.rs:76:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,7 +229,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:72:5 + --> tests/ui/manual_ignore_case_cmp.rs:78:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +241,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:76:5 + --> tests/ui/manual_ignore_case_cmp.rs:82:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +253,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:78:5 + --> tests/ui/manual_ignore_case_cmp.rs:84:5 | LL | a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +265,7 @@ LL + a.to_uppercase().eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:80:5 + --> tests/ui/manual_ignore_case_cmp.rs:86:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -280,7 +277,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:82:5 + --> tests/ui/manual_ignore_case_cmp.rs:88:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -292,7 +289,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:86:5 + --> tests/ui/manual_ignore_case_cmp.rs:92:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +301,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:88:5 + --> tests/ui/manual_ignore_case_cmp.rs:94:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -316,7 +313,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:90:5 + --> tests/ui/manual_ignore_case_cmp.rs:96:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -328,7 +325,7 @@ LL + "a".eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:94:5 + --> tests/ui/manual_ignore_case_cmp.rs:100:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -340,7 +337,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:96:5 + --> tests/ui/manual_ignore_case_cmp.rs:102:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -352,7 +349,7 @@ LL + "a".eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:100:5 + --> tests/ui/manual_ignore_case_cmp.rs:106:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -364,7 +361,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:102:5 + --> tests/ui/manual_ignore_case_cmp.rs:108:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +373,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:104:5 + --> tests/ui/manual_ignore_case_cmp.rs:110:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -388,7 +385,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:107:5 + --> tests/ui/manual_ignore_case_cmp.rs:113:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -400,7 +397,7 @@ LL + b.eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:109:5 + --> tests/ui/manual_ignore_case_cmp.rs:115:5 | LL | b.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -412,7 +409,7 @@ LL + b.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:111:5 + --> tests/ui/manual_ignore_case_cmp.rs:117:5 | LL | "a" == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -424,7 +421,7 @@ LL + "a".eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:115:5 + --> tests/ui/manual_ignore_case_cmp.rs:121:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -436,7 +433,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:117:5 + --> tests/ui/manual_ignore_case_cmp.rs:123:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -448,7 +445,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:119:5 + --> tests/ui/manual_ignore_case_cmp.rs:125:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -460,7 +457,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:122:5 + --> tests/ui/manual_ignore_case_cmp.rs:128:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -472,7 +469,7 @@ LL + b.eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:124:5 + --> tests/ui/manual_ignore_case_cmp.rs:130:5 | LL | b.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -484,7 +481,7 @@ LL + b.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:126:5 + --> tests/ui/manual_ignore_case_cmp.rs:132:5 | LL | "a" == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,7 +493,7 @@ LL + "a".eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:130:5 + --> tests/ui/manual_ignore_case_cmp.rs:136:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -508,7 +505,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:134:5 + --> tests/ui/manual_ignore_case_cmp.rs:140:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -520,7 +517,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:138:5 + --> tests/ui/manual_ignore_case_cmp.rs:144:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +529,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:140:5 + --> tests/ui/manual_ignore_case_cmp.rs:146:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -544,7 +541,7 @@ LL + b.eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:144:5 + --> tests/ui/manual_ignore_case_cmp.rs:150:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -556,7 +553,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:148:5 + --> tests/ui/manual_ignore_case_cmp.rs:154:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -568,7 +565,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:152:5 + --> tests/ui/manual_ignore_case_cmp.rs:158:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +577,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:154:5 + --> tests/ui/manual_ignore_case_cmp.rs:160:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed index 44f15d61f8563..ec87fe217aee6 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.fixed +++ b/src/tools/clippy/tests/ui/manual_inspect.fixed @@ -1,5 +1,5 @@ +#![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] #![warn(clippy::manual_inspect)] -#![allow(clippy::no_effect, clippy::op_ref)] fn main() { let _ = Some(0).inspect(|&x| { @@ -107,7 +107,7 @@ fn main() { let _ = || { let _x = x; }; - return; + return ; } println!("test"); }); @@ -185,3 +185,12 @@ fn main() { }); } } + +#[rustfmt::skip] +fn layout_check() { + if let Some(x) = Some(1).inspect(|&x| { println!("{x}"); //~ manual_inspect + // Do not collapse code into this comment + }) { + println!("{x}"); + } +} diff --git a/src/tools/clippy/tests/ui/manual_inspect.rs b/src/tools/clippy/tests/ui/manual_inspect.rs index d34f2abce6ae1..e679636201e6a 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.rs +++ b/src/tools/clippy/tests/ui/manual_inspect.rs @@ -1,5 +1,5 @@ +#![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] #![warn(clippy::manual_inspect)] -#![allow(clippy::no_effect, clippy::op_ref)] fn main() { let _ = Some(0).map(|x| { @@ -197,3 +197,12 @@ fn main() { }); } } + +#[rustfmt::skip] +fn layout_check() { + if let Some(x) = Some(1).map(|x| { println!("{x}"); //~ manual_inspect + // Do not collapse code into this comment + x }) { + println!("{x}"); + } +} diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr index 510325d2baaa9..eb98f9f5995a3 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.stderr +++ b/src/tools/clippy/tests/ui/manual_inspect.stderr @@ -98,7 +98,7 @@ LL | if x.is_empty() { LL | let _ = || { LL ~ let _x = x; LL | }; -LL ~ return; +LL ~ return ; LL | } LL ~ println!("test"); | @@ -187,5 +187,18 @@ LL | LL ~ println!("{}", x); | -error: aborting due to 13 previous errors +error: using `map` over `inspect` + --> tests/ui/manual_inspect.rs:203:30 + | +LL | if let Some(x) = Some(1).map(|x| { println!("{x}"); + | ^^^ + | +help: try + | +LL ~ if let Some(x) = Some(1).inspect(|&x| { println!("{x}"); +LL | // Do not collapse code into this comment +LL ~ }) { + | + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed index 6f29d76bd2109..8a1ab785dfbfd 100644 --- a/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed @@ -1,4 +1,17 @@ #![warn(clippy::manual_is_power_of_two)] +#![allow(clippy::precedence)] + +macro_rules! binop { + ($a: expr, equal, $b: expr) => { + $a == $b + }; + ($a: expr, and, $b: expr) => { + $a & $b + }; + ($a: expr, minus, $b: expr) => { + $a - $b + }; +} fn main() { let a = 16_u64; @@ -7,6 +20,8 @@ fn main() { //~^ manual_is_power_of_two let _ = a.is_power_of_two(); //~^ manual_is_power_of_two + let _ = a.is_power_of_two(); + //~^ manual_is_power_of_two // Test different orders of expression let _ = a.is_power_of_two(); @@ -23,4 +38,23 @@ fn main() { // is_power_of_two only works for unsigned integers let _ = b.count_ones() == 1; let _ = b & (b - 1) == 0; + + let i: i32 = 3; + let _ = (i as u32).is_power_of_two(); + //~^ manual_is_power_of_two + + let _ = binop!(a.count_ones(), equal, 1); + let _ = binop!(a, and, a - 1) == 0; + let _ = a & binop!(a, minus, 1) == 0; +} + +#[clippy::msrv = "1.31"] +const fn low_msrv(a: u32) -> bool { + a & (a - 1) == 0 +} + +#[clippy::msrv = "1.32"] +const fn high_msrv(a: u32) -> bool { + a.is_power_of_two() + //~^ manual_is_power_of_two } diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.rs b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs index 0c44d7a660b43..57a3b05e0336a 100644 --- a/src/tools/clippy/tests/ui/manual_is_power_of_two.rs +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs @@ -1,10 +1,25 @@ #![warn(clippy::manual_is_power_of_two)] +#![allow(clippy::precedence)] + +macro_rules! binop { + ($a: expr, equal, $b: expr) => { + $a == $b + }; + ($a: expr, and, $b: expr) => { + $a & $b + }; + ($a: expr, minus, $b: expr) => { + $a - $b + }; +} fn main() { let a = 16_u64; let _ = a.count_ones() == 1; //~^ manual_is_power_of_two + let _ = u64::count_ones(a) == 1; + //~^ manual_is_power_of_two let _ = a & (a - 1) == 0; //~^ manual_is_power_of_two @@ -23,4 +38,23 @@ fn main() { // is_power_of_two only works for unsigned integers let _ = b.count_ones() == 1; let _ = b & (b - 1) == 0; + + let i: i32 = 3; + let _ = i as u32 & (i as u32 - 1) == 0; + //~^ manual_is_power_of_two + + let _ = binop!(a.count_ones(), equal, 1); + let _ = binop!(a, and, a - 1) == 0; + let _ = a & binop!(a, minus, 1) == 0; +} + +#[clippy::msrv = "1.31"] +const fn low_msrv(a: u32) -> bool { + a & (a - 1) == 0 +} + +#[clippy::msrv = "1.32"] +const fn high_msrv(a: u32) -> bool { + a & (a - 1) == 0 + //~^ manual_is_power_of_two } diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr index ad12ee10565f6..5781a093d5f2b 100644 --- a/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:6:13 + --> tests/ui/manual_is_power_of_two.rs:19:13 | LL | let _ = a.count_ones() == 1; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` @@ -8,34 +8,52 @@ LL | let _ = a.count_ones() == 1; = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:8:13 + --> tests/ui/manual_is_power_of_two.rs:21:13 + | +LL | let _ = u64::count_ones(a) == 1; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:23:13 | LL | let _ = a & (a - 1) == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:12:13 + --> tests/ui/manual_is_power_of_two.rs:27:13 | LL | let _ = 1 == a.count_ones(); | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:14:13 + --> tests/ui/manual_is_power_of_two.rs:29:13 | LL | let _ = (a - 1) & a == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:16:13 + --> tests/ui/manual_is_power_of_two.rs:31:13 | LL | let _ = 0 == a & (a - 1); | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:18:13 + --> tests/ui/manual_is_power_of_two.rs:33:13 | LL | let _ = 0 == (a - 1) & a; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` -error: aborting due to 6 previous errors +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:43:13 + | +LL | let _ = i as u32 & (i as u32 - 1) == 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `(i as u32).is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:58:5 + | +LL | a & (a - 1) == 0 + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/manual_map_option.rs b/src/tools/clippy/tests/ui/manual_map_option.rs index 9477d0d795d2d..40133748d4599 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.rs +++ b/src/tools/clippy/tests/ui/manual_map_option.rs @@ -101,7 +101,7 @@ fn main() { match &mut Some(String::new()) { //~^ manual_map - Some(ref x) => Some(x.len()), + &mut Some(ref x) => Some(x.len()), None => None, }; diff --git a/src/tools/clippy/tests/ui/manual_map_option.stderr b/src/tools/clippy/tests/ui/manual_map_option.stderr index 8f9bce4c265c9..486379c1e5f33 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.stderr +++ b/src/tools/clippy/tests/ui/manual_map_option.stderr @@ -127,7 +127,7 @@ error: manual implementation of `Option::map` | LL | / match &mut Some(String::new()) { LL | | -LL | | Some(ref x) => Some(x.len()), +LL | | &mut Some(ref x) => Some(x.len()), LL | | None => None, LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.fixed b/src/tools/clippy/tests/ui/manual_map_option_2.fixed index d698cc74ea65a..206c6d5d07763 100644 --- a/src/tools/clippy/tests/ui/manual_map_option_2.fixed +++ b/src/tools/clippy/tests/ui/manual_map_option_2.fixed @@ -115,7 +115,7 @@ mod with_type_coercion { fn with_fn_ret(s: &Option) -> Option<(String, &str)> { // Don't lint, `map` doesn't work as the return type is adjusted. match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } } @@ -124,7 +124,7 @@ mod with_type_coercion { if true { // Don't lint, `map` doesn't work as the return type is adjusted. return match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, }; } @@ -136,7 +136,7 @@ mod with_type_coercion { let x: Option<(String, &'a str)>; x = { match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } }; diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.rs b/src/tools/clippy/tests/ui/manual_map_option_2.rs index 069c2381f6db1..a47dc950760e2 100644 --- a/src/tools/clippy/tests/ui/manual_map_option_2.rs +++ b/src/tools/clippy/tests/ui/manual_map_option_2.rs @@ -143,7 +143,7 @@ mod with_type_coercion { fn with_fn_ret(s: &Option) -> Option<(String, &str)> { // Don't lint, `map` doesn't work as the return type is adjusted. match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } } @@ -152,7 +152,7 @@ mod with_type_coercion { if true { // Don't lint, `map` doesn't work as the return type is adjusted. return match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, }; } @@ -164,7 +164,7 @@ mod with_type_coercion { let x: Option<(String, &'a str)>; x = { match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } }; diff --git a/src/tools/clippy/tests/ui/manual_ok_err.fixed b/src/tools/clippy/tests/ui/manual_ok_err.fixed index bc169b64be9fd..e6f799aa58d61 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.fixed +++ b/src/tools/clippy/tests/ui/manual_ok_err.fixed @@ -80,6 +80,11 @@ fn no_lint() { Ok(3) => None, Ok(v) => Some(v), }; + + let _ = match funcall() { + Ok(v @ 1..) => Some(v), + _ => None, + }; } const fn cf(x: Result) -> Option { diff --git a/src/tools/clippy/tests/ui/manual_ok_err.rs b/src/tools/clippy/tests/ui/manual_ok_err.rs index 03c730d4b4e46..972b2c41ee7aa 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.rs +++ b/src/tools/clippy/tests/ui/manual_ok_err.rs @@ -116,6 +116,11 @@ fn no_lint() { Ok(3) => None, Ok(v) => Some(v), }; + + let _ = match funcall() { + Ok(v @ 1..) => Some(v), + _ => None, + }; } const fn cf(x: Result) -> Option { diff --git a/src/tools/clippy/tests/ui/manual_ok_err.stderr b/src/tools/clippy/tests/ui/manual_ok_err.stderr index 13fceacda1074..040e170f397e2 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.stderr +++ b/src/tools/clippy/tests/ui/manual_ok_err.stderr @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: replace with: `(-S).ok()` error: manual implementation of `ok` - --> tests/ui/manual_ok_err.rs:132:12 + --> tests/ui/manual_ok_err.rs:137:12 | LL | } else if let Ok(n) = "1".parse::() { | ____________^ diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed index ca8491131c06c..016f520e216c0 100644 --- a/src/tools/clippy/tests/ui/manual_retain.fixed +++ b/src/tools/clippy/tests/ui/manual_retain.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_retain)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { @@ -31,7 +31,7 @@ fn binary_heap_retain() { // Do lint, because we use pattern matching let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -99,7 +99,7 @@ fn btree_set_retain() { // Do lint, because we use pattern matching let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -166,7 +166,7 @@ fn hash_set_retain() { // Do lint, because we use pattern matching let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -220,7 +220,7 @@ fn vec_retain() { // Do lint, because we use pattern matching let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs index cd05a41f3f25a..62f9b7b0595d0 100644 --- a/src/tools/clippy/tests/ui/manual_retain.rs +++ b/src/tools/clippy/tests/ui/manual_retain.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_retain)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { @@ -31,7 +31,7 @@ fn binary_heap_retain() { // Do lint, because we use pattern matching let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -103,7 +103,7 @@ fn btree_set_retain() { // Do lint, because we use pattern matching let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -174,7 +174,7 @@ fn hash_set_retain() { // Do lint, because we use pattern matching let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -228,7 +228,7 @@ fn vec_retain() { // Do lint, because we use pattern matching let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr index 2f81647dd8b7b..e7d3e34b5d7d4 100644 --- a/src/tools/clippy/tests/ui/manual_retain.stderr +++ b/src/tools/clippy/tests/ui/manual_retain.stderr @@ -22,8 +22,8 @@ LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().colle error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:34:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:36:5 @@ -74,8 +74,8 @@ LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:106:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:108:5 @@ -126,8 +126,8 @@ LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:177:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:179:5 @@ -162,8 +162,8 @@ LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:231:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:233:5 diff --git a/src/tools/clippy/tests/ui/manual_strip_fixable.fixed b/src/tools/clippy/tests/ui/manual_strip_fixable.fixed index 75a3f1645de33..b59e3719d951d 100644 --- a/src/tools/clippy/tests/ui/manual_strip_fixable.fixed +++ b/src/tools/clippy/tests/ui/manual_strip_fixable.fixed @@ -1,4 +1,5 @@ #![warn(clippy::manual_strip)] +#![allow(clippy::uninlined_format_args)] fn main() { let s = "abc"; diff --git a/src/tools/clippy/tests/ui/manual_strip_fixable.rs b/src/tools/clippy/tests/ui/manual_strip_fixable.rs index 5080068449e20..4fb3a9bf007f6 100644 --- a/src/tools/clippy/tests/ui/manual_strip_fixable.rs +++ b/src/tools/clippy/tests/ui/manual_strip_fixable.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_strip)] +#![allow(clippy::uninlined_format_args)] fn main() { let s = "abc"; diff --git a/src/tools/clippy/tests/ui/manual_strip_fixable.stderr b/src/tools/clippy/tests/ui/manual_strip_fixable.stderr index 1c276e5d8fdfe..da8b0cd08f893 100644 --- a/src/tools/clippy/tests/ui/manual_strip_fixable.stderr +++ b/src/tools/clippy/tests/ui/manual_strip_fixable.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> tests/ui/manual_strip_fixable.rs:7:24 + --> tests/ui/manual_strip_fixable.rs:8:24 | LL | let stripped = &s["ab".len()..]; | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> tests/ui/manual_strip_fixable.rs:6:5 + --> tests/ui/manual_strip_fixable.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,13 +19,13 @@ LL ~ println!("{stripped}{}", stripped); | error: stripping a suffix manually - --> tests/ui/manual_strip_fixable.rs:13:24 + --> tests/ui/manual_strip_fixable.rs:14:24 | LL | let stripped = &s[..s.len() - "bc".len()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> tests/ui/manual_strip_fixable.rs:12:5 + --> tests/ui/manual_strip_fixable.rs:13:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index 07e4bdd483a8c..e12287a709395 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -18,11 +18,9 @@ fn option_unwrap_or() { // multiline case #[rustfmt::skip] - Some(1).unwrap_or({ - 42 + 42 - + 42 + 42 + 42 - + 42 + 42 + 42 - }); + Some(1).unwrap_or(42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42); // string case Some("Bob").unwrap_or("Alice"); @@ -125,11 +123,9 @@ fn result_unwrap_or() { // multiline case #[rustfmt::skip] - Ok::(1).unwrap_or({ - 42 + 42 - + 42 + 42 + 42 - + 42 + 42 + 42 - }); + Ok::(1).unwrap_or(42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42); // string case Ok::<&str, &str>("Bob").unwrap_or("Alice"); @@ -159,11 +155,7 @@ fn result_unwrap_or() { Ok(s) => s, Err(s) => s, }; - // could lint, but unused_variables takes care of it - match Ok::<&str, &str>("Alice") { - Ok(s) => s, - Err(s) => "Bob", - }; + Ok::<&str, &str>("Alice").unwrap_or("Bob"); Ok::(1).unwrap_or(42); @@ -250,4 +242,12 @@ mod issue_13018 { } } +fn implicit_deref(v: Vec) { + let _ = if let Some(s) = v.first() { s } else { "" }; +} + +fn allowed_manual_unwrap_or_zero() -> u32 { + Some(42).unwrap_or(0) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index c88b6f95da68e..53cffcab5b56c 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -216,8 +216,8 @@ fn result_unwrap_or() { Ok(s) => s, Err(s) => s, }; - // could lint, but unused_variables takes care of it match Ok::<&str, &str>("Alice") { + //~^ manual_unwrap_or Ok(s) => s, Err(s) => "Bob", }; @@ -316,4 +316,17 @@ mod issue_13018 { } } +fn implicit_deref(v: Vec) { + let _ = if let Some(s) = v.first() { s } else { "" }; +} + +fn allowed_manual_unwrap_or_zero() -> u32 { + if let Some(x) = Some(42) { + //~^ manual_unwrap_or + x + } else { + 0 + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr index a5deb55786e96..320e895fb8237 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr @@ -44,11 +44,9 @@ LL | | }; | help: replace with | -LL ~ Some(1).unwrap_or({ -LL + 42 + 42 -LL + + 42 + 42 + 42 -LL + + 42 + 42 + 42 -LL ~ }); +LL ~ Some(1).unwrap_or(42 + 42 +LL + + 42 + 42 + 42 +LL ~ + 42 + 42 + 42); | error: this pattern reimplements `Option::unwrap_or` @@ -145,11 +143,9 @@ LL | | }; | help: replace with | -LL ~ Ok::(1).unwrap_or({ -LL + 42 + 42 -LL + + 42 + 42 + 42 -LL + + 42 + 42 + 42 -LL ~ }); +LL ~ Ok::(1).unwrap_or(42 + 42 +LL + + 42 + 42 + 42 +LL ~ + 42 + 42 + 42); | error: this pattern reimplements `Result::unwrap_or` @@ -162,6 +158,16 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:219:5 + | +LL | / match Ok::<&str, &str>("Alice") { +LL | | +LL | | Ok(s) => s, +LL | | Err(s) => "Bob", +LL | | }; + | |_____^ help: replace with: `Ok::<&str, &str>("Alice").unwrap_or("Bob")` + error: this pattern reimplements `Result::unwrap_or` --> tests/ui/manual_unwrap_or.rs:225:5 | @@ -184,5 +190,16 @@ LL | | None => 0, LL | | }; | |_________^ help: replace with: `some_macro!().unwrap_or(0)` -error: aborting due to 16 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:324:5 + | +LL | / if let Some(x) = Some(42) { +LL | | +LL | | x +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: replace with: `Some(42).unwrap_or(0)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index 832376fa5af15..9dae9fcae079b 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_unwrap_or_default)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::manual_unwrap_or)] +#![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option> = None; @@ -36,10 +36,12 @@ fn main() { // Issue #12531 unsafe fn no_deref_ptr(a: Option, b: *const Option) -> i32 { - match a { - // `*b` being correct depends on `a == Some(_)` - Some(_) => (*b).unwrap_or_default(), - _ => 0, + unsafe { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => (*b).unwrap_or_default(), + _ => 0, + } } } @@ -99,3 +101,8 @@ fn issue_12928() { let y = if let Some(Y(a, _)) = x { a } else { 0 }; let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } + +// For symetry with `manual_unwrap_or` test +fn allowed_manual_unwrap_or_zero() -> u32 { + Some(42).unwrap_or_default() +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index bedb3f0af0f3e..539d7a8bbae59 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_unwrap_or_default)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::manual_unwrap_or)] +#![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option> = None; @@ -68,14 +68,16 @@ fn main() { // Issue #12531 unsafe fn no_deref_ptr(a: Option, b: *const Option) -> i32 { - match a { - // `*b` being correct depends on `a == Some(_)` - Some(_) => match *b { - //~^ manual_unwrap_or_default - Some(v) => v, + unsafe { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => match *b { + //~^ manual_unwrap_or_default + Some(v) => v, + _ => 0, + }, _ => 0, - }, - _ => 0, + } } } @@ -135,3 +137,13 @@ fn issue_12928() { let y = if let Some(Y(a, _)) = x { a } else { 0 }; let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } + +// For symetry with `manual_unwrap_or` test +fn allowed_manual_unwrap_or_zero() -> u32 { + if let Some(x) = Some(42) { + //~^ manual_unwrap_or_default + x + } else { + 0 + } +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index ca9aa159152e3..e8f38a2e3899e 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -76,15 +76,26 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:73:20 + --> tests/ui/manual_unwrap_or_default.rs:74:24 | -LL | Some(_) => match *b { - | ____________________^ +LL | Some(_) => match *b { + | ________________________^ LL | | -LL | | Some(v) => v, -LL | | _ => 0, -LL | | }, - | |_________^ help: replace it with: `(*b).unwrap_or_default()` +LL | | Some(v) => v, +LL | | _ => 0, +LL | | }, + | |_____________^ help: replace it with: `(*b).unwrap_or_default()` -error: aborting due to 8 previous errors +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:143:5 + | +LL | / if let Some(x) = Some(42) { +LL | | +LL | | x +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: replace it with: `Some(42).unwrap_or_default()` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed index 948fec970d869..f8379ed23c5b2 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed @@ -1,10 +1,11 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_untyped)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::map_identity)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::unnecessary_wraps)] #![feature(result_flattening)] +#![allow( + clippy::let_underscore_untyped, + clippy::missing_docs_in_private_items, + clippy::map_identity, + clippy::redundant_closure, + clippy::unnecessary_wraps +)] fn main() { // mapping to Option on Iterator diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs index 67a91ab94147e..040a9ca85f647 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.rs +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs @@ -1,10 +1,11 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_untyped)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::map_identity)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::unnecessary_wraps)] #![feature(result_flattening)] +#![allow( + clippy::let_underscore_untyped, + clippy::missing_docs_in_private_items, + clippy::map_identity, + clippy::redundant_closure, + clippy::unnecessary_wraps +)] fn main() { // mapping to Option on Iterator diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr index 05d4d9a6ad85c..fe68eb7e4ab44 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:16:47 + --> tests/ui/map_flatten_fixable.rs:17:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)` @@ -8,43 +8,43 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = help: to override `-D warnings` add `#[allow(clippy::map_flatten)]` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:18:47 + --> tests/ui/map_flatten_fixable.rs:19:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:20:47 + --> tests/ui/map_flatten_fixable.rs:21:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:22:47 + --> tests/ui/map_flatten_fixable.rs:23:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:26:47 + --> tests/ui/map_flatten_fixable.rs:27:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)` error: called `map(..).flatten()` on `Option` - --> tests/ui/map_flatten_fixable.rs:30:40 + --> tests/ui/map_flatten_fixable.rs:31:40 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Result` - --> tests/ui/map_flatten_fixable.rs:34:42 + --> tests/ui/map_flatten_fixable.rs:35:42 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:44:10 + --> tests/ui/map_flatten_fixable.rs:45:10 | LL | .map(|n| match n { | __________^ @@ -74,7 +74,7 @@ LL ~ }); | error: called `map(..).flatten()` on `Option` - --> tests/ui/map_flatten_fixable.rs:65:10 + --> tests/ui/map_flatten_fixable.rs:66:10 | LL | .map(|_| { | __________^ diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.rs b/src/tools/clippy/tests/ui/match_on_vec_items.rs deleted file mode 100644 index f3174ec9734df..0000000000000 --- a/src/tools/clippy/tests/ui/match_on_vec_items.rs +++ /dev/null @@ -1,161 +0,0 @@ -#![warn(clippy::match_on_vec_items)] -#![allow(clippy::redundant_at_rest_pattern, clippy::useless_vec)] -//@no-rustfix -fn match_with_wildcard() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 1; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - _ => {}, - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => {}, - } -} - -fn match_without_wildcard() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 2; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - num => {}, - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - [ref sub @ ..] => {}, - } -} - -fn match_wildcard_and_action() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - _ => println!("Hello, World!"), - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => println!("Hello, World!"), - } -} - -fn match_vec_ref() { - let arr = &vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - _ => {}, - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => {}, - } -} - -fn match_with_get() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Ok - match arr.get(idx) { - Some(0) => println!("0"), - Some(1) => println!("1"), - _ => {}, - } - - // Ok - match arr.get(range) { - Some(&[0, 1]) => println!("0 1"), - Some(&[1, 2]) => println!("1 2"), - _ => {}, - } -} - -fn match_with_array() { - let arr = [0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Ok - match arr[idx] { - 0 => println!("0"), - 1 => println!("1"), - _ => {}, - } - - // Ok - match arr[range] { - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => {}, - } -} - -fn match_with_endless_range() { - let arr = vec![0, 1, 2, 3]; - let range = ..; - - // Ok - match arr[range] { - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - [0, 1, 2, 3] => println!("0, 1, 2, 3"), - _ => {}, - } - - // Ok - match arr[..] { - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - [0, 1, 2, 3] => println!("0, 1, 2, 3"), - _ => {}, - } -} - -fn main() { - match_with_wildcard(); - match_without_wildcard(); - match_wildcard_and_action(); - match_vec_ref(); - match_with_get(); - match_with_array(); - match_with_endless_range(); -} diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.stderr b/src/tools/clippy/tests/ui/match_on_vec_items.stderr deleted file mode 100644 index ae79e1305f7fd..0000000000000 --- a/src/tools/clippy/tests/ui/match_on_vec_items.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:10:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - | - = note: `-D clippy::match-on-vec-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::match_on_vec_items)]` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:18:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:32:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:40:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:54:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:62:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:76:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:84:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: aborting due to 8 previous errors - diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed index 3a3eee4c958b4..bdf39796ebfcb 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.fixed +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -171,3 +171,20 @@ fn issue_10447() -> usize { 2 } + +fn issue14634() { + macro_rules! id { + ($i:ident) => { + $i + }; + } + dbg!(3); + println!("here"); + //~^^^ match_single_binding + let id!(a) = dbg!(3); + println!("found {a}"); + //~^^^ match_single_binding + let id!(b) = dbg!(3); + let id!(_a) = dbg!(b + 1); + //~^^^ match_single_binding +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs index ada51254c6cdf..419ff95d873b0 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.rs +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -229,3 +229,23 @@ fn issue_10447() -> usize { 2 } + +fn issue14634() { + macro_rules! id { + ($i:ident) => { + $i + }; + } + match dbg!(3) { + _ => println!("here"), + } + //~^^^ match_single_binding + match dbg!(3) { + id!(a) => println!("found {a}"), + } + //~^^^ match_single_binding + let id!(_a) = match dbg!(3) { + id!(b) => dbg!(b + 1), + }; + //~^^^ match_single_binding +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr index 7e1ec32dac2ff..bdd0134a5f1c9 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.stderr +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -336,5 +336,47 @@ LL | | _ => println!("1"), LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` -error: aborting due to 24 previous errors +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:239:5 + | +LL | / match dbg!(3) { +LL | | _ => println!("here"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ dbg!(3); +LL + println!("here"); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:243:5 + | +LL | / match dbg!(3) { +LL | | id!(a) => println!("found {a}"), +LL | | } + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let id!(a) = dbg!(3); +LL + println!("found {a}"); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:247:5 + | +LL | / let id!(_a) = match dbg!(3) { +LL | | id!(b) => dbg!(b + 1), +LL | | }; + | |______^ + | +help: consider using a `let` statement + | +LL ~ let id!(b) = dbg!(3); +LL + let id!(_a) = dbg!(b + 1); + | + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index 76b0d131dd41d..2f4004181f6a8 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -1,6 +1,5 @@ //@aux-build:option_helpers.rs -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::disallowed_names, clippy::default_trait_access, @@ -19,8 +18,7 @@ clippy::wrong_self_convention, clippy::unused_async, clippy::unused_self, - clippy::useless_vec, - unused + clippy::useless_vec )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 353b999d7da0f..b226ce7c65dab 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:104:5 + --> tests/ui/methods.rs:102:5 | LL | / fn new() -> i32 { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:126:13 + --> tests/ui/methods.rs:124:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs index f3eeb85f20ed6..ee19d3ff71421 100644 --- a/src/tools/clippy/tests/ui/min_max.rs +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![allow(clippy::manual_clamp)] use std::cmp::{max as my_max, max, min as my_min, min}; diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr index 84b4d37545529..87510a465a08b 100644 --- a/src/tools/clippy/tests/ui/min_max.stderr +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -1,80 +1,79 @@ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:22:5 + --> tests/ui/min_max.rs:21:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::min-max` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::min_max)]` + = note: `#[deny(clippy::min_max)]` on by default error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:25:5 + --> tests/ui/min_max.rs:24:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:28:5 + --> tests/ui/min_max.rs:27:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:31:5 + --> tests/ui/min_max.rs:30:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:34:5 + --> tests/ui/min_max.rs:33:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:45:5 + --> tests/ui/min_max.rs:44:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:48:5 + --> tests/ui/min_max.rs:47:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:54:5 + --> tests/ui/min_max.rs:53:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:57:5 + --> tests/ui/min_max.rs:56:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:60:5 + --> tests/ui/min_max.rs:59:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:67:5 + --> tests/ui/min_max.rs:66:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:72:5 + --> tests/ui/min_max.rs:71:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:75:5 + --> tests/ui/min_max.rs:74:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed index cada5307b1c8e..bc123d1a40ba2 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed @@ -54,63 +54,63 @@ impl B { unsafe fn a(&self) -> &u8 { //~^ misnamed_getters - &self.a + unsafe { &self.a } } unsafe fn a_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn b(self) -> u8 { //~^ misnamed_getters - self.b + unsafe { self.b } } unsafe fn b_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn c(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } unsafe fn a_unchecked(&self) -> &u8 { //~^ misnamed_getters - &self.a + unsafe { &self.a } } unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn b_unchecked(self) -> u8 { //~^ misnamed_getters - self.b + unsafe { self.b } } unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn c_unchecked(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } } diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs index f529c56b4717b..6590101157c3f 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters.rs @@ -54,63 +54,63 @@ impl B { unsafe fn a(&self) -> &u8 { //~^ misnamed_getters - &self.b + unsafe { &self.b } } unsafe fn a_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn b(self) -> u8 { //~^ misnamed_getters - self.a + unsafe { self.a } } unsafe fn b_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn c(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } unsafe fn a_unchecked(&self) -> &u8 { //~^ misnamed_getters - &self.b + unsafe { &self.b } } unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn b_unchecked(self) -> u8 { //~^ misnamed_getters - self.a + unsafe { self.a } } unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn c_unchecked(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } } diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr index 5dd1d75bcf6f1..aaf21cecb9255 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr @@ -73,8 +73,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a(&self) -> &u8 { LL | | LL | | -LL | | &self.b - | | ------- help: consider using: `&self.a` +LL | | unsafe { &self.b } + | | ------- help: consider using: `&self.a` LL | | } | |_____^ @@ -84,8 +84,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.b - | | ----------- help: consider using: `&mut self.a` +LL | | unsafe { &mut self.b } + | | ----------- help: consider using: `&mut self.a` LL | | } | |_____^ @@ -95,8 +95,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b(self) -> u8 { LL | | LL | | -LL | | self.a - | | ------ help: consider using: `self.b` +LL | | unsafe { self.a } + | | ------ help: consider using: `self.b` LL | | } | |_____^ @@ -106,8 +106,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.a - | | ----------- help: consider using: `&mut self.b` +LL | | unsafe { &mut self.a } + | | ----------- help: consider using: `&mut self.b` LL | | } | |_____^ @@ -117,8 +117,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | LL | | -LL | | &self.b - | | ------- help: consider using: `&self.a` +LL | | unsafe { &self.b } + | | ------- help: consider using: `&self.a` LL | | } | |_____^ @@ -128,8 +128,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.b - | | ----------- help: consider using: `&mut self.a` +LL | | unsafe { &mut self.b } + | | ----------- help: consider using: `&mut self.a` LL | | } | |_____^ @@ -139,8 +139,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_unchecked(self) -> u8 { LL | | LL | | -LL | | self.a - | | ------ help: consider using: `self.b` +LL | | unsafe { self.a } + | | ------ help: consider using: `self.b` LL | | } | |_____^ @@ -150,8 +150,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.a - | | ----------- help: consider using: `&mut self.b` +LL | | unsafe { &mut self.a } + | | ----------- help: consider using: `&mut self.b` LL | | } | |_____^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed new file mode 100644 index 0000000000000..7112719a9f284 --- /dev/null +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed @@ -0,0 +1,24 @@ +//@edition: 2021 +#![allow(unused)] +#![allow(clippy::struct_field_names)] +#![warn(clippy::misnamed_getters)] + +// Edition 2021 specific check, where `unsafe` blocks are not required +// inside `unsafe fn`. + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + //~^ misnamed_getters + + &self.a + } +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs new file mode 100644 index 0000000000000..19b5d086041f4 --- /dev/null +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs @@ -0,0 +1,24 @@ +//@edition: 2021 +#![allow(unused)] +#![allow(clippy::struct_field_names)] +#![warn(clippy::misnamed_getters)] + +// Edition 2021 specific check, where `unsafe` blocks are not required +// inside `unsafe fn`. + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + //~^ misnamed_getters + + &self.b + } +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr new file mode 100644 index 0000000000000..5495e2e3733f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr @@ -0,0 +1,16 @@ +error: getter function appears to return the wrong field + --> tests/ui/misnamed_getters_2021.rs:15:5 + | +LL | / unsafe fn a(&self) -> &u8 { +LL | | +LL | | +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + | + = note: `-D clippy::misnamed-getters` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed index 3bbafe0bba3fe..9018f38100efd 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed @@ -139,4 +139,31 @@ fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { let _ = v4[0] + v4[1] + v4[2]; } +// ok +fn same_index_multiple_times(v1: &[u8]) { + let _ = v1[0] + v1[0]; +} + +// ok +fn highest_index_first(v1: &[u8]) { + let _ = v1[2] + v1[1] + v1[0]; +} + +fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert!(v1.len() == 3); + assert_eq!(v2.len(), 4); + assert!(v3.len() == 3); + assert_eq!(4, v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing + + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ missing_asserts_for_indexing + + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs index f8ea0173c13fc..44c5eddf3d8b9 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs @@ -139,4 +139,31 @@ fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { let _ = v4[0] + v4[1] + v4[2]; } +// ok +fn same_index_multiple_times(v1: &[u8]) { + let _ = v1[0] + v1[0]; +} + +// ok +fn highest_index_first(v1: &[u8]) { + let _ = v1[2] + v1[1] + v1[0]; +} + +fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert_eq!(v1.len(), 2); + assert_eq!(v2.len(), 4); + assert_eq!(2, v3.len()); + assert_eq!(4, v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing + + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ missing_asserts_for_indexing + + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr index 5d30920ccf521..b610de94b5308 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr @@ -301,5 +301,57 @@ LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 11 previous errors +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:158:13 + | +LL | assert_eq!(v1.len(), 2); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` +... +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:163:13 + | +LL | assert_eq!(2, v3.len()); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` +... +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:13 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:21 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:29 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs index a520151a2dd94..eb98969efa47f 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -73,4 +73,17 @@ pub fn issue11856(values: &[i32]) -> usize { ascending.len() } +fn assert_after_indexing(v1: &[u8]) { + let _ = v1[1] + v1[2]; + //~^ ERROR: indexing into a slice multiple times without an `assert` + assert!(v1.len() > 2); +} + +fn issue14255(v1: &[u8]) { + assert_ne!(v1.len(), 2); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 24109b052a8af..a17ad02321386 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -180,5 +180,48 @@ LL | let _ = x[0] + x[1]; | ^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 8 previous errors +error: indexing into a slice multiple times without an `assert` + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + | +LL | let _ = v1[1] + v1[2]; + | ^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + | +LL | let _ = v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:21 + | +LL | let _ = v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index 10df44e73b85f..65eb2d5938b6b 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -144,7 +144,7 @@ mod msrv { #[clippy::msrv = "1.62"] mod with_extern { - const extern "C" fn c() {} + const unsafe extern "C" fn c() {} //~^ missing_const_for_fn #[rustfmt::skip] @@ -153,7 +153,7 @@ mod msrv { //~^ missing_const_for_fn // any item functions in extern block won't trigger this lint - extern "C" { + unsafe extern "C" { fn c_in_block(); } } diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index bc44b34daef7e..3690d2f799ff4 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -144,7 +144,7 @@ mod msrv { #[clippy::msrv = "1.62"] mod with_extern { - extern "C" fn c() {} + unsafe extern "C" fn c() {} //~^ missing_const_for_fn #[rustfmt::skip] @@ -153,7 +153,7 @@ mod msrv { //~^ missing_const_for_fn // any item functions in extern block won't trigger this lint - extern "C" { + unsafe extern "C" { fn c_in_block(); } } diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index 5df5a54ff5216..10e07d12f5a4c 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -212,12 +212,12 @@ LL | const fn union_access_can_be_const() { error: this could be a `const fn` --> tests/ui/missing_const_for_fn/could_be_const.rs:147:9 | -LL | extern "C" fn c() {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | unsafe extern "C" fn c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `const` | -LL | const extern "C" fn c() {} +LL | const unsafe extern "C" fn c() {} | +++++ error: this could be a `const fn` diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index 95e361c5d5556..ffdae8504f72e 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -151,6 +151,45 @@ pub fn debug_assertions() { debug_assert_ne!(1, 2); } +pub fn partially_const(n: usize) { + //~^ missing_panics_doc + + const { + assert!(N > 5); + } + + assert!(N > n); +} + +pub fn expect_allow(i: Option) { + #[expect(clippy::missing_panics_doc)] + i.unwrap(); + + #[allow(clippy::missing_panics_doc)] + i.unwrap(); +} + +pub fn expect_allow_with_error(i: Option) { + //~^ missing_panics_doc + + #[expect(clippy::missing_panics_doc)] + i.unwrap(); + + #[allow(clippy::missing_panics_doc)] + i.unwrap(); + + i.unwrap(); +} + +pub fn expect_after_error(x: Option, y: Option) { + //~^ missing_panics_doc + + let x = x.unwrap(); + + #[expect(clippy::missing_panics_doc)] + let y = y.unwrap(); +} + // all function must be triggered the lint. // `pub` is required, because the lint does not consider unreachable items pub mod issue10240 { diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr index a83e2fa367dd3..7f0acf8de9b77 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -73,76 +73,112 @@ LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:157:5 + --> tests/ui/missing_panics_doc.rs:154:1 + | +LL | pub fn partially_const(n: usize) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:161:5 + | +LL | assert!(N > n); + | ^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:172:1 + | +LL | pub fn expect_allow_with_error(i: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:181:5 + | +LL | i.unwrap(); + | ^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:184:1 + | +LL | pub fn expect_after_error(x: Option, y: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:187:13 + | +LL | let x = x.unwrap(); + | ^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:196:5 | LL | pub fn option_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:160:9 + --> tests/ui/missing_panics_doc.rs:199:9 | LL | o.unwrap() | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:163:5 + --> tests/ui/missing_panics_doc.rs:202:5 | LL | pub fn option_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:166:9 + --> tests/ui/missing_panics_doc.rs:205:9 | LL | o.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:169:5 + --> tests/ui/missing_panics_doc.rs:208:5 | LL | pub fn result_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:172:9 + --> tests/ui/missing_panics_doc.rs:211:9 | LL | res.unwrap() | ^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:175:5 + --> tests/ui/missing_panics_doc.rs:214:5 | LL | pub fn result_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:178:9 + --> tests/ui/missing_panics_doc.rs:217:9 | LL | res.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:181:5 + --> tests/ui/missing_panics_doc.rs:220:5 | LL | pub fn last_unwrap(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:183:10 + --> tests/ui/missing_panics_doc.rs:222:10 | LL | *v.last().unwrap() | ^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:186:5 + --> tests/ui/missing_panics_doc.rs:225:5 | LL | pub fn last_expect(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:188:10 + --> tests/ui/missing_panics_doc.rs:227:10 | LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed index a3c94ab139ec6..58faeaee09d46 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed @@ -18,8 +18,10 @@ fn bar(x: i32) -> i32 { } unsafe fn foo1() -> i32 { - // Should not warn! - std::mem::transmute([1u16, 2u16]) + unsafe { + // Should not warn! + std::mem::transmute([1u16, 2u16]) + } } // Should not warn! @@ -31,33 +33,35 @@ enum Foo { } unsafe fn foo2() -> i32 { - let mut i: i32 = 0; - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations + unsafe { + let mut i: i32 = 0; + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations - let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations - bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations + let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations - i = local_bad_transmute!([1u16, 2u16]); + i = local_bad_transmute!([1u16, 2u16]); - // Should not warn. - i = bad_transmute!([1u16, 2u16]); + // Should not warn. + i = bad_transmute!([1u16, 2u16]); - i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); + //~^ ERROR: transmute used without annotations - i = std::mem::transmute::(Foo::A); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute::(Foo::A); + //~^ ERROR: transmute used without annotations - i + i + } } fn main() { diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs index c12e1b0f8d220..c9a4c5fa83b2b 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs @@ -18,8 +18,10 @@ fn bar(x: i32) -> i32 { } unsafe fn foo1() -> i32 { - // Should not warn! - std::mem::transmute([1u16, 2u16]) + unsafe { + // Should not warn! + std::mem::transmute([1u16, 2u16]) + } } // Should not warn! @@ -31,33 +33,35 @@ enum Foo { } unsafe fn foo2() -> i32 { - let mut i: i32 = 0; - i = std::mem::transmute([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<_, _>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<_, i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations + unsafe { + let mut i: i32 = 0; + i = std::mem::transmute([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations - let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations - bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations + let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations - i = local_bad_transmute!([1u16, 2u16]); + i = local_bad_transmute!([1u16, 2u16]); - // Should not warn. - i = bad_transmute!([1u16, 2u16]); + // Should not warn. + i = bad_transmute!([1u16, 2u16]); - i = std::mem::transmute([0i16, 0i16]); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute([0i16, 0i16]); + //~^ ERROR: transmute used without annotations - i = std::mem::transmute(Foo::A); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute(Foo::A); + //~^ ERROR: transmute used without annotations - i + i + } } fn main() { diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr b/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr index 5903ed488ef18..63f7e28ee7dc6 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr @@ -1,41 +1,41 @@ error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:35:19 + --> tests/ui/missing_transmute_annotations.rs:38:23 | -LL | i = std::mem::transmute([1u16, 2u16]); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute([1u16, 2u16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` | = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:37:19 + --> tests/ui/missing_transmute_annotations.rs:40:23 | -LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:39:19 + --> tests/ui/missing_transmute_annotations.rs:42:23 | -LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:41:19 + --> tests/ui/missing_transmute_annotations.rs:44:23 | -LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:44:32 + --> tests/ui/missing_transmute_annotations.rs:47:36 | -LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:46:19 + --> tests/ui/missing_transmute_annotations.rs:49:23 | -LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations --> tests/ui/missing_transmute_annotations.rs:11:19 @@ -43,31 +43,31 @@ error: transmute used without annotations LL | std::mem::transmute($e) | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` ... -LL | i = local_bad_transmute!([1u16, 2u16]); - | ---------------------------------- in this macro invocation +LL | i = local_bad_transmute!([1u16, 2u16]); + | ---------------------------------- in this macro invocation | = note: this error originates in the macro `local_bad_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:54:19 + --> tests/ui/missing_transmute_annotations.rs:57:23 | -LL | i = std::mem::transmute([0i16, 0i16]); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` +LL | i = std::mem::transmute([0i16, 0i16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:57:19 + --> tests/ui/missing_transmute_annotations.rs:60:23 | -LL | i = std::mem::transmute(Foo::A); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::` +LL | i = std::mem::transmute(Foo::A); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:64:35 + --> tests/ui/missing_transmute_annotations.rs:68:35 | LL | let x: _ = unsafe { std::mem::transmute::<_, i32>([1u16, 2u16]) }; | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:67:30 + --> tests/ui/missing_transmute_annotations.rs:71:30 | LL | let x: _ = std::mem::transmute::<_, i32>([1u16, 2u16]); | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index b5d356a502170..4c1d6b1ccb596 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -88,11 +88,13 @@ static mut COUNTER: usize = 0; /// /// Don't ever call this from multiple threads pub unsafe fn mutates_static() -> usize { - COUNTER += 1; - COUNTER + unsafe { + COUNTER += 1; + COUNTER + } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 14ea16662fdb4..71d546718ae79 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -88,11 +88,13 @@ static mut COUNTER: usize = 0; /// /// Don't ever call this from multiple threads pub unsafe fn mutates_static() -> usize { - COUNTER += 1; - COUNTER + unsafe { + COUNTER += 1; + COUNTER + } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs index b8c10f3eeb8f9..1b0b351518cbb 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.rs +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -1,4 +1,10 @@ -#![allow(unused, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut)] +#![allow( + unused, + clippy::needless_lifetimes, + clippy::needless_pass_by_ref_mut, + clippy::redundant_allocation, + clippy::boxed_local +)] #![warn(clippy::mut_from_ref)] struct Foo; @@ -40,6 +46,18 @@ fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { unsafe { unimplemented!() } } +fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + //~^ mut_from_ref + + unsafe { unimplemented!() } +} + +fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + //~^ mut_from_ref + + unsafe { unimplemented!() } +} + // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { unsafe { unimplemented!() } @@ -50,6 +68,20 @@ fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { unsafe { unimplemented!() } } +fn works_tuples<'a>(x: (&'a u32, &'a mut u32)) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +fn works_box<'a>(x: &'a u32, y: Box<&'a mut u32>) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +struct RefMut<'a>(&'a mut u32); + +fn works_parameter<'a>(x: &'a u32, y: RefMut<'a>) -> &'a mut u32 { + unsafe { unimplemented!() } +} + unsafe fn also_broken(x: &u32) -> &mut u32 { //~^ mut_from_ref diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr index 8c3c8e0c3d851..0974268734653 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.stderr +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:7:39 + --> tests/ui/mut_from_ref.rs:13:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:7:29 + --> tests/ui/mut_from_ref.rs:13:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,64 +13,88 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:15:25 + --> tests/ui/mut_from_ref.rs:21:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:15:16 + --> tests/ui/mut_from_ref.rs:21:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:25:21 + --> tests/ui/mut_from_ref.rs:31:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:25:12 + --> tests/ui/mut_from_ref.rs:31:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:50 + --> tests/ui/mut_from_ref.rs:37:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:25 + --> tests/ui/mut_from_ref.rs:37:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:67 + --> tests/ui/mut_from_ref.rs:43:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:27 + --> tests/ui/mut_from_ref.rs:43:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:53:35 + --> tests/ui/mut_from_ref.rs:49:46 + | +LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> tests/ui/mut_from_ref.rs:49:24 + | +LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + | ^^^^^^^ ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> tests/ui/mut_from_ref.rs:55:37 + | +LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> tests/ui/mut_from_ref.rs:55:24 + | +LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + | ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> tests/ui/mut_from_ref.rs:85:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:53:26 + --> tests/ui/mut_from_ref.rs:85:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 80a712a9286a4..7db5c9f274f6e 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index 838fc1d7c36ee..a6d5d60fbf05b 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:8:5 + --> tests/ui/mutex_atomic.rs:7:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -8,31 +8,31 @@ LL | Mutex::new(true); = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:11:5 + --> tests/ui/mutex_atomic.rs:10:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:14:5 + --> tests/ui/mutex_atomic.rs:13:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:18:5 + --> tests/ui/mutex_atomic.rs:17:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:21:5 + --> tests/ui/mutex_atomic.rs:20:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:24:5 + --> tests/ui/mutex_atomic.rs:23:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -41,31 +41,31 @@ LL | Mutex::new(0u32); = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:27:5 + --> tests/ui/mutex_atomic.rs:26:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:31:5 + --> tests/ui/mutex_atomic.rs:30:5 | LL | Mutex::new(0u8); | ^^^^^^^^^^^^^^^ error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:34:5 + --> tests/ui/mutex_atomic.rs:33:5 | LL | Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:41:5 + --> tests/ui/mutex_atomic.rs:40:5 | LL | Mutex::new(X); | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed index e4504bc2784cc..84924cac62d52 100644 --- a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed @@ -89,7 +89,7 @@ fn should_not_lint( tuple_struct: TupleStruct, s: Struct, ) { - if let [ref a] = slice {} + if let [a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs index 7edfda60b9790..280cef43340cc 100644 --- a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs @@ -89,7 +89,7 @@ fn should_not_lint( tuple_struct: TupleStruct, s: Struct, ) { - if let [ref a] = slice {} + if let [a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index 6551fa56b42ce..b09efe9888f50 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -126,3 +126,87 @@ fn main() { fn foo(_: impl IntoIterator) {} fn bar>(_: Vec, _: I) {} fn baz>(_: I, _: (), _: impl IntoIterator) {} + +mod issue9191 { + use std::cell::Cell; + use std::collections::HashSet; + use std::hash::Hash; + use std::marker::PhantomData; + use std::ops::Deref; + + fn captures_ref_mut(xs: Vec, mut ys: HashSet) { + if xs.iter().map(|x| ys.remove(x)).collect::>().contains(&true) { + todo!() + } + } + + #[derive(Debug, Clone)] + struct MyRef<'a>(PhantomData<&'a mut Cell>>, *mut Cell>); + + impl MyRef<'_> { + fn new(target: &mut Cell>) -> Self { + MyRef(PhantomData, target) + } + + fn get(&mut self) -> &mut Cell> { + unsafe { &mut *self.1 } + } + } + + fn captures_phantom(xs: Vec, mut ys: Cell>) { + let mut ys_ref = MyRef::new(&mut ys); + if xs + .iter() + .map({ + let mut ys_ref = ys_ref.clone(); + move |x| ys_ref.get().get_mut().remove(x) + }) + .collect::>() + .contains(&true) + { + todo!() + } + } +} + +pub fn issue8055(v: impl IntoIterator) -> Result, usize> { + let mut zeros = 0; + + let res: Vec<_> = v + .into_iter() + .filter(|i| { + if *i == 0 { + zeros += 1 + }; + *i != 0 + }) + .collect(); + + if zeros != 0 { + return Err(zeros); + } + Ok(res.into_iter()) +} + +mod issue8055_regression { + struct Foo { + inner: T, + marker: core::marker::PhantomData, + } + + impl Iterator for Foo { + type Item = T::Item; + fn next(&mut self) -> Option { + self.inner.next() + } + } + + fn foo() { + Foo { + inner: [].iter(), + marker: core::marker::PhantomData, + } + .collect::>() + .len(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 973c41c687544..da4182966bb17 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -126,3 +126,87 @@ fn main() { fn foo(_: impl IntoIterator) {} fn bar>(_: Vec, _: I) {} fn baz>(_: I, _: (), _: impl IntoIterator) {} + +mod issue9191 { + use std::cell::Cell; + use std::collections::HashSet; + use std::hash::Hash; + use std::marker::PhantomData; + use std::ops::Deref; + + fn captures_ref_mut(xs: Vec, mut ys: HashSet) { + if xs.iter().map(|x| ys.remove(x)).collect::>().contains(&true) { + todo!() + } + } + + #[derive(Debug, Clone)] + struct MyRef<'a>(PhantomData<&'a mut Cell>>, *mut Cell>); + + impl MyRef<'_> { + fn new(target: &mut Cell>) -> Self { + MyRef(PhantomData, target) + } + + fn get(&mut self) -> &mut Cell> { + unsafe { &mut *self.1 } + } + } + + fn captures_phantom(xs: Vec, mut ys: Cell>) { + let mut ys_ref = MyRef::new(&mut ys); + if xs + .iter() + .map({ + let mut ys_ref = ys_ref.clone(); + move |x| ys_ref.get().get_mut().remove(x) + }) + .collect::>() + .contains(&true) + { + todo!() + } + } +} + +pub fn issue8055(v: impl IntoIterator) -> Result, usize> { + let mut zeros = 0; + + let res: Vec<_> = v + .into_iter() + .filter(|i| { + if *i == 0 { + zeros += 1 + }; + *i != 0 + }) + .collect(); + + if zeros != 0 { + return Err(zeros); + } + Ok(res.into_iter()) +} + +mod issue8055_regression { + struct Foo { + inner: T, + marker: core::marker::PhantomData, + } + + impl Iterator for Foo { + type Item = T::Item; + fn next(&mut self) -> Option { + self.inner.next() + } + } + + fn foo() { + Foo { + inner: [].iter(), + marker: core::marker::PhantomData, + } + .collect::>() + .len(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index d59393fb3f3c6..e9d811986aa49 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -534,4 +534,11 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +pub fn issue14607<'s>(x: &'s u8) { + #[expect(clippy::redundant_closure_call)] + (|| { + let _: &'s u8 = x; + })(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index e24907ab5fcdf..0b6eb9755b932 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -534,4 +534,11 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +pub fn issue14607<'s>(x: &'s u8) { + #[expect(clippy::redundant_closure_call)] + (|| { + let _: &'s u8 = x; + })(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs index f0c5a716ac991..bdad3e3d5b008 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs @@ -1,8 +1,9 @@ #![allow( clippy::if_same_then_else, clippy::no_effect, + clippy::ptr_arg, clippy::redundant_closure_call, - clippy::ptr_arg + clippy::uninlined_format_args )] #![warn(clippy::needless_pass_by_ref_mut)] //@no-rustfix @@ -300,7 +301,7 @@ struct Data { } // Unsafe functions should not warn. unsafe fn get_mut_unchecked(ptr: &mut NonNull>) -> &mut T { - &mut (*ptr.as_ptr()).value + unsafe { &mut (*ptr.as_ptr()).value } } // Unsafe blocks should not warn. fn get_mut_unchecked2(ptr: &mut NonNull>) -> &mut T { diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr index 6637a255b5f51..94d98f0e9b12d 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:11:11 + --> tests/ui/needless_pass_by_ref_mut.rs:12:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:37:12 + --> tests/ui/needless_pass_by_ref_mut.rs:38:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:48:12 + --> tests/ui/needless_pass_by_ref_mut.rs:49:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:51:29 + --> tests/ui/needless_pass_by_ref_mut.rs:52:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:129:16 + --> tests/ui/needless_pass_by_ref_mut.rs:130:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:134:16 + --> tests/ui/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:139:16 + --> tests/ui/needless_pass_by_ref_mut.rs:140:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:144:16 + --> tests/ui/needless_pass_by_ref_mut.rs:145:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:149:24 + --> tests/ui/needless_pass_by_ref_mut.rs:150:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:154:24 + --> tests/ui/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:159:32 + --> tests/ui/needless_pass_by_ref_mut.rs:160:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:164:24 + --> tests/ui/needless_pass_by_ref_mut.rs:165:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:164:45 + --> tests/ui/needless_pass_by_ref_mut.rs:165:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:200:16 + --> tests/ui/needless_pass_by_ref_mut.rs:201:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:205:20 + --> tests/ui/needless_pass_by_ref_mut.rs:206:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,115 +96,115 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:218:39 + --> tests/ui/needless_pass_by_ref_mut.rs:219:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:227:26 + --> tests/ui/needless_pass_by_ref_mut.rs:228:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:247:30 + --> tests/ui/needless_pass_by_ref_mut.rs:248:30 | LL | async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:267:16 + --> tests/ui/needless_pass_by_ref_mut.rs:268:16 | LL | fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:279:22 + --> tests/ui/needless_pass_by_ref_mut.rs:280:22 | LL | async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:334:12 + --> tests/ui/needless_pass_by_ref_mut.rs:335:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:18 + --> tests/ui/needless_pass_by_ref_mut.rs:338:18 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:45 + --> tests/ui/needless_pass_by_ref_mut.rs:338:45 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:346:46 + --> tests/ui/needless_pass_by_ref_mut.rs:347:46 | LL | async fn foo2(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:363:18 + --> tests/ui/needless_pass_by_ref_mut.rs:364:18 | LL | fn _empty_tup(x: &mut (())) {} | ^^^^^^^^^ help: consider changing to: `&()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:365:19 + --> tests/ui/needless_pass_by_ref_mut.rs:366:19 | LL | fn _single_tup(x: &mut ((i32,))) {} | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:367:18 + --> tests/ui/needless_pass_by_ref_mut.rs:368:18 | LL | fn _multi_tup(x: &mut ((i32, u32))) {} | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:369:11 + --> tests/ui/needless_pass_by_ref_mut.rs:370:11 | LL | fn _fn(x: &mut (fn())) {} | ^^^^^^^^^^^ help: consider changing to: `&fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:372:23 + --> tests/ui/needless_pass_by_ref_mut.rs:373:23 | LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:374:20 + --> tests/ui/needless_pass_by_ref_mut.rs:375:20 | LL | fn _extern_c_fn(x: &mut extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:376:18 + --> tests/ui/needless_pass_by_ref_mut.rs:377:18 | LL | fn _unsafe_fn(x: &mut unsafe fn()) {} | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:378:25 + --> tests/ui/needless_pass_by_ref_mut.rs:379:25 | LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:380:20 + --> tests/ui/needless_pass_by_ref_mut.rs:381:20 | LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:382:20 + --> tests/ui/needless_pass_by_ref_mut.rs:383:20 | LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)` diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut_2021.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut_2021.rs new file mode 100644 index 0000000000000..994eba9cae3d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut_2021.rs @@ -0,0 +1,12 @@ +//@edition: 2021 +//@check-pass +#![warn(clippy::needless_pass_by_ref_mut)] + +struct Data { + value: T, +} + +// Unsafe functions should not warn. +unsafe fn get_mut_unchecked(ptr: &mut std::ptr::NonNull>) -> &mut T { + &mut (*ptr.as_ptr()).value +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed index 995470493bfb7..ff6e08300e298 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.fixed +++ b/src/tools/clippy/tests/ui/neg_multiply.fixed @@ -53,3 +53,32 @@ fn main() { X * -1; // should be ok -1 * X; // should also be ok } + +fn float() { + let x = 0.0; + + -x; + //~^ neg_multiply + + -x; + //~^ neg_multiply + + 100.0 + -x; + //~^ neg_multiply + + -(100.0 + x); + //~^ neg_multiply + + -17.0; + //~^ neg_multiply + + 0.0 + -0.0; + //~^ neg_multiply + + -(3.0_f32 as f64); + //~^ neg_multiply + -(3.0_f32 as f64); + //~^ neg_multiply + + -1.0 * -1.0; // should be ok +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs index 95b94e29517fa..b0f4e85c78e5d 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.rs +++ b/src/tools/clippy/tests/ui/neg_multiply.rs @@ -53,3 +53,32 @@ fn main() { X * -1; // should be ok -1 * X; // should also be ok } + +fn float() { + let x = 0.0; + + x * -1.0; + //~^ neg_multiply + + -1.0 * x; + //~^ neg_multiply + + 100.0 + x * -1.0; + //~^ neg_multiply + + (100.0 + x) * -1.0; + //~^ neg_multiply + + -1.0 * 17.0; + //~^ neg_multiply + + 0.0 + 0.0 * -1.0; + //~^ neg_multiply + + 3.0_f32 as f64 * -1.0; + //~^ neg_multiply + (3.0_f32 as f64) * -1.0; + //~^ neg_multiply + + -1.0 * -1.0; // should be ok +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr index 9efa5d3ba1f1d..2ef7e32ce05e1 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.stderr +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -49,5 +49,53 @@ error: this multiplication by -1 can be written more succinctly LL | (3_usize as i32) * -1; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)` -error: aborting due to 8 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:60:5 + | +LL | x * -1.0; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:63:5 + | +LL | -1.0 * x; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:66:13 + | +LL | 100.0 + x * -1.0; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:69:5 + | +LL | (100.0 + x) * -1.0; + | ^^^^^^^^^^^^^^^^^^ help: consider using: `-(100.0 + x)` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:72:5 + | +LL | -1.0 * 17.0; + | ^^^^^^^^^^^ help: consider using: `-17.0` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:75:11 + | +LL | 0.0 + 0.0 * -1.0; + | ^^^^^^^^^^ help: consider using: `-0.0` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:78:5 + | +LL | 3.0_f32 as f64 * -1.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:80:5 + | +LL | (3.0_f32 as f64) * -1.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs index 0d09b3ceecde7..f4248ffc0f4d0 100644 --- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs @@ -43,7 +43,7 @@ extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} -extern "C" { +unsafe extern "C" { fn c_abi_in_block(arg_one: u32, arg_two: usize); } diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed index 8774c666db11f..23dbee5a08488 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed @@ -162,3 +162,36 @@ impl PartialOrd for I { return Some(self.cmp(other)); } } + +// #13640, do not lint + +#[derive(Eq, PartialEq)] +struct J(u32); + +impl Ord for J { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for J { + fn partial_cmp(&self, other: &Self) -> Option { + self.cmp(other).into() + } +} + +// #13640, check that a simple `.into()` does not obliterate the lint + +#[derive(Eq, PartialEq)] +struct K(u32); + +impl Ord for K { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for K { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs index 568b97c8fff7b..12f055a542b89 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs @@ -166,3 +166,38 @@ impl PartialOrd for I { return Some(self.cmp(other)); } } + +// #13640, do not lint + +#[derive(Eq, PartialEq)] +struct J(u32); + +impl Ord for J { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for J { + fn partial_cmp(&self, other: &Self) -> Option { + self.cmp(other).into() + } +} + +// #13640, check that a simple `.into()` does not obliterate the lint + +#[derive(Eq, PartialEq)] +struct K(u32); + +impl Ord for K { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for K { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { + Ordering::Greater.into() + } +} diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr index 86845df4ea906..c7de968588f8b 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr @@ -31,5 +31,18 @@ LL - fn partial_cmp(&self, _: &Self) -> Option { LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } | -error: aborting due to 2 previous errors +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> tests/ui/non_canonical_partial_ord_impl.rs:198:1 + | +LL | / impl PartialOrd for K { +LL | | +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________- +LL | || Ordering::Greater.into() +LL | || } + | ||_____- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs index b772c754f8b76..3f34dff563d26 100644 --- a/src/tools/clippy/tests/ui/non_expressive_names.rs +++ b/src/tools/clippy/tests/ui/non_expressive_names.rs @@ -1,5 +1,4 @@ -#![warn(clippy::all)] -#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] +#![allow(clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] #[derive(Clone, Debug)] enum MaybeInst { diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stderr b/src/tools/clippy/tests/ui/non_expressive_names.stderr index 3bd77a730fe78..11b12d2c5f103 100644 --- a/src/tools/clippy/tests/ui/non_expressive_names.stderr +++ b/src/tools/clippy/tests/ui/non_expressive_names.stderr @@ -1,5 +1,5 @@ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:28:9 + --> tests/ui/non_expressive_names.rs:27:9 | LL | let _1 = 1; | ^^ @@ -8,31 +8,31 @@ LL | let _1 = 1; = help: to override `-D warnings` add `#[allow(clippy::just_underscores_and_digits)]` error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:30:9 + --> tests/ui/non_expressive_names.rs:29:9 | LL | let ____1 = 1; | ^^^^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:32:9 + --> tests/ui/non_expressive_names.rs:31:9 | LL | let __1___2 = 12; | ^^^^^^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:54:13 + --> tests/ui/non_expressive_names.rs:53:13 | LL | let _1 = 1; | ^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:56:13 + --> tests/ui/non_expressive_names.rs:55:13 | LL | let ____1 = 1; | ^^^^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:58:13 + --> tests/ui/non_expressive_names.rs:57:13 | LL | let __1___2 = 12; | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs index 046ea70b08f16..31778f7450989 100644 --- a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs @@ -35,7 +35,7 @@ unsafe impl Send for ArcGuard {} //~^ ERROR: some fields in `ArcGuard` are not safe to be sent to another thread // rusb / RUSTSEC-2020-0098 -extern "C" { +unsafe extern "C" { type libusb_device_handle; } @@ -90,7 +90,7 @@ unsafe impl Send for MultiParam {} //~^ ERROR: some fields in `MultiParam` are not safe to be sent to another thread // Tests for raw pointer heuristic -extern "C" { +unsafe extern "C" { type NonSend; } diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed index f7c56b6fffe81..2b30c8f984ebe 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -9,16 +9,16 @@ use once_cell::sync::Lazy; fn main() {} static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library let x = "bar"; x.to_uppercase() }); static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_QUX: std::sync::LazyLock = { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library if "qux".len() == 3 { std::sync::LazyLock::new(|| "qux".to_uppercase()) } else if "qux".is_ascii() { @@ -39,11 +39,11 @@ mod once_cell_lazy_with_fns { use once_cell::sync::Lazy; static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_replaceable_fns() { let _ = std::sync::LazyLock::force(&LAZY_FOO); diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs index 90bc428137cea..c52338eee83cb 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -9,16 +9,16 @@ use once_cell::sync::Lazy; fn main() {} static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: Lazy = Lazy::new(|| { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library let x = "bar"; x.to_uppercase() }); static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_QUX: Lazy = { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library if "qux".len() == 3 { Lazy::new(|| "qux".to_uppercase()) } else if "qux".is_ascii() { @@ -39,11 +39,11 @@ mod once_cell_lazy_with_fns { use once_cell::sync::Lazy; static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_replaceable_fns() { let _ = Lazy::force(&LAZY_FOO); diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr index 333052ae1c110..bb80cd11c7199 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr @@ -1,4 +1,4 @@ -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:11:18 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -12,7 +12,7 @@ LL - static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); LL + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18 | LL | static LAZY_BAR: Lazy = Lazy::new(|| { @@ -24,7 +24,7 @@ LL - static LAZY_BAR: Lazy = Lazy::new(|| { LL + static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18 | LL | static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; @@ -36,7 +36,7 @@ LL - static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; LL + static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18 | LL | static LAZY_QUX: Lazy = { @@ -54,7 +54,7 @@ LL | } else { LL ~ std::sync::LazyLock::new(|| "qux".to_string()) | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:41:22 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -69,7 +69,7 @@ LL | fn calling_replaceable_fns() { LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:43:22 | LL | static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); @@ -84,7 +84,7 @@ LL | let _ = Lazy::force(&LAZY_FOO); LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:45:26 | LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs index 34f8dd1ccb2ea..acc8c04678f50 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs @@ -9,11 +9,11 @@ mod once_cell_lazy { use once_cell::sync::Lazy; static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_irreplaceable_fns() { let _ = Lazy::get(&LAZY_FOO); @@ -31,13 +31,13 @@ mod lazy_static_lazy_static { lazy_static! { static ref LAZY_FOO: String = "foo".to_uppercase(); } - //~^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + //~^^^ ERROR: this macro has been superseded by `std::sync::LazyLock` lazy_static! { static ref LAZY_BAR: String = "bar".to_uppercase(); static ref LAZY_BAZ: String = "baz".to_uppercase(); } - //~^^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` - //~| ERROR: this macro has been superceded by `std::sync::LazyLock` + //~^^^^ ERROR: this macro has been superseded by `std::sync::LazyLock` + //~| ERROR: this macro has been superseded by `std::sync::LazyLock` } fn main() {} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr index 216190ae4ca31..2c35cad6237ab 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr @@ -1,4 +1,4 @@ -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:31:5 | LL | / lazy_static! { @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 | LL | / lazy_static! { @@ -18,7 +18,7 @@ LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); LL | | } | |_____^ -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 | LL | / lazy_static! { @@ -29,7 +29,7 @@ LL | | } | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:11:22 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -41,7 +41,7 @@ LL - static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); LL + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26 | LL | static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); @@ -53,7 +53,7 @@ LL - static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); LL + static mut LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26 | LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index a155ff3508be0..1eecc3dee3dc5 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -216,3 +216,23 @@ fn issue14184(a: f32, b: bool) { println!("Hi"); } } + +mod issue14404 { + enum TyKind { + Ref(i32, i32, i32), + Other, + } + + struct Expr; + + fn is_mutable(expr: &Expr) -> bool { + todo!() + } + + fn should_not_give_macro(ty: TyKind, expr: Expr) { + if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { + //~^ nonminimal_bool + todo!() + } + } +} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr index 336cce40abf0d..0e3e4cf7988e2 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -227,7 +227,13 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:214:8 | LL | if !(a < 2.0 && !b) { - | ^^^^^^^^^^^^^^^^ help: try: `!(a < 2.0) || b` + | ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b` -error: aborting due to 30 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool.rs:233:12 + | +LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed index 66f5070787b09..70ae090626b96 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed @@ -46,6 +46,18 @@ fn main() { let partial = true.then_some(1); partial.unwrap_or_else(|| n * 2); // not lint + + if true { () } else { Default::default() }; + //~^ obfuscated_if_else + + if true { () } else { Default::default() }; + //~^ obfuscated_if_else + + if true { 1 } else { Default::default() }; + //~^ obfuscated_if_else + + if true { 1 } else { Default::default() }; + //~^ obfuscated_if_else } fn issue11141() { diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.rs b/src/tools/clippy/tests/ui/obfuscated_if_else.rs index 4efd740eb60bb..8e1f57ca2c026 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.rs +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.rs @@ -46,6 +46,18 @@ fn main() { let partial = true.then_some(1); partial.unwrap_or_else(|| n * 2); // not lint + + true.then_some(()).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then(|| ()).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then_some(1).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then(|| 1).unwrap_or_default(); + //~^ obfuscated_if_else } fn issue11141() { diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr index d676c25669570..0de7259d8bb82 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr @@ -68,52 +68,76 @@ LL | true.then_some(1).unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:53:13 + --> tests/ui/obfuscated_if_else.rs:50:5 + | +LL | true.then_some(()).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:53:5 + | +LL | true.then(|| ()).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:56:5 + | +LL | true.then_some(1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:59:5 + | +LL | true.then(|| 1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:65:13 | LL | let _ = true.then_some(40).unwrap_or(17) | 2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(if true { 40 } else { 17 })` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:13 + --> tests/ui/obfuscated_if_else.rs:69:13 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(if true { 30 } else { 17 })` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:48 + --> tests/ui/obfuscated_if_else.rs:69:48 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 2 } else { 3 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:81 + --> tests/ui/obfuscated_if_else.rs:69:81 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 10 } else { 1 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:63:17 + --> tests/ui/obfuscated_if_else.rs:75:17 | LL | let _ = 2 | true.then_some(40).unwrap_or(17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 40 } else { 17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:67:13 + --> tests/ui/obfuscated_if_else.rs:79:13 | LL | let _ = true.then_some(42).unwrap_or(17) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 42 } else { 17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:71:14 + --> tests/ui/obfuscated_if_else.rs:83:14 | LL | let _ = *true.then_some(&42).unwrap_or(&17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { &42 } else { &17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:75:14 + --> tests/ui/obfuscated_if_else.rs:87:14 | LL | let _ = *true.then_some(&42).unwrap_or(&17) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { &42 } else { &17 }` -error: aborting due to 19 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/op_ref.fixed b/src/tools/clippy/tests/ui/op_ref.fixed index 46a59e419cce0..f412190b9fd9e 100644 --- a/src/tools/clippy/tests/ui/op_ref.fixed +++ b/src/tools/clippy/tests/ui/op_ref.fixed @@ -98,3 +98,15 @@ impl Mul
for A { self * &rhs } } + +mod issue_2597 { + fn ex1() { + let a: &str = "abc"; + let b: String = "abc".to_owned(); + println!("{}", a > &b); + } + + pub fn ex2(array: &[T], val: &T, idx: usize) -> bool { + &array[idx] < val + } +} diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs index e10840ff4b97b..a4bbd86c7e95b 100644 --- a/src/tools/clippy/tests/ui/op_ref.rs +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -98,3 +98,15 @@ impl Mul for A { self * &rhs } } + +mod issue_2597 { + fn ex1() { + let a: &str = "abc"; + let b: String = "abc".to_owned(); + println!("{}", a > &b); + } + + pub fn ex2(array: &[T], val: &T, idx: usize) -> bool { + &array[idx] < val + } +} diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index ee30988960175..fe3ac9e8f92c3 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -288,3 +288,17 @@ mod issue13964 { }; } } + +mod issue11059 { + use std::fmt::Debug; + + fn box_coercion_unsize(o: Option) -> Box { + if let Some(o) = o { Box::new(o) } else { Box::new("foo") } + } + + static S: String = String::new(); + + fn deref_with_overload(o: Option<&str>) -> &str { + if let Some(o) = o { o } else { &S } + } +} diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 525a5df4371c2..5b7498bc8e23b 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -351,3 +351,17 @@ mod issue13964 { }; } } + +mod issue11059 { + use std::fmt::Debug; + + fn box_coercion_unsize(o: Option) -> Box { + if let Some(o) = o { Box::new(o) } else { Box::new("foo") } + } + + static S: String = String::new(); + + fn deref_with_overload(o: Option<&str>) -> &str { + if let Some(o) = o { o } else { &S } + } +} diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 1794ac57fe5b1..a1119d75c231b 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -179,16 +179,20 @@ fn f() -> Option<()> { mod issue6675 { unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T { - #[allow(unused)] - let x = vec![0; 1000]; // future-proofing, make this function expensive. - &*p + unsafe { + #[allow(unused)] + let x = vec![0; 1000]; // future-proofing, make this function expensive. + &*p + } } unsafe fn foo() { - let s = "test".to_owned(); - let s = &s as *const _; - None.unwrap_or_else(|| ptr_to_ref(s)); - //~^ or_fun_call + unsafe { + let s = "test".to_owned(); + let s = &s as *const _; + None.unwrap_or_else(|| ptr_to_ref(s)); + //~^ or_fun_call + } } fn bar() { diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 256db343c0573..a7cd632bf166f 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -179,16 +179,20 @@ fn f() -> Option<()> { mod issue6675 { unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T { - #[allow(unused)] - let x = vec![0; 1000]; // future-proofing, make this function expensive. - &*p + unsafe { + #[allow(unused)] + let x = vec![0; 1000]; // future-proofing, make this function expensive. + &*p + } } unsafe fn foo() { - let s = "test".to_owned(); - let s = &s as *const _; - None.unwrap_or(ptr_to_ref(s)); - //~^ or_fun_call + unsafe { + let s = "test".to_owned(); + let s = &s as *const _; + None.unwrap_or(ptr_to_ref(s)); + //~^ or_fun_call + } } fn bar() { diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 93c87b2f12cde..35bda7e4d3314 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -125,91 +125,91 @@ LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:190:14 + --> tests/ui/or_fun_call.rs:193:18 | -LL | None.unwrap_or(ptr_to_ref(s)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` +LL | None.unwrap_or(ptr_to_ref(s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:197:14 + --> tests/ui/or_fun_call.rs:201:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:200:14 + --> tests/ui/or_fun_call.rs:204:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:276:25 + --> tests/ui/or_fun_call.rs:280:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:278:25 + --> tests/ui/or_fun_call.rs:282:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:310:18 + --> tests/ui/or_fun_call.rs:314:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:314:28 + --> tests/ui/or_fun_call.rs:318:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:318:27 + --> tests/ui/or_fun_call.rs:322:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:322:22 + --> tests/ui/or_fun_call.rs:326:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:326:23 + --> tests/ui/or_fun_call.rs:330:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:330:25 + --> tests/ui/or_fun_call.rs:334:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:334:25 + --> tests/ui/or_fun_call.rs:338:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:376:17 + --> tests/ui/or_fun_call.rs:380:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:381:17 + --> tests/ui/or_fun_call.rs:385:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:386:17 + --> tests/ui/or_fun_call.rs:390:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -229,19 +229,19 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:392:17 + --> tests/ui/or_fun_call.rs:396:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:397:17 + --> tests/ui/or_fun_call.rs:401:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:21 + --> tests/ui/or_fun_call.rs:408:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs index bdac3764bf167..643d8fedda984 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs @@ -1,5 +1,5 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] +#![allow(clippy::single_match)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs index 3c789f570b038..a1c447d258346 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs @@ -1,4 +1,3 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr index 763f688ea8975..b3ae63ec031a4 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:15:12 + --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:14:12 | LL | if let Value::B | Value::A(_) = ref_value {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | if let Value::B | Value::A(_) = ref_value {} = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:18:34 + --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:17:34 | LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:21:32 + --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:20:32 | LL | if let Value::B | Value::A(Some(_)) = *ref_value {} | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs index 7fc53d591a917..c5e395c4084f2 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs @@ -1,4 +1,3 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr index 70f7bdc389061..e18a88c2bf510 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:13:9 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:12:9 | LL | let Struct { .. } = ref_value; | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let Struct { .. } = ref_value; = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:16:33 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:15:33 | LL | if let &Struct { ref_inner: Some(_) } = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let &Struct { ref_inner: Some(_) } = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:19:32 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:18:32 | LL | if let Struct { ref_inner: Some(_) } = *ref_value {} | ^^^^^^^ @@ -25,7 +25,7 @@ LL | if let Struct { ref_inner: Some(_) } = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:37:12 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:36:12 | LL | if let StructEnum::Var { .. } = ref_value {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | if let StructEnum::Var { .. } = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:40:12 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:39:12 | LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:43:42 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:42:42 | LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} | ^^^^^^^ @@ -49,7 +49,7 @@ LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:46:41 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:45:41 | LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} | ^^^^^^^ @@ -57,7 +57,7 @@ LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:49:12 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:48:12 | LL | if let StructEnum::Empty = ref_value {} | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs index ecd95d9ae2b3b..8bec5abc88f17 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs @@ -1,4 +1,3 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr index d47c5d509c3fa..ee307be63c1a5 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:11:9 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:10:9 | LL | let TupleStruct(_) = ref_value; | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let TupleStruct(_) = ref_value; = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:14:25 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:13:25 | LL | if let &TupleStruct(Some(_)) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let &TupleStruct(Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:17:24 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:16:24 | LL | if let TupleStruct(Some(_)) = *ref_value {} | ^^^^^^^ @@ -25,7 +25,7 @@ LL | if let TupleStruct(Some(_)) = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:35:12 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:34:12 | LL | if let TupleEnum::Var(_) = ref_value {} | ^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | if let TupleEnum::Var(_) = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:38:28 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:37:28 | LL | if let &TupleEnum::Var(Some(_)) = ref_value {} | ^^^^^^^ @@ -41,7 +41,7 @@ LL | if let &TupleEnum::Var(Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:41:27 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:40:27 | LL | if let TupleEnum::Var(Some(_)) = *ref_value {} | ^^^^^^^ @@ -49,7 +49,7 @@ LL | if let TupleEnum::Var(Some(_)) = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:44:12 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:43:12 | LL | if let TupleEnum::Empty = ref_value {} | ^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | if let TupleEnum::Empty = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:60:9 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:59:9 | LL | let (_a, _b) = ref_value; | ^^^^^^^^ @@ -65,7 +65,7 @@ LL | let (_a, _b) = ref_value; = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:63:18 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:62:18 | LL | if let &(_a, Some(_)) = ref_value {} | ^^^^^^^ @@ -73,7 +73,7 @@ LL | if let &(_a, Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:66:17 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:65:17 | LL | if let (_a, Some(_)) = *ref_value {} | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs index 0bbc26a0c27c5..49ea1d3f7a67c 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs @@ -1,5 +1,10 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] +#![allow( + clippy::match_ref_pats, + clippy::never_loop, + clippy::redundant_pattern_matching, + clippy::single_match +)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr index 3f6b5feb9b07b..cd604d604c12c 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:11:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:16:9 | LL | Some(_) => (), | ^^^^^^^ @@ -9,7 +9,7 @@ LL | Some(_) => (), = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:31:12 + --> tests/ui/pattern_type_mismatch/syntax.rs:36:12 | LL | if let Some(_) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let Some(_) = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:43:15 + --> tests/ui/pattern_type_mismatch/syntax.rs:48:15 | LL | while let Some(_) = ref_value { | ^^^^^^^ @@ -25,7 +25,7 @@ LL | while let Some(_) = ref_value { = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:63:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:68:9 | LL | for (_a, _b) in slice.iter() {} | ^^^^^^^^ @@ -33,7 +33,7 @@ LL | for (_a, _b) in slice.iter() {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:74:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:79:9 | LL | let (_n, _m) = ref_value; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | let (_n, _m) = ref_value; = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:84:12 + --> tests/ui/pattern_type_mismatch/syntax.rs:89:12 | LL | fn foo((_a, _b): &(i32, i32)) {} | ^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn foo((_a, _b): &(i32, i32)) {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:99:10 + --> tests/ui/pattern_type_mismatch/syntax.rs:104:10 | LL | foo(|(_a, _b)| ()); | ^^^^^^^^ @@ -57,7 +57,7 @@ LL | foo(|(_a, _b)| ()); = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:116:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:121:9 | LL | Some(_) => (), | ^^^^^^^ @@ -65,7 +65,7 @@ LL | Some(_) => (), = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:137:17 + --> tests/ui/pattern_type_mismatch/syntax.rs:142:17 | LL | Some(_) => (), | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed index bcb8ecfc38d25..a6dd5fd63a9f6 100644 --- a/src/tools/clippy/tests/ui/patterns.fixed +++ b/src/tools/clippy/tests/ui/patterns.fixed @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![warn(clippy::all)] -#![allow(unused)] #![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs index 19639ebd13d60..64bfbdecdac2b 100644 --- a/src/tools/clippy/tests/ui/patterns.rs +++ b/src/tools/clippy/tests/ui/patterns.rs @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![warn(clippy::all)] -#![allow(unused)] #![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] diff --git a/src/tools/clippy/tests/ui/patterns.stderr b/src/tools/clippy/tests/ui/patterns.stderr index b9950fe181cc7..ff5e1a8de90a4 100644 --- a/src/tools/clippy/tests/ui/patterns.stderr +++ b/src/tools/clippy/tests/ui/patterns.stderr @@ -1,5 +1,5 @@ error: the `y @ _` pattern can be written as just `y` - --> tests/ui/patterns.rs:14:9 + --> tests/ui/patterns.rs:12:9 | LL | y @ _ => (), | ^^^^^ help: try: `y` @@ -8,13 +8,13 @@ LL | y @ _ => (), = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern)]` error: the `x @ _` pattern can be written as just `x` - --> tests/ui/patterns.rs:30:9 + --> tests/ui/patterns.rs:28:9 | LL | ref mut x @ _ => { | ^^^^^^^^^^^^^ help: try: `ref mut x` error: the `x @ _` pattern can be written as just `x` - --> tests/ui/patterns.rs:39:9 + --> tests/ui/patterns.rs:37:9 | LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs index 171716be26024..7f69c61b0289c 100644 --- a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs +++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs @@ -6,29 +6,37 @@ use core::arch::asm; unsafe fn nomem_bad(p: &i32) { - asm!( - "asdf {p1}, {p2}, {p3}", - p1 = in(reg) p, - //~^ pointers_in_nomem_asm_block + unsafe { + asm!( + "asdf {p1}, {p2}, {p3}", + p1 = in(reg) p, + //~^ pointers_in_nomem_asm_block - p2 = in(reg) p as *const _ as usize, - p3 = in(reg) p, - options(nomem, nostack, preserves_flags) - ); + p2 = in(reg) p as *const _ as usize, + p3 = in(reg) p, + options(nomem, nostack, preserves_flags) + ); + } } unsafe fn nomem_good(p: &i32) { - asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); - let p = p as *const i32 as usize; - asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + unsafe { + asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + } } unsafe fn nomem_bad2(p: &mut i32) { - asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); - //~^ pointers_in_nomem_asm_block + unsafe { + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ pointers_in_nomem_asm_block + } } unsafe fn nomem_fn(p: extern "C" fn()) { - asm!("call {p}", p = in(reg) p, options(nomem)); - //~^ pointers_in_nomem_asm_block + unsafe { + asm!("call {p}", p = in(reg) p, options(nomem)); + //~^ pointers_in_nomem_asm_block + } } diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr index ca24e34f63c0f..eabac2444eccc 100644 --- a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr +++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr @@ -1,11 +1,11 @@ error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:11:9 + --> tests/ui/pointers_in_nomem_asm_block.rs:12:13 | -LL | p1 = in(reg) p, - | ^^^^^^^^^^^^^^ +LL | p1 = in(reg) p, + | ^^^^^^^^^^^^^^ ... -LL | p3 = in(reg) p, - | ^^^^^^^^^^^^^^ +LL | p3 = in(reg) p, + | ^^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint @@ -13,19 +13,19 @@ LL | p3 = in(reg) p, = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:27:22 + --> tests/ui/pointers_in_nomem_asm_block.rs:32:26 | -LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); - | ^^^^^^^^^^^^^ +LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:32:22 + --> tests/ui/pointers_in_nomem_asm_block.rs:39:26 | -LL | asm!("call {p}", p = in(reg) p, options(nomem)); - | ^^^^^^^^^^^^^ +LL | asm!("call {p}", p = in(reg) p, options(nomem)); + | ^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed index 6dded72d3e191..79bfae1f7ebb4 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -12,11 +12,13 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; unsafe fn ptr_to_ref(p: *const T, om: *mut U) { - let _: &mut T = std::mem::transmute(p.cast_mut()); - //~^ ptr_cast_constness - let _ = &mut *p.cast_mut(); - //~^ ptr_cast_constness - let _: &T = &*(om as *const T); + unsafe { + let _: &mut T = std::mem::transmute(p.cast_mut()); + //~^ ptr_cast_constness + let _ = &mut *p.cast_mut(); + //~^ ptr_cast_constness + let _: &T = &*(om as *const T); + } } #[inline_macros] @@ -98,3 +100,9 @@ fn null_pointers() { let _ = external!(ptr::null::() as *mut u32); let _ = external!(ptr::null::().cast_mut()); } + +fn issue14621() { + let mut local = 4; + let _ = std::ptr::addr_of_mut!(local).cast_const(); + //~^ ptr_cast_constness +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs index e9629f5290ec1..f6590dabd5b84 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -12,11 +12,13 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; unsafe fn ptr_to_ref(p: *const T, om: *mut U) { - let _: &mut T = std::mem::transmute(p as *mut T); - //~^ ptr_cast_constness - let _ = &mut *(p as *mut T); - //~^ ptr_cast_constness - let _: &T = &*(om as *const T); + unsafe { + let _: &mut T = std::mem::transmute(p as *mut T); + //~^ ptr_cast_constness + let _ = &mut *(p as *mut T); + //~^ ptr_cast_constness + let _: &T = &*(om as *const T); + } } #[inline_macros] @@ -98,3 +100,9 @@ fn null_pointers() { let _ = external!(ptr::null::() as *mut u32); let _ = external!(ptr::null::().cast_mut()); } + +fn issue14621() { + let mut local = 4; + let _ = std::ptr::addr_of_mut!(local) as *const _; + //~^ ptr_cast_constness +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr index 1eeeef7470138..0b1644168ff51 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -1,74 +1,74 @@ error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:15:41 + --> tests/ui/ptr_cast_constness.rs:16:45 | -LL | let _: &mut T = std::mem::transmute(p as *mut T); - | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` | = note: `-D clippy::ptr-cast-constness` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_cast_constness)]` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:17:19 + --> tests/ui/ptr_cast_constness.rs:18:23 | -LL | let _ = &mut *(p as *mut T); - | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` +LL | let _ = &mut *(p as *mut T); + | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:33:17 + --> tests/ui/ptr_cast_constness.rs:35:17 | LL | let _ = *ptr_ptr as *mut u32; | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:37:13 + --> tests/ui/ptr_cast_constness.rs:39:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:39:13 + --> tests/ui/ptr_cast_constness.rs:41:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:73:13 + --> tests/ui/ptr_cast_constness.rs:75:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:75:13 + --> tests/ui/ptr_cast_constness.rs:77:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:82:13 + --> tests/ui/ptr_cast_constness.rs:84:13 | LL | let _ = ptr::null::() as *mut String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` error: `as` casting to make a mutable null pointer into a const null pointer - --> tests/ui/ptr_cast_constness.rs:84:13 + --> tests/ui/ptr_cast_constness.rs:86:13 | LL | let _ = ptr::null_mut::() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:86:13 + --> tests/ui/ptr_cast_constness.rs:88:13 | LL | let _ = ptr::null::().cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:88:13 + --> tests/ui/ptr_cast_constness.rs:90:13 | LL | let _ = ptr::null_mut::().cast_const(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:92:21 + --> tests/ui/ptr_cast_constness.rs:94:21 | LL | let _ = inline!(ptr::null::() as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` @@ -76,12 +76,18 @@ LL | let _ = inline!(ptr::null::() as *mut u32); = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:94:21 + --> tests/ui/ptr_cast_constness.rs:96:21 | LL | let _ = inline!(ptr::null::().cast_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` | = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 13 previous errors +error: `as` casting between raw pointers while changing only its constness + --> tests/ui/ptr_cast_constness.rs:106:13 + | +LL | let _ = std::ptr::addr_of_mut!(local) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_eq.fixed b/src/tools/clippy/tests/ui/ptr_eq.fixed index df6305ed497e8..484ff30732392 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.fixed +++ b/src/tools/clippy/tests/ui/ptr_eq.fixed @@ -4,6 +4,9 @@ macro_rules! mac { ($a:expr, $b:expr) => { $a as *const _ as usize == $b as *const _ as usize }; + (cast $a:expr) => { + $a as *const [i32; 3] + }; } macro_rules! another_mac { @@ -51,4 +54,8 @@ fn main() { #[allow(clippy::eq_op)] let _issue14337 = std::ptr::eq(main as *const (), main as *const ()); //~^ ptr_eq + + // Do not peel the content of macros + let _ = std::ptr::eq(mac!(cast a), mac!(cast b)); + //~^ ptr_eq } diff --git a/src/tools/clippy/tests/ui/ptr_eq.rs b/src/tools/clippy/tests/ui/ptr_eq.rs index 0ed0ff0d13716..f28707cc3e927 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.rs +++ b/src/tools/clippy/tests/ui/ptr_eq.rs @@ -4,6 +4,9 @@ macro_rules! mac { ($a:expr, $b:expr) => { $a as *const _ as usize == $b as *const _ as usize }; + (cast $a:expr) => { + $a as *const [i32; 3] + }; } macro_rules! another_mac { @@ -51,4 +54,8 @@ fn main() { #[allow(clippy::eq_op)] let _issue14337 = main as *const () == main as *const (); //~^ ptr_eq + + // Do not peel the content of macros + let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + //~^ ptr_eq } diff --git a/src/tools/clippy/tests/ui/ptr_eq.stderr b/src/tools/clippy/tests/ui/ptr_eq.stderr index 33190df284a3f..906831b9e0312 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.stderr +++ b/src/tools/clippy/tests/ui/ptr_eq.stderr @@ -1,5 +1,5 @@ error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:19:13 + --> tests/ui/ptr_eq.rs:22:13 | LL | let _ = a as *const _ as usize == b as *const _ as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` @@ -8,52 +8,58 @@ LL | let _ = a as *const _ as usize == b as *const _ as usize; = help: to override `-D warnings` add `#[allow(clippy::ptr_eq)]` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:21:13 + --> tests/ui/ptr_eq.rs:24:13 | LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:23:13 + --> tests/ui/ptr_eq.rs:26:13 | LL | let _ = a.as_ptr() == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b as *const _)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:25:13 + --> tests/ui/ptr_eq.rs:28:13 | LL | let _ = a.as_ptr() == b.as_ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_ptr(), b.as_ptr())` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:36:13 + --> tests/ui/ptr_eq.rs:39:13 | LL | let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b as *mut [i32] as *mut _)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:38:13 + --> tests/ui/ptr_eq.rs:41:13 | LL | let _ = a.as_mut_ptr() == b.as_mut_ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a.as_mut_ptr(), b.as_mut_ptr())` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:45:13 + --> tests/ui/ptr_eq.rs:48:13 | LL | let _ = x as *const u32 == y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:48:13 + --> tests/ui/ptr_eq.rs:51:13 | LL | let _ = x as *const u32 != y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:52:23 + --> tests/ui/ptr_eq.rs:55:23 | LL | let _issue14337 = main as *const () == main as *const (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(main as *const (), main as *const ())` -error: aborting due to 9 previous errors +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:59:13 + | +LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index fff41f578284d..6bd071d07f523 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -3,6 +3,8 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] +use std::sync::MutexGuard; + fn some_func(a: Option) -> Option { a?; @@ -430,3 +432,9 @@ fn msrv_1_13(arg: Option) -> Option { println!("{}", val); Some(val) } + +fn issue_14615(a: MutexGuard>) -> Option { + let a = (*a)?; + //~^^^ question_mark + Some(format!("{a}")) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index c71c8ee984edd..dd093c9bf480c 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -3,6 +3,8 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] +use std::sync::MutexGuard; + fn some_func(a: Option) -> Option { if a.is_none() { //~^ question_mark @@ -524,3 +526,11 @@ fn msrv_1_13(arg: Option) -> Option { println!("{}", val); Some(val) } + +fn issue_14615(a: MutexGuard>) -> Option { + let Some(a) = *a else { + return None; + }; + //~^^^ question_mark + Some(format!("{a}")) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 183b8866a7481..8fe04b895cea2 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:7:5 + --> tests/ui/question_mark.rs:9:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:53:9 + --> tests/ui/question_mark.rs:55:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:58:9 + --> tests/ui/question_mark.rs:60:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:63:17 + --> tests/ui/question_mark.rs:65:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:70:17 + --> tests/ui/question_mark.rs:72:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:88:9 + --> tests/ui/question_mark.rs:90:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:97:9 + --> tests/ui/question_mark.rs:99:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:106:9 + --> tests/ui/question_mark.rs:108:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:114:26 + --> tests/ui/question_mark.rs:116:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:125:17 + --> tests/ui/question_mark.rs:127:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:147:5 + --> tests/ui/question_mark.rs:149:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:152:16 + --> tests/ui/question_mark.rs:154:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:163:5 + --> tests/ui/question_mark.rs:165:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:169:5 + --> tests/ui/question_mark.rs:171:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:196:13 + --> tests/ui/question_mark.rs:198:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:199:5 + --> tests/ui/question_mark.rs:201:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:204:16 + --> tests/ui/question_mark.rs:206:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:210:5 + --> tests/ui/question_mark.rs:212:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:302:5 + --> tests/ui/question_mark.rs:304:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:310:5 + --> tests/ui/question_mark.rs:312:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:388:13 + --> tests/ui/question_mark.rs:390:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:449:5 + --> tests/ui/question_mark.rs:451:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:464:5 + --> tests/ui/question_mark.rs:466:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:474:5 + --> tests/ui/question_mark.rs:476:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:485:5 + --> tests/ui/question_mark.rs:487:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:489:5 + --> tests/ui/question_mark.rs:491:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:493:5 + --> tests/ui/question_mark.rs:495:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:515:5 + --> tests/ui/question_mark.rs:517:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:519:15 + --> tests/ui/question_mark.rs:521:15 | LL | let val = match arg { | _______________^ @@ -275,5 +275,13 @@ LL | | None => return None, LL | | }; | |_____^ help: try instead: `arg?` -error: aborting due to 29 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:531:5 + | +LL | / let Some(a) = *a else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let a = (*a)?;` + +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs index 0562f7dcc7614..832f147c6ed53 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.rs +++ b/src/tools/clippy/tests/ui/redundant_allocation.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![allow(clippy::boxed_local, clippy::disallowed_names)] pub struct MyStruct; diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr index 44d30f95d7bc3..886ed2088c67b 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.stderr +++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr @@ -1,5 +1,5 @@ error: usage of `Box>` - --> tests/ui/redundant_allocation.rs:16:30 + --> tests/ui/redundant_allocation.rs:15:30 | LL | pub fn box_test6(foo: Box>) {} | ^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | pub fn box_test6(foo: Box>) {} = help: to override `-D warnings` add `#[allow(clippy::redundant_allocation)]` error: usage of `Box>` - --> tests/ui/redundant_allocation.rs:19:30 + --> tests/ui/redundant_allocation.rs:18:30 | LL | pub fn box_test7(foo: Box>) {} | ^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | pub fn box_test7(foo: Box>) {} = help: consider using just `Box` or `Arc` error: usage of `Box>>` - --> tests/ui/redundant_allocation.rs:22:27 + --> tests/ui/redundant_allocation.rs:21:27 | LL | pub fn box_test8() -> Box>> { | ^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | pub fn box_test8() -> Box>> { = help: consider using just `Box>` or `Rc>` error: usage of `Box>` - --> tests/ui/redundant_allocation.rs:28:30 + --> tests/ui/redundant_allocation.rs:27:30 | LL | pub fn box_test9(foo: Box>) -> Box>> { | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | pub fn box_test9(foo: Box>) -> Box>> { = help: consider using just `Box` or `Arc` error: usage of `Box>>` - --> tests/ui/redundant_allocation.rs:28:46 + --> tests/ui/redundant_allocation.rs:27:46 | LL | pub fn box_test9(foo: Box>) -> Box>> { | ^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | pub fn box_test9(foo: Box>) -> Box>> { = help: consider using just `Box>` or `Arc>` error: usage of `Rc>` - --> tests/ui/redundant_allocation.rs:42:24 + --> tests/ui/redundant_allocation.rs:41:24 | LL | pub fn rc_test5(a: Rc>) {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | pub fn rc_test5(a: Rc>) {} = help: consider using just `Rc` or `Box` error: usage of `Rc>` - --> tests/ui/redundant_allocation.rs:45:24 + --> tests/ui/redundant_allocation.rs:44:24 | LL | pub fn rc_test7(a: Rc>) {} | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | pub fn rc_test7(a: Rc>) {} = help: consider using just `Rc` or `Arc` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:48:26 + --> tests/ui/redundant_allocation.rs:47:26 | LL | pub fn rc_test8() -> Rc>> { | ^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | pub fn rc_test8() -> Rc>> { = help: consider using just `Rc>` or `Box>` error: usage of `Rc>` - --> tests/ui/redundant_allocation.rs:54:29 + --> tests/ui/redundant_allocation.rs:53:29 | LL | pub fn rc_test9(foo: Rc>) -> Rc>> { | ^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | pub fn rc_test9(foo: Rc>) -> Rc>> { = help: consider using just `Rc` or `Arc` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:54:44 + --> tests/ui/redundant_allocation.rs:53:44 | LL | pub fn rc_test9(foo: Rc>) -> Rc>> { | ^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | pub fn rc_test9(foo: Rc>) -> Rc>> { = help: consider using just `Rc>` or `Arc>` error: usage of `Arc>` - --> tests/ui/redundant_allocation.rs:68:25 + --> tests/ui/redundant_allocation.rs:67:25 | LL | pub fn arc_test5(a: Arc>) {} | ^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | pub fn arc_test5(a: Arc>) {} = help: consider using just `Arc` or `Box` error: usage of `Arc>` - --> tests/ui/redundant_allocation.rs:71:25 + --> tests/ui/redundant_allocation.rs:70:25 | LL | pub fn arc_test6(a: Arc>) {} | ^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | pub fn arc_test6(a: Arc>) {} = help: consider using just `Arc` or `Rc` error: usage of `Arc>>` - --> tests/ui/redundant_allocation.rs:74:27 + --> tests/ui/redundant_allocation.rs:73:27 | LL | pub fn arc_test8() -> Arc>> { | ^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | pub fn arc_test8() -> Arc>> { = help: consider using just `Arc>` or `Box>` error: usage of `Arc>` - --> tests/ui/redundant_allocation.rs:80:30 + --> tests/ui/redundant_allocation.rs:79:30 | LL | pub fn arc_test9(foo: Arc>) -> Arc>> { | ^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = help: consider using just `Arc` or `Rc` error: usage of `Arc>>` - --> tests/ui/redundant_allocation.rs:80:45 + --> tests/ui/redundant_allocation.rs:79:45 | LL | pub fn arc_test9(foo: Arc>) -> Arc>> { | ^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = help: consider using just `Arc>` or `Rc>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:105:27 + --> tests/ui/redundant_allocation.rs:104:27 | LL | pub fn test_rc_box(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | pub fn test_rc_box(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:138:31 + --> tests/ui/redundant_allocation.rs:137:31 | LL | pub fn test_rc_box_str(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | pub fn test_rc_box_str(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:141:33 + --> tests/ui/redundant_allocation.rs:140:33 | LL | pub fn test_rc_box_slice(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | pub fn test_rc_box_slice(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:144:32 + --> tests/ui/redundant_allocation.rs:143:32 | LL | pub fn test_rc_box_path(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^ @@ -172,7 +172,7 @@ LL | pub fn test_rc_box_path(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:147:34 + --> tests/ui/redundant_allocation.rs:146:34 | LL | pub fn test_rc_box_custom(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed index 7773ba11f973e..dbc6c0794d1a7 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::disallowed_names, unused_variables, dead_code)] -#![allow(unused_imports)] +#![allow(clippy::disallowed_names)] pub struct MyStruct; diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs index fb86ed2b3cfdf..05b6429492ce7 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::disallowed_names, unused_variables, dead_code)] -#![allow(unused_imports)] +#![allow(clippy::disallowed_names)] pub struct MyStruct; diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr index ed8282cc82ce9..4073766887174 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr @@ -1,5 +1,5 @@ error: usage of `Box<&T>` - --> tests/ui/redundant_allocation_fixable.rs:23:30 + --> tests/ui/redundant_allocation_fixable.rs:21:30 | LL | pub fn box_test1(foo: Box<&T>) {} | ^^^^^^^ help: try: `&T` @@ -9,7 +9,7 @@ LL | pub fn box_test1(foo: Box<&T>) {} = help: to override `-D warnings` add `#[allow(clippy::redundant_allocation)]` error: usage of `Box<&MyStruct>` - --> tests/ui/redundant_allocation_fixable.rs:26:27 + --> tests/ui/redundant_allocation_fixable.rs:24:27 | LL | pub fn box_test2(foo: Box<&MyStruct>) {} | ^^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -17,7 +17,7 @@ LL | pub fn box_test2(foo: Box<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap error: usage of `Box<&MyEnum>` - --> tests/ui/redundant_allocation_fixable.rs:29:27 + --> tests/ui/redundant_allocation_fixable.rs:27:27 | LL | pub fn box_test3(foo: Box<&MyEnum>) {} | ^^^^^^^^^^^^ help: try: `&MyEnum` @@ -25,7 +25,7 @@ LL | pub fn box_test3(foo: Box<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap error: usage of `Box>` - --> tests/ui/redundant_allocation_fixable.rs:34:30 + --> tests/ui/redundant_allocation_fixable.rs:32:30 | LL | pub fn box_test5(foo: Box>) {} | ^^^^^^^^^^^ help: try: `Box` @@ -33,7 +33,7 @@ LL | pub fn box_test5(foo: Box>) {} = note: `Box` is already on the heap, `Box>` makes an extra allocation error: usage of `Rc<&T>` - --> tests/ui/redundant_allocation_fixable.rs:44:29 + --> tests/ui/redundant_allocation_fixable.rs:42:29 | LL | pub fn rc_test1(foo: Rc<&T>) {} | ^^^^^^ help: try: `&T` @@ -41,7 +41,7 @@ LL | pub fn rc_test1(foo: Rc<&T>) {} = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap error: usage of `Rc<&MyStruct>` - --> tests/ui/redundant_allocation_fixable.rs:47:26 + --> tests/ui/redundant_allocation_fixable.rs:45:26 | LL | pub fn rc_test2(foo: Rc<&MyStruct>) {} | ^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -49,7 +49,7 @@ LL | pub fn rc_test2(foo: Rc<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap error: usage of `Rc<&MyEnum>` - --> tests/ui/redundant_allocation_fixable.rs:50:26 + --> tests/ui/redundant_allocation_fixable.rs:48:26 | LL | pub fn rc_test3(foo: Rc<&MyEnum>) {} | ^^^^^^^^^^^ help: try: `&MyEnum` @@ -57,7 +57,7 @@ LL | pub fn rc_test3(foo: Rc<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap error: usage of `Rc>` - --> tests/ui/redundant_allocation_fixable.rs:55:24 + --> tests/ui/redundant_allocation_fixable.rs:53:24 | LL | pub fn rc_test6(a: Rc>) {} | ^^^^^^^^^^^^ help: try: `Rc` @@ -65,7 +65,7 @@ LL | pub fn rc_test6(a: Rc>) {} = note: `Rc` is already on the heap, `Rc>` makes an extra allocation error: usage of `Arc<&T>` - --> tests/ui/redundant_allocation_fixable.rs:65:30 + --> tests/ui/redundant_allocation_fixable.rs:63:30 | LL | pub fn arc_test1(foo: Arc<&T>) {} | ^^^^^^^ help: try: `&T` @@ -73,7 +73,7 @@ LL | pub fn arc_test1(foo: Arc<&T>) {} = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap error: usage of `Arc<&MyStruct>` - --> tests/ui/redundant_allocation_fixable.rs:68:27 + --> tests/ui/redundant_allocation_fixable.rs:66:27 | LL | pub fn arc_test2(foo: Arc<&MyStruct>) {} | ^^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -81,7 +81,7 @@ LL | pub fn arc_test2(foo: Arc<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap error: usage of `Arc<&MyEnum>` - --> tests/ui/redundant_allocation_fixable.rs:71:27 + --> tests/ui/redundant_allocation_fixable.rs:69:27 | LL | pub fn arc_test3(foo: Arc<&MyEnum>) {} | ^^^^^^^^^^^^ help: try: `&MyEnum` @@ -89,7 +89,7 @@ LL | pub fn arc_test3(foo: Arc<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap error: usage of `Arc>` - --> tests/ui/redundant_allocation_fixable.rs:76:25 + --> tests/ui/redundant_allocation_fixable.rs:74:25 | LL | pub fn arc_test7(a: Arc>) {} | ^^^^^^^^^^^^^^ help: try: `Arc` diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed index 23c00b34a00a8..c1c389f7c4ed2 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.fixed +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -259,3 +259,35 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +mod issue10074 { + #[derive(Debug, Clone)] + enum MyEnum { + A = 1, + } + + fn false_positive_on_as() { + let e = MyEnum::A; + let v = e.clone() as u16; + + println!("{e:?}"); + println!("{v}"); + } +} + +mod issue13900 { + use std::fmt::Display; + + fn do_something(f: impl Display + Clone) -> String { + let g = f.clone(); + format!("{} + {}", f, g) + } + + fn regression() { + let mut a = String::new(); + let mut b = String::new(); + for _ in 1..10 { + b = a.clone(); + } + } +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs index f9fe8ba0236d0..78d98762efc86 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.rs +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -259,3 +259,35 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +mod issue10074 { + #[derive(Debug, Clone)] + enum MyEnum { + A = 1, + } + + fn false_positive_on_as() { + let e = MyEnum::A; + let v = e.clone() as u16; + + println!("{e:?}"); + println!("{v}"); + } +} + +mod issue13900 { + use std::fmt::Display; + + fn do_something(f: impl Display + Clone) -> String { + let g = f.clone(); + format!("{} + {}", f, g) + } + + fn regression() { + let mut a = String::new(); + let mut b = String::new(); + for _ in 1..10 { + b = a.clone(); + } + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed index 549c97d9534ad..1cec19ab8c99d 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::redundant_pattern_matching)] -#![allow(unused_must_use)] +#![warn(clippy::redundant_pattern_matching)] #![allow( clippy::match_like_matches_macro, clippy::needless_bool, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs index decb1396d56dd..123573a8602b6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::redundant_pattern_matching)] -#![allow(unused_must_use)] +#![warn(clippy::redundant_pattern_matching)] #![allow( clippy::match_like_matches_macro, clippy::needless_bool, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr index 66d2cecdc0c91..3be7cf81afe95 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:15:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:14:12 | LL | if let V4(_) = &ipaddr {} | -------^^^^^---------- help: try: `if ipaddr.is_ipv4()` @@ -8,43 +8,43 @@ LL | if let V4(_) = &ipaddr {} = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:18:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:17:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:21:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:20:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:25:8 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:24:8 | LL | if matches!(V4(Ipv4Addr::LOCALHOST), V4(_)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:29:8 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:28:8 | LL | if matches!(V6(Ipv6Addr::LOCALHOST), V6(_)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:32:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:31:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:35:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:34:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:46:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:45:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | @@ -54,7 +54,7 @@ LL | | }; | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:52:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:51:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | @@ -64,7 +64,7 @@ LL | | }; | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:58:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:57:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:64:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:63:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | @@ -84,49 +84,49 @@ LL | | }; | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:70:20 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:69:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:79:20 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:78:20 | LL | let _ = if let V4(_) = gen_ipaddr() { | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:82:19 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:81:19 | LL | } else if let V6(_) = gen_ipaddr() { | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:95:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:94:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:98:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:97:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:101:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:100:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:104:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:103:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:107:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:106:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | @@ -136,7 +136,7 @@ LL | | }; | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:113:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:112:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index 5585006dc362b..33a5308bd3574 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -1,14 +1,12 @@ -#![warn(clippy::all)] +#![feature(let_chains, if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else )] -#![feature(let_chains, if_let_guard)] fn issue_11174(boolean: bool, maybe_some: Option) -> bool { maybe_some.is_none() && (!boolean) diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index 581a432f38e11..60bce2994ea3a 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -1,14 +1,12 @@ -#![warn(clippy::all)] +#![feature(let_chains, if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else )] -#![feature(let_chains, if_let_guard)] fn issue_11174(boolean: bool, maybe_some: Option) -> bool { matches!(maybe_some, None if !boolean) diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 681602567d2f2..e5a6598898aa1 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:14:5 + --> tests/ui/redundant_pattern_matching_option.rs:12:5 | LL | matches!(maybe_some, None if !boolean) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (!boolean)` @@ -8,55 +8,55 @@ LL | matches!(maybe_some, None if !boolean) = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:19:13 + --> tests/ui/redundant_pattern_matching_option.rs:17:13 | LL | let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (boolean || boolean2)` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:35:12 + --> tests/ui/redundant_pattern_matching_option.rs:33:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:38:12 + --> tests/ui/redundant_pattern_matching_option.rs:36:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:41:12 + --> tests/ui/redundant_pattern_matching_option.rs:39:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:48:15 + --> tests/ui/redundant_pattern_matching_option.rs:46:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:51:15 + --> tests/ui/redundant_pattern_matching_option.rs:49:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:54:15 + --> tests/ui/redundant_pattern_matching_option.rs:52:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:58:15 + --> tests/ui/redundant_pattern_matching_option.rs:56:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:67:5 + --> tests/ui/redundant_pattern_matching_option.rs:65:5 | LL | / match Some(42) { LL | | @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:73:5 + --> tests/ui/redundant_pattern_matching_option.rs:71:5 | LL | / match None::<()> { LL | | @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:79:13 + --> tests/ui/redundant_pattern_matching_option.rs:77:13 | LL | let _ = match None::<()> { | _____________^ @@ -87,55 +87,55 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:86:20 + --> tests/ui/redundant_pattern_matching_option.rs:84:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:93:20 + --> tests/ui/redundant_pattern_matching_option.rs:91:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:96:19 + --> tests/ui/redundant_pattern_matching_option.rs:94:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:103:12 + --> tests/ui/redundant_pattern_matching_option.rs:101:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:119:12 + --> tests/ui/redundant_pattern_matching_option.rs:117:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:122:12 + --> tests/ui/redundant_pattern_matching_option.rs:120:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:125:15 + --> tests/ui/redundant_pattern_matching_option.rs:123:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:128:15 + --> tests/ui/redundant_pattern_matching_option.rs:126:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:131:5 + --> tests/ui/redundant_pattern_matching_option.rs:129:5 | LL | / match Some(42) { LL | | @@ -145,7 +145,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:137:5 + --> tests/ui/redundant_pattern_matching_option.rs:135:5 | LL | / match None::<()> { LL | | @@ -155,19 +155,19 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:146:12 + --> tests/ui/redundant_pattern_matching_option.rs:144:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:148:12 + --> tests/ui/redundant_pattern_matching_option.rs:146:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:155:5 + --> tests/ui/redundant_pattern_matching_option.rs:153:5 | LL | / match x { LL | | @@ -177,7 +177,7 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:161:5 + --> tests/ui/redundant_pattern_matching_option.rs:159:5 | LL | / match x { LL | | @@ -187,7 +187,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:167:5 + --> tests/ui/redundant_pattern_matching_option.rs:165:5 | LL | / match x { LL | | @@ -197,7 +197,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:173:5 + --> tests/ui/redundant_pattern_matching_option.rs:171:5 | LL | / match x { LL | | @@ -207,19 +207,19 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:189:13 + --> tests/ui/redundant_pattern_matching_option.rs:187:13 | LL | let _ = matches!(x, Some(_)); | ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:192:13 + --> tests/ui/redundant_pattern_matching_option.rs:190:13 | LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:203:17 + --> tests/ui/redundant_pattern_matching_option.rs:201:17 | LL | let _ = matches!(*p, None); | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed index c8e18e8676f21..800889b5fda0a 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs index 727503d21a54a..1668c2ff2bbac 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr index 5f659184f7b38..5cd9d9636e466 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:15:12 + --> tests/ui/redundant_pattern_matching_poll.rs:13:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` @@ -8,49 +8,49 @@ LL | if let Pending = Pending::<()> {} = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:18:12 + --> tests/ui/redundant_pattern_matching_poll.rs:16:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:21:12 + --> tests/ui/redundant_pattern_matching_poll.rs:19:12 | LL | if let Ready(_) = Ready(42) { | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:29:8 + --> tests/ui/redundant_pattern_matching_poll.rs:27:8 | LL | if matches!(Ready(42), Ready(_)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:33:8 + --> tests/ui/redundant_pattern_matching_poll.rs:31:8 | LL | if matches!(Pending::<()>, Pending) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:36:15 + --> tests/ui/redundant_pattern_matching_poll.rs:34:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:39:15 + --> tests/ui/redundant_pattern_matching_poll.rs:37:15 | LL | while let Pending = Ready(42) {} | ----------^^^^^^^------------ help: try: `while Ready(42).is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:42:15 + --> tests/ui/redundant_pattern_matching_poll.rs:40:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:49:5 + --> tests/ui/redundant_pattern_matching_poll.rs:47:5 | LL | / match Ready(42) { LL | | @@ -60,7 +60,7 @@ LL | | }; | |_____^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:55:5 + --> tests/ui/redundant_pattern_matching_poll.rs:53:5 | LL | / match Pending::<()> { LL | | @@ -70,7 +70,7 @@ LL | | }; | |_____^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:61:13 + --> tests/ui/redundant_pattern_matching_poll.rs:59:13 | LL | let _ = match Pending::<()> { | _____________^ @@ -81,49 +81,49 @@ LL | | }; | |_____^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:68:20 + --> tests/ui/redundant_pattern_matching_poll.rs:66:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:73:20 + --> tests/ui/redundant_pattern_matching_poll.rs:71:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:76:19 + --> tests/ui/redundant_pattern_matching_poll.rs:74:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:93:12 + --> tests/ui/redundant_pattern_matching_poll.rs:91:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:96:12 + --> tests/ui/redundant_pattern_matching_poll.rs:94:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:99:15 + --> tests/ui/redundant_pattern_matching_poll.rs:97:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:102:15 + --> tests/ui/redundant_pattern_matching_poll.rs:100:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:105:5 + --> tests/ui/redundant_pattern_matching_poll.rs:103:5 | LL | / match Ready(42) { LL | | @@ -133,7 +133,7 @@ LL | | }; | |_____^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:111:5 + --> tests/ui/redundant_pattern_matching_poll.rs:109:5 | LL | / match Pending::<()> { LL | | diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed index 1158796083147..dab816716d598 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed @@ -1,6 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(deprecated, unused_must_use)] +#![allow(deprecated)] #![allow( clippy::if_same_then_else, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs index 35f8f91b31527..3fd70515d0847 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs @@ -1,6 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(deprecated, unused_must_use)] +#![allow(deprecated)] #![allow( clippy::if_same_then_else, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr index 4f78b95356c21..7e7d27d07a7f6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:15:12 + --> tests/ui/redundant_pattern_matching_result.rs:14:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try: `if result.is_ok()` @@ -8,31 +8,31 @@ LL | if let Ok(_) = &result {} = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:18:12 + --> tests/ui/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:21:12 + --> tests/ui/redundant_pattern_matching_result.rs:20:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:24:15 + --> tests/ui/redundant_pattern_matching_result.rs:23:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:27:15 + --> tests/ui/redundant_pattern_matching_result.rs:26:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:38:5 + --> tests/ui/redundant_pattern_matching_result.rs:37:5 | LL | / match Ok::(42) { LL | | @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: try: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:44:5 + --> tests/ui/redundant_pattern_matching_result.rs:43:5 | LL | / match Ok::(42) { LL | | @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:50:5 + --> tests/ui/redundant_pattern_matching_result.rs:49:5 | LL | / match Err::(42) { LL | | @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: try: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:56:5 + --> tests/ui/redundant_pattern_matching_result.rs:55:5 | LL | / match Err::(42) { LL | | @@ -72,73 +72,73 @@ LL | | }; | |_____^ help: try: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:62:20 + --> tests/ui/redundant_pattern_matching_result.rs:61:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:71:20 + --> tests/ui/redundant_pattern_matching_result.rs:70:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:74:19 + --> tests/ui/redundant_pattern_matching_result.rs:73:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:98:19 + --> tests/ui/redundant_pattern_matching_result.rs:97:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:100:16 + --> tests/ui/redundant_pattern_matching_result.rs:99:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:107:12 + --> tests/ui/redundant_pattern_matching_result.rs:106:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:109:15 + --> tests/ui/redundant_pattern_matching_result.rs:108:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:128:12 + --> tests/ui/redundant_pattern_matching_result.rs:127:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:131:12 + --> tests/ui/redundant_pattern_matching_result.rs:130:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:134:15 + --> tests/ui/redundant_pattern_matching_result.rs:133:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:137:15 + --> tests/ui/redundant_pattern_matching_result.rs:136:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:140:5 + --> tests/ui/redundant_pattern_matching_result.rs:139:5 | LL | / match Ok::(42) { LL | | @@ -148,7 +148,7 @@ LL | | }; | |_____^ help: try: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:146:5 + --> tests/ui/redundant_pattern_matching_result.rs:145:5 | LL | / match Err::(42) { LL | | @@ -158,7 +158,7 @@ LL | | }; | |_____^ help: try: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:157:5 + --> tests/ui/redundant_pattern_matching_result.rs:156:5 | LL | / match x { LL | | @@ -168,7 +168,7 @@ LL | | }; | |_____^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:163:5 + --> tests/ui/redundant_pattern_matching_result.rs:162:5 | LL | / match x { LL | | @@ -178,7 +178,7 @@ LL | | }; | |_____^ help: try: `x.is_err()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:169:5 + --> tests/ui/redundant_pattern_matching_result.rs:168:5 | LL | / match x { LL | | @@ -188,7 +188,7 @@ LL | | }; | |_____^ help: try: `x.is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:175:5 + --> tests/ui/redundant_pattern_matching_result.rs:174:5 | LL | / match x { LL | | @@ -198,13 +198,13 @@ LL | | }; | |_____^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:197:13 + --> tests/ui/redundant_pattern_matching_result.rs:196:13 | LL | let _ = matches!(x, Ok(_)); | ^^^^^^^^^^^^^^^^^^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:200:13 + --> tests/ui/redundant_pattern_matching_result.rs:199:13 | LL | let _ = matches!(x, Err(_)); | ^^^^^^^^^^^^^^^^^^^ help: try: `x.is_err()` diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed index a6450123f4c9d..8a30fedede4a4 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -131,6 +131,14 @@ mod m4 { } } +mod m5 { + pub mod m5_1 {} + // Test that the primary span isn't butchered for item kinds that don't have an ident. + pub use m5_1::*; //~ redundant_pub_crate + #[rustfmt::skip] + pub use m5_1::{*}; //~ redundant_pub_crate +} + pub use m4::*; mod issue_8732 { diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs index 7415d34d50cc7..45ba13a63b2e2 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.rs +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -131,6 +131,14 @@ mod m4 { } } +mod m5 { + pub mod m5_1 {} + // Test that the primary span isn't butchered for item kinds that don't have an ident. + pub(crate) use m5_1::*; //~ redundant_pub_crate + #[rustfmt::skip] + pub(crate) use m5_1::{*}; //~ redundant_pub_crate +} + pub use m4::*; mod issue_8732 { diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr index 95909ea8b0663..4a47a321028d1 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -129,5 +129,21 @@ LL | pub(crate) fn g() {} // private due to m4_2 | | | help: consider using: `pub` -error: aborting due to 16 previous errors +error: pub(crate) import inside private module + --> tests/ui/redundant_pub_crate.rs:137:5 + | +LL | pub(crate) use m5_1::*; + | ----------^^^^^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) import inside private module + --> tests/ui/redundant_pub_crate.rs:139:27 + | +LL | pub(crate) use m5_1::{*}; + | ---------- ^ + | | + | help: consider using: `pub` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix.fixed b/src/tools/clippy/tests/ui/redundant_test_prefix.fixed new file mode 100644 index 0000000000000..b99771f0640ca --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix.fixed @@ -0,0 +1,158 @@ +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +mod m1 { + pub fn f5() {} +} + +#[cfg(test)] +#[test] +fn f6() { + //~^ redundant_test_prefix + + use m1::f5; + + f5(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn foo() { + //~^ redundant_test_prefix + } + + #[test] + fn foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn f1() { + //~^ redundant_test_prefix + } + + #[test] + fn f2() { + //~^ redundant_test_prefix + } + + #[test] + fn f3() { + //~^ redundant_test_prefix + } + + #[test] + fn f4() { + //~^ redundant_test_prefix + } + + #[test] + fn f5() { + //~^ redundant_test_prefix + } + + #[test] + fn f6() { + //~^ redundant_test_prefix + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn foo() { + //~^ redundant_test_prefix + } + + #[test] + fn foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn f1() { + //~^ redundant_test_prefix + } + + #[test] + fn f2() { + //~^ redundant_test_prefix + } + + #[test] + fn f3() { + //~^ redundant_test_prefix + } + + #[test] + fn f4() { + //~^ redundant_test_prefix + } + + #[test] + fn f5() { + //~^ redundant_test_prefix + } + + #[test] + fn f6() { + //~^ redundant_test_prefix + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix.rs b/src/tools/clippy/tests/ui/redundant_test_prefix.rs new file mode 100644 index 0000000000000..3aec577cffa16 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix.rs @@ -0,0 +1,158 @@ +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn test_f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn test_f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +mod m1 { + pub fn f5() {} +} + +#[cfg(test)] +#[test] +fn test_f6() { + //~^ redundant_test_prefix + + use m1::f5; + + f5(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix.stderr b/src/tools/clippy/tests/ui/redundant_test_prefix.stderr new file mode 100644 index 0000000000000..d156af586df3f --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix.stderr @@ -0,0 +1,119 @@ +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:17:4 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + | + = note: `-D clippy::redundant-test-prefix` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_test_prefix)]` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:26:4 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:39:4 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:54:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:59:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:66:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:71:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:76:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:81:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:86:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:91:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:100:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:105:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:112:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:117:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:122:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:127:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:132:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:137:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.rs b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.rs new file mode 100644 index 0000000000000..6ad5d011d8b71 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.rs @@ -0,0 +1,288 @@ +//@no-rustfix: name conflicts + +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn test_f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn test_f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +fn f5() {} + +#[cfg(test)] +#[test] +fn test_f5() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // Collision with existing function. +} + +mod m1 { + pub fn f6() {} + pub fn f7() {} +} + +#[cfg(test)] +#[test] +fn test_f6() { + //~^ redundant_test_prefix + + use m1::f6; + + f6(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, but has a function call that will result in recursion. +} + +#[cfg(test)] +#[test] +fn test_f8() { + //~^ redundant_test_prefix + + use m1::f7; + + f7(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +// Although there's no direct call of `f` in the test, name collision still exists, +// since all `m3` functions are imported and then `map` is used to call `f`. +mod m2 { + mod m3 { + pub fn f(_: i32) -> i32 { + 0 + } + } + + use m3::*; + + #[cfg(test)] + #[test] + fn test_f() { + //~^ redundant_test_prefix + let a = Some(3); + let _ = a.map(f); + } +} + +mod m3 { + fn test_m3_1() { + // Has prefix, but no `#[test]` attribute, ignore. + } + + #[test] + fn test_m3_2() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } + + #[test] + fn test_1() { + //~^ redundant_test_prefix + + // `1` is invalid function name, so suggestion to rename is emitted + } + + #[test] + fn test_const() { + //~^ redundant_test_prefix + + // `const` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_async() { + //~^ redundant_test_prefix + + // `async` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_yield() { + //~^ redundant_test_prefix + + // `yield` is reserved keyword for future use, so suggestion to rename is emitted + } + + #[test] + fn test_() { + //~^ redundant_test_prefix + + // `` is invalid function name, so suggestion to rename is emitted + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } + + #[test] + fn test_1() { + //~^ redundant_test_prefix + + // `1` is invalid function name, so suggestion to rename is emitted + } + + #[test] + fn test_const() { + //~^ redundant_test_prefix + + // `const` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_async() { + //~^ redundant_test_prefix + + // `async` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_yield() { + //~^ redundant_test_prefix + + // `yield` is reserved keyword for future use, so suggestion to rename is emitted + } + + #[test] + fn test_() { + //~^ redundant_test_prefix + + // `` is invalid function name, so suggestion to rename is emitted + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.stderr b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.stderr new file mode 100644 index 0000000000000..6440faf1b3c83 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.stderr @@ -0,0 +1,241 @@ +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:19:4 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + | + = note: `-D clippy::redundant-test-prefix` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_test_prefix)]` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:28:4 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:39:4 + | +LL | fn test_f5() { + | ^^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f5() { +LL + fn f5_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:53:4 + | +LL | fn test_f6() { + | ^^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f6() { +LL + fn f6_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:65:4 + | +LL | fn test_f8() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f8` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:88:8 + | +LL | fn test_f() { + | ^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f() { +LL + fn f_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:101:8 + | +LL | fn test_m3_2() { + | ^^^^^^^^^ help: consider removing the `test_` prefix: `m3_2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:114:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:119:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:126:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:131:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:136:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:141:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:146:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:151:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:156:8 + | +LL | fn test_1() { + | ^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:163:8 + | +LL | fn test_const() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:170:8 + | +LL | fn test_async() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:177:8 + | +LL | fn test_yield() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:184:8 + | +LL | fn test_() { + | ^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:195:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:200:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:207:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:212:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:217:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:222:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:227:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:232:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:237:8 + | +LL | fn test_1() { + | ^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:244:8 + | +LL | fn test_const() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:251:8 + | +LL | fn test_async() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:258:8 + | +LL | fn test_yield() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:265:8 + | +LL | fn test_() { + | ^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: aborting due to 33 previous errors + diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr index 030a9a28ec684..886bf2b034989 100644 --- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr +++ b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:10:5 + --> tests/ui/ref_option/ref_option_traits.rs:9:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:12:5 + --> tests/ui/ref_option/ref_option_traits.rs:11:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:17:5 + --> tests/ui/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:19:5 + --> tests/ui/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr index 2837ee80fb2ef..cfab7fa5734c3 100644 --- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr +++ b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:17:5 + --> tests/ui/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:19:5 + --> tests/ui/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs index 811da2eb4d500..4c773e84f8da8 100644 --- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs +++ b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs @@ -3,7 +3,6 @@ //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all -#![allow(unused, clippy::all)] #![warn(clippy::ref_option)] pub trait PubTrait { diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 7964047069689..acf7914d25365 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -13,8 +13,9 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] +#![allow(clippy::manual_filter_map)] +#![allow(unpredictable_function_pointer_comparisons)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] @@ -29,7 +30,6 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] #![allow(clippy::needless_borrow)] -#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::missing_const_for_thread_local)] @@ -39,11 +39,11 @@ #![allow(invalid_reference_casting)] #![allow(suspicious_double_ref_op)] #![allow(invalid_nan_comparisons)] +#![allow(invalid_null_arguments)] #![allow(double_negations)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] -#![allow(unpredictable_function_pointer_comparisons)] #![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] @@ -62,6 +62,7 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] +#![allow(clippy::reversed_empty_ranges)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -74,8 +75,9 @@ #![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` @@ -94,7 +96,6 @@ #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` -#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` #![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` @@ -104,11 +105,11 @@ #![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` #![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` #![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` #![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` #![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` -#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` #![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` #![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` #![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` @@ -119,7 +120,6 @@ #![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` #![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` #![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` @@ -131,5 +131,6 @@ #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index aa7b905b4b818..32641a684a44b 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -13,8 +13,9 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] +#![allow(clippy::manual_filter_map)] +#![allow(unpredictable_function_pointer_comparisons)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] @@ -29,7 +30,6 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] #![allow(clippy::needless_borrow)] -#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::missing_const_for_thread_local)] @@ -39,11 +39,11 @@ #![allow(invalid_reference_casting)] #![allow(suspicious_double_ref_op)] #![allow(invalid_nan_comparisons)] +#![allow(invalid_null_arguments)] #![allow(double_negations)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] -#![allow(unpredictable_function_pointer_comparisons)] #![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] @@ -62,6 +62,7 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] +#![allow(clippy::reversed_empty_ranges)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -74,8 +75,9 @@ #![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` @@ -94,7 +96,6 @@ #![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` -#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` #![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` @@ -104,11 +105,11 @@ #![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` #![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` #![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` #![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` #![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` -#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` #![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` #![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` #![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` @@ -119,7 +120,6 @@ #![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` #![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` #![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` #![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` #![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` @@ -131,5 +131,6 @@ #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index b3c88167c1115..e9d2debff91a3 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,347 +8,341 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` + --> tests/ui/rename.rs:78:9 + | +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` + error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` -error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:78:9 +error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` + --> tests/ui/rename.rs:80:9 | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` +LL | #![warn(clippy::fn_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:97:9 - | -LL | #![warn(clippy::reverse_range_loop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` - error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` + --> tests/ui/rename.rs:108:9 + | +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` + error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` -error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:111:9 - | -LL | #![warn(clippy::fn_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` - error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` -error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:122:9 - | -LL | #![warn(clippy::invalid_null_ptr_usage)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` - error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` --> tests/ui/rename.rs:123:9 | @@ -415,5 +409,11 @@ error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_ LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` + --> tests/ui/rename.rs:134:9 + | +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` + error: aborting due to 69 previous errors diff --git a/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr b/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr index d1078b3e8e48e..f688e4bc744ae 100644 --- a/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr +++ b/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr @@ -11,7 +11,7 @@ LL | | } | |_^ | = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI - = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + = help: qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` note: the lint level is defined here --> tests/ui/repr_packed_without_abi.rs:1:9 | @@ -31,7 +31,7 @@ LL | | } | |_^ | = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI - = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + = help: qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/result_unit_error_no_std.rs b/src/tools/clippy/tests/ui/result_unit_error_no_std.rs index 8a1849b8490ab..a64e8414d78fe 100644 --- a/src/tools/clippy/tests/ui/result_unit_error_no_std.rs +++ b/src/tools/clippy/tests/ui/result_unit_error_no_std.rs @@ -14,7 +14,7 @@ pub fn returns_unit_error_lint() -> Result { Err(()) } -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { 0 } diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed index 847e5140d3e65..cc4dbc919d81d 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed @@ -214,10 +214,9 @@ mod issue7392 { } fn ref_bindings() { - let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some - let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter().any(|&&(&x, ref y)| x == *y); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs index e976d12600cc1..fa31a9ddedc66 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs @@ -221,10 +221,11 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter() + .find(|&&&(&x, ref y)| x == *y) + .is_none(); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr index ccc17025222d9..b079cf7ea361b 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr @@ -248,116 +248,122 @@ LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); error: called `is_none()` after searching an `Iterator` with `find` --> tests/ui/search_is_some_fixable_none.rs:224:17 | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` - -error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:226:17 +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + | _________________^ +LL | | +LL | | .iter() +LL | | .find(|&&&(&x, ref y)| x == *y) +LL | | .is_none(); + | |______________________^ + | +help: consider using + | +LL ~ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] +LL + +LL ~ .iter().any(|&&(&x, ref y)| x == *y); | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:246:17 + --> tests/ui/search_is_some_fixable_none.rs:247:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:248:17 + --> tests/ui/search_is_some_fixable_none.rs:249:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:258:17 + --> tests/ui/search_is_some_fixable_none.rs:259:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:260:17 + --> tests/ui/search_is_some_fixable_none.rs:261:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:262:17 + --> tests/ui/search_is_some_fixable_none.rs:263:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:279:17 + --> tests/ui/search_is_some_fixable_none.rs:280:17 | LL | let _ = v.iter().find(|x| **x == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:281:17 + --> tests/ui/search_is_some_fixable_none.rs:282:17 | LL | Foo.bar(v.iter().find(|x| **x == 42).is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:287:9 + --> tests/ui/search_is_some_fixable_none.rs:288:9 | LL | v.iter().find(|x| **x == 42).is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:293:9 + --> tests/ui/search_is_some_fixable_none.rs:294:9 | LL | v.iter().find(|x| **x == 42).is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:299:17 + --> tests/ui/search_is_some_fixable_none.rs:300:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:301:17 + --> tests/ui/search_is_some_fixable_none.rs:302:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:304:17 + --> tests/ui/search_is_some_fixable_none.rs:305:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:306:17 + --> tests/ui/search_is_some_fixable_none.rs:307:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:312:17 + --> tests/ui/search_is_some_fixable_none.rs:313:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:315:17 + --> tests/ui/search_is_some_fixable_none.rs:316:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:321:17 + --> tests/ui/search_is_some_fixable_none.rs:322:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:324:17 + --> tests/ui/search_is_some_fixable_none.rs:325:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` -error: aborting due to 55 previous errors +error: aborting due to 54 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.fixed new file mode 100644 index 0000000000000..6e15244901c28 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.fixed @@ -0,0 +1,14 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter().any(|&&(&x, ref y)| x == *y); + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.rs new file mode 100644 index 0000000000000..4b1db3f9fc328 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.rs @@ -0,0 +1,16 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter() + .find(|&&&(&x, ref y)| x == *y) + .is_none(); + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.stderr new file mode 100644 index 0000000000000..af93be1a70719 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.stderr @@ -0,0 +1,35 @@ +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:6:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` + +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:8:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:10:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + | _________________^ +LL | | +LL | | .iter() +LL | | .find(|&&&(&x, ref y)| x == *y) +LL | | .is_none(); + | |______________________^ + | +help: consider using + | +LL ~ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] +LL + +LL ~ .iter().any(|&&(&x, ref y)| x == *y); + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed index 05e88b8528f15..42b39b33b575c 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed @@ -214,10 +214,9 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + .iter() + .any(|&&(&x, ref y)| x == *y); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs index caab816f24361..ca4f4d941cb2f 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs @@ -220,10 +220,11 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + .iter() + .find(|&&&(&x, ref y)| x == *y) + //~^ search_is_some + .is_some(); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr index af719b78831a1..8291f48d43c4d 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr @@ -227,70 +227,67 @@ LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:223:55 + --> tests/ui/search_is_some_fixable_some.rs:225:14 | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` - -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:225:55 - | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` +LL | .find(|&&&(&x, ref y)| x == *y) + | ______________^ +LL | | +LL | | .is_some(); + | |______________________^ help: consider using: `any(|&&(&x, ref y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:245:26 + --> tests/ui/search_is_some_fixable_some.rs:246:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:247:26 + --> tests/ui/search_is_some_fixable_some.rs:248:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:257:26 + --> tests/ui/search_is_some_fixable_some.rs:258:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:259:26 + --> tests/ui/search_is_some_fixable_some.rs:260:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:261:26 + --> tests/ui/search_is_some_fixable_some.rs:262:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:277:18 + --> tests/ui/search_is_some_fixable_some.rs:278:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:287:26 + --> tests/ui/search_is_some_fixable_some.rs:288:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:291:26 + --> tests/ui/search_is_some_fixable_some.rs:292:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:295:26 + --> tests/ui/search_is_some_fixable_some.rs:296:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` -error: aborting due to 47 previous errors +error: aborting due to 46 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.fixed new file mode 100644 index 0000000000000..d2b05db562a0b --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.fixed @@ -0,0 +1,11 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.rs new file mode 100644 index 0000000000000..c3f5ef769dab7 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.rs @@ -0,0 +1,11 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); + //~^ search_is_some + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.stderr new file mode 100644 index 0000000000000..91d9540e6fcf3 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.stderr @@ -0,0 +1,17 @@ +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some_2021.rs:6:55 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` + +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some_2021.rs:8:55 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs index 7d503a1cf6c17..05009b2ddd416 100644 --- a/src/tools/clippy/tests/ui/shadow.rs +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -167,4 +167,19 @@ fn issue13795(value: Issue13795) { //~^ shadow_same } +fn issue14377() { + let a; + let b; + (a, b) = (0, 1); + + struct S { + c: i32, + d: i32, + } + + let c; + let d; + S { c, d } = S { c: 1, d: 2 }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs index 4ec0f02d66451..53704f59cb999 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs +++ b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs @@ -1,6 +1,5 @@ //@ check-pass -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs index 87b3a7d2fa0cf..e8de0e04c0c4c 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr index 8738b61192a3c..5609d6a21a360 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_1.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> tests/ui/should_impl_trait/method_list_1.rs:25:5 + --> tests/ui/should_impl_trait/method_list_1.rs:24:5 | LL | / pub fn add(self, other: T) -> T { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:31:5 + --> tests/ui/should_impl_trait/method_list_1.rs:30:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` - --> tests/ui/should_impl_trait/method_list_1.rs:37:5 + --> tests/ui/should_impl_trait/method_list_1.rs:36:5 | LL | / pub fn as_ref(&self) -> &T { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` - --> tests/ui/should_impl_trait/method_list_1.rs:43:5 + --> tests/ui/should_impl_trait/method_list_1.rs:42:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | @@ -49,7 +49,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` - --> tests/ui/should_impl_trait/method_list_1.rs:49:5 + --> tests/ui/should_impl_trait/method_list_1.rs:48:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | @@ -61,7 +61,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` - --> tests/ui/should_impl_trait/method_list_1.rs:55:5 + --> tests/ui/should_impl_trait/method_list_1.rs:54:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | @@ -73,7 +73,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` - --> tests/ui/should_impl_trait/method_list_1.rs:61:5 + --> tests/ui/should_impl_trait/method_list_1.rs:60:5 | LL | / pub fn borrow(&self) -> &str { LL | | @@ -85,7 +85,7 @@ LL | | } = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:67:5 + --> tests/ui/should_impl_trait/method_list_1.rs:66:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | @@ -97,7 +97,7 @@ LL | | } = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` - --> tests/ui/should_impl_trait/method_list_1.rs:73:5 + --> tests/ui/should_impl_trait/method_list_1.rs:72:5 | LL | / pub fn clone(&self) -> Self { LL | | @@ -109,7 +109,7 @@ LL | | } = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` - --> tests/ui/should_impl_trait/method_list_1.rs:79:5 + --> tests/ui/should_impl_trait/method_list_1.rs:78:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `default` can be confused for the standard trait method `std::default::Default::default` - --> tests/ui/should_impl_trait/method_list_1.rs:85:5 + --> tests/ui/should_impl_trait/method_list_1.rs:84:5 | LL | / pub fn default() -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> tests/ui/should_impl_trait/method_list_1.rs:91:5 + --> tests/ui/should_impl_trait/method_list_1.rs:90:5 | LL | / pub fn deref(&self) -> &Self { LL | | @@ -145,7 +145,7 @@ LL | | } = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:97:5 + --> tests/ui/should_impl_trait/method_list_1.rs:96:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | @@ -157,7 +157,7 @@ LL | | } = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name error: method `div` can be confused for the standard trait method `std::ops::Div::div` - --> tests/ui/should_impl_trait/method_list_1.rs:103:5 + --> tests/ui/should_impl_trait/method_list_1.rs:102:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ LL | | } = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` - --> tests/ui/should_impl_trait/method_list_1.rs:109:5 + --> tests/ui/should_impl_trait/method_list_1.rs:108:5 | LL | / pub fn drop(&mut self) { LL | | diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs index f0c4d4f15cb63..1f25ab3938a3d 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr index 85de74337020d..0f5818507779f 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr @@ -1,5 +1,5 @@ error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` - --> tests/ui/should_impl_trait/method_list_2.rs:26:5 + --> tests/ui/should_impl_trait/method_list_2.rs:25:5 | LL | / pub fn eq(&self, other: &Self) -> bool { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` - --> tests/ui/should_impl_trait/method_list_2.rs:32:5 + --> tests/ui/should_impl_trait/method_list_2.rs:31:5 | LL | / pub fn from_iter(iter: T) -> Self { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` - --> tests/ui/should_impl_trait/method_list_2.rs:38:5 + --> tests/ui/should_impl_trait/method_list_2.rs:37:5 | LL | / pub fn from_str(s: &str) -> Result { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` - --> tests/ui/should_impl_trait/method_list_2.rs:44:5 + --> tests/ui/should_impl_trait/method_list_2.rs:43:5 | LL | / pub fn hash(&self, state: &mut T) { LL | | @@ -49,7 +49,7 @@ LL | | } = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name error: method `index` can be confused for the standard trait method `std::ops::Index::index` - --> tests/ui/should_impl_trait/method_list_2.rs:50:5 + --> tests/ui/should_impl_trait/method_list_2.rs:49:5 | LL | / pub fn index(&self, index: usize) -> &Self { LL | | @@ -61,7 +61,7 @@ LL | | } = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` - --> tests/ui/should_impl_trait/method_list_2.rs:56:5 + --> tests/ui/should_impl_trait/method_list_2.rs:55:5 | LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { LL | | @@ -73,7 +73,7 @@ LL | | } = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` - --> tests/ui/should_impl_trait/method_list_2.rs:62:5 + --> tests/ui/should_impl_trait/method_list_2.rs:61:5 | LL | / pub fn into_iter(self) -> Self { LL | | @@ -85,7 +85,7 @@ LL | | } = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` - --> tests/ui/should_impl_trait/method_list_2.rs:68:5 + --> tests/ui/should_impl_trait/method_list_2.rs:67:5 | LL | / pub fn mul(self, rhs: Self) -> Self { LL | | @@ -97,7 +97,7 @@ LL | | } = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` - --> tests/ui/should_impl_trait/method_list_2.rs:74:5 + --> tests/ui/should_impl_trait/method_list_2.rs:73:5 | LL | / pub fn neg(self) -> Self { LL | | @@ -109,7 +109,7 @@ LL | | } = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` - --> tests/ui/should_impl_trait/method_list_2.rs:80:5 + --> tests/ui/should_impl_trait/method_list_2.rs:79:5 | LL | / pub fn next(&mut self) -> Option { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name error: method `not` can be confused for the standard trait method `std::ops::Not::not` - --> tests/ui/should_impl_trait/method_list_2.rs:86:5 + --> tests/ui/should_impl_trait/method_list_2.rs:85:5 | LL | / pub fn not(self) -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` - --> tests/ui/should_impl_trait/method_list_2.rs:92:5 + --> tests/ui/should_impl_trait/method_list_2.rs:91:5 | LL | / pub fn rem(self, rhs: Self) -> Self { LL | | @@ -145,7 +145,7 @@ LL | | } = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` - --> tests/ui/should_impl_trait/method_list_2.rs:98:5 + --> tests/ui/should_impl_trait/method_list_2.rs:97:5 | LL | / pub fn shl(self, rhs: Self) -> Self { LL | | @@ -157,7 +157,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` - --> tests/ui/should_impl_trait/method_list_2.rs:104:5 + --> tests/ui/should_impl_trait/method_list_2.rs:103:5 | LL | / pub fn shr(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` - --> tests/ui/should_impl_trait/method_list_2.rs:110:5 + --> tests/ui/should_impl_trait/method_list_2.rs:109:5 | LL | / pub fn sub(self, rhs: Self) -> Self { LL | | diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs index c1cc4032bec99..a1ecd7bc166cf 100644 --- a/src/tools/clippy/tests/ui/single_call_fn.rs +++ b/src/tools/clippy/tests/ui/single_call_fn.rs @@ -94,7 +94,7 @@ trait Trait { //~^ single_call_fn fn foo(&self); } -extern "C" { +unsafe extern "C" { // test some kind of foreign item fn rand() -> std::ffi::c_int; } diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index 0e198ec79344a..db5107600ee6d 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -366,3 +366,39 @@ fn irrefutable_match() { //~^^^^^^^^^ single_match //~| NOTE: you might want to preserve the comments from inside the `match` } + +fn issue_14493() { + macro_rules! mac { + (some) => { + Some(42) + }; + (any) => { + _ + }; + (str) => { + "foo" + }; + } + + if let Some(u) = mac!(some) { println!("{u}") } + //~^^^^ single_match + + // When scrutinee comes from macro, do not tell that arm will always match + // and suggest an equality check instead. + if mac!(str) == "foo" { println!("eq") } + //~^^^^ ERROR: for an equality check + + // Do not lint if any match arm come from expansion + match Some(0) { + mac!(some) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + Some(42) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + mac!(some) => println!("eq"), + _ => println!("neq"), + } +} diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index fcac65f8aaf5e..a367b94c4ca6b 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -461,3 +461,45 @@ fn irrefutable_match() { //~^^^^^^^^^ single_match //~| NOTE: you might want to preserve the comments from inside the `match` } + +fn issue_14493() { + macro_rules! mac { + (some) => { + Some(42) + }; + (any) => { + _ + }; + (str) => { + "foo" + }; + } + + match mac!(some) { + Some(u) => println!("{u}"), + _ => (), + } + //~^^^^ single_match + + // When scrutinee comes from macro, do not tell that arm will always match + // and suggest an equality check instead. + match mac!(str) { + "foo" => println!("eq"), + _ => (), + } + //~^^^^ ERROR: for an equality check + + // Do not lint if any match arm come from expansion + match Some(0) { + mac!(some) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + Some(42) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + mac!(some) => println!("eq"), + _ => println!("neq"), + } +} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index 2467423b9c17d..1a4edc45c928d 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -321,5 +321,23 @@ LL + println!("{u}"); LL + } | -error: aborting due to 29 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:478:5 + | +LL | / match mac!(some) { +LL | | Some(u) => println!("{u}"), +LL | | _ => (), +LL | | } + | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match.rs:486:5 + | +LL | / match mac!(str) { +LL | | "foo" => println!("eq"), +LL | | _ => (), +LL | | } + | |_____^ help: try: `if mac!(str) == "foo" { println!("eq") }` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed index 3696b0e066d28..3faa4b21ee414 100644 --- a/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed +++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed @@ -87,4 +87,8 @@ pub mod useless_outer_doc { use std::mem; } +// Do not lint, this is not a `///!` +#[doc = "! here's some docs !"] +fn issue14265() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.rs b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs index 4107f5526d132..4af6ed850c2bb 100644 --- a/src/tools/clippy/tests/ui/suspicious_doc_comments.rs +++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs @@ -87,4 +87,8 @@ pub mod useless_outer_doc { use std::mem; } +// Do not lint, this is not a `///!` +#[doc = "! here's some docs !"] +fn issue14265() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed index 888665a17ad16..6a64e64e98fa2 100644 --- a/src/tools/clippy/tests/ui/swap.fixed +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -1,14 +1,9 @@ //@aux-build: macro_rules.rs -#![warn(clippy::all)] #![allow( clippy::disallowed_names, clippy::no_effect, clippy::redundant_clone, - redundant_semicolons, - dead_code, - unused_assignments, - unused_variables, clippy::let_and_return, clippy::useless_vec, clippy::redundant_locals diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs index 51af55ecd27c8..e2d89c47382da 100644 --- a/src/tools/clippy/tests/ui/swap.rs +++ b/src/tools/clippy/tests/ui/swap.rs @@ -1,14 +1,9 @@ //@aux-build: macro_rules.rs -#![warn(clippy::all)] #![allow( clippy::disallowed_names, clippy::no_effect, clippy::redundant_clone, - redundant_semicolons, - dead_code, - unused_assignments, - unused_variables, clippy::let_and_return, clippy::useless_vec, clippy::redundant_locals diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr index 15f7566d58960..195b888187e6d 100644 --- a/src/tools/clippy/tests/ui/swap.stderr +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually - --> tests/ui/swap.rs:28:5 + --> tests/ui/swap.rs:23:5 | LL | / let temp = bar.a; LL | | @@ -12,7 +12,7 @@ LL | | bar.b = temp; = help: to override `-D warnings` add `#[allow(clippy::manual_swap)]` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:41:5 + --> tests/ui/swap.rs:36:5 | LL | / let temp = foo[0]; LL | | @@ -21,7 +21,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:51:5 + --> tests/ui/swap.rs:46:5 | LL | / let temp = foo[0]; LL | | @@ -30,7 +30,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:71:5 + --> tests/ui/swap.rs:66:5 | LL | / let temp = foo[0]; LL | | @@ -39,7 +39,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually - --> tests/ui/swap.rs:83:5 + --> tests/ui/swap.rs:78:5 | LL | / a ^= b; LL | | @@ -48,7 +48,7 @@ LL | | a ^= b; | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually - --> tests/ui/swap.rs:92:5 + --> tests/ui/swap.rs:87:5 | LL | / bar.a ^= bar.b; LL | | @@ -57,7 +57,7 @@ LL | | bar.a ^= bar.b; | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:101:5 + --> tests/ui/swap.rs:96:5 | LL | / foo[0] ^= foo[1]; LL | | @@ -66,7 +66,7 @@ LL | | foo[0] ^= foo[1]; | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually - --> tests/ui/swap.rs:131:5 + --> tests/ui/swap.rs:126:5 | LL | / let temp = foo[0][1]; LL | | @@ -77,7 +77,7 @@ LL | | bar[1][0] = temp; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `a` and `b` manually - --> tests/ui/swap.rs:147:7 + --> tests/ui/swap.rs:142:7 | LL | ; let t = a; | _______^ @@ -89,7 +89,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> tests/ui/swap.rs:158:7 + --> tests/ui/swap.rs:153:7 | LL | ; let t = c.0; | _______^ @@ -101,7 +101,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `b` and `a` manually - --> tests/ui/swap.rs:188:5 + --> tests/ui/swap.rs:183:5 | LL | / let t = b; LL | | @@ -112,7 +112,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> tests/ui/swap.rs:143:5 + --> tests/ui/swap.rs:138:5 | LL | / a = b; LL | | @@ -120,11 +120,10 @@ LL | | b = a; | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` | = note: or maybe you should use `std::mem::replace`? - = note: `-D clippy::almost-swapped` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_swapped)]` + = note: `#[deny(clippy::almost_swapped)]` on by default error: this looks like you are trying to swap `c.0` and `a` - --> tests/ui/swap.rs:154:5 + --> tests/ui/swap.rs:149:5 | LL | / c.0 = a; LL | | @@ -134,7 +133,7 @@ LL | | a = c.0; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> tests/ui/swap.rs:163:5 + --> tests/ui/swap.rs:158:5 | LL | / let a = b; LL | | @@ -144,7 +143,7 @@ LL | | let b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `d` and `c` - --> tests/ui/swap.rs:169:5 + --> tests/ui/swap.rs:164:5 | LL | / d = c; LL | | @@ -154,7 +153,7 @@ LL | | c = d; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> tests/ui/swap.rs:174:5 + --> tests/ui/swap.rs:169:5 | LL | / let a = b; LL | | @@ -164,7 +163,7 @@ LL | | b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `s.0.x` and `s.0.y` manually - --> tests/ui/swap.rs:224:5 + --> tests/ui/swap.rs:219:5 | LL | / let t = s.0.x; LL | | diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.fixed b/src/tools/clippy/tests/ui/swap_with_temporary.fixed new file mode 100644 index 0000000000000..4007d998ba068 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary.fixed @@ -0,0 +1,74 @@ +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + // No lint + swap(&mut x, &mut y); + + y = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + x = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + *z = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint + swap(z, func_returning_refmut(&mut x)); + + swap(&mut y, z); + + *z = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (funcall $f:ident) => { + $f() + }; + (wholeexpr) => { + swap(&mut 42, &mut 0) + }; + (ident $v:ident) => { + $v + }; + } + *z = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(ident z) = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(ident z) = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(refmut y) = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint if it comes from a macro as it may depend on the arguments + mac!(wholeexpr); +} + +struct S { + t: String, +} + +fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { + swap(&mut s.t, &mut v[0]); + swap(&mut s.t, v.get_mut(0).unwrap()); + swap(w.unwrap(), &mut s.t); +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.rs b/src/tools/clippy/tests/ui/swap_with_temporary.rs new file mode 100644 index 0000000000000..d403c086c0f4f --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary.rs @@ -0,0 +1,74 @@ +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + // No lint + swap(&mut x, &mut y); + + swap(&mut func(), &mut y); + //~^ ERROR: swapping with a temporary value is inefficient + + swap(&mut x, &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + swap(z, &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint + swap(z, func_returning_refmut(&mut x)); + + swap(&mut y, z); + + swap(&mut func(), z); + //~^ ERROR: swapping with a temporary value is inefficient + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (funcall $f:ident) => { + $f() + }; + (wholeexpr) => { + swap(&mut 42, &mut 0) + }; + (ident $v:ident) => { + $v + }; + } + swap(&mut mac!(funcall func), z); + //~^ ERROR: swapping with a temporary value is inefficient + swap(&mut mac!(funcall func), mac!(ident z)); + //~^ ERROR: swapping with a temporary value is inefficient + swap(mac!(ident z), &mut mac!(funcall func)); + //~^ ERROR: swapping with a temporary value is inefficient + swap(mac!(refmut y), &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint if it comes from a macro as it may depend on the arguments + mac!(wholeexpr); +} + +struct S { + t: String, +} + +fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { + swap(&mut s.t, &mut v[0]); + swap(&mut s.t, v.get_mut(0).unwrap()); + swap(w.unwrap(), &mut s.t); +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.stderr b/src/tools/clippy/tests/ui/swap_with_temporary.stderr new file mode 100644 index 0000000000000..59355771a9648 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary.stderr @@ -0,0 +1,100 @@ +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:22:5 + | +LL | swap(&mut func(), &mut y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `y = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:22:15 + | +LL | swap(&mut func(), &mut y); + | ^^^^^^ + = note: `-D clippy::swap-with-temporary` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::swap_with_temporary)]` + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:25:5 + | +LL | swap(&mut x, &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `x = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:25:23 + | +LL | swap(&mut x, &mut func()); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:28:5 + | +LL | swap(z, &mut func()); + | ^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:28:18 + | +LL | swap(z, &mut func()); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:36:5 + | +LL | swap(&mut func(), z); + | ^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:36:15 + | +LL | swap(&mut func(), z); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:53:5 + | +LL | swap(&mut mac!(funcall func), z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:53:15 + | +LL | swap(&mut mac!(funcall func), z); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:55:5 + | +LL | swap(&mut mac!(funcall func), mac!(ident z)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(ident z) = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:55:15 + | +LL | swap(&mut mac!(funcall func), mac!(ident z)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:57:5 + | +LL | swap(mac!(ident z), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(ident z) = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:57:30 + | +LL | swap(mac!(ident z), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:59:5 + | +LL | swap(mac!(refmut y), &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(refmut y) = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:59:31 + | +LL | swap(mac!(refmut y), &mut func()); + | ^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.rs b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.rs new file mode 100644 index 0000000000000..a974ca82abf26 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.rs @@ -0,0 +1,62 @@ +//@no-rustfix +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + swap(&mut func(), &mut func()); + //~^ ERROR: swapping temporary values has no effect + + if matches!(swap(&mut func(), &mut func()), ()) { + //~^ ERROR: swapping temporary values has no effect + println!("Yeah"); + } + + if matches!(swap(z, &mut func()), ()) { + //~^ ERROR: swapping with a temporary value is inefficient + println!("Yeah"); + } + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (refmut) => { + mac!(refmut String::new()) + }; + (funcall $f:ident) => { + $f() + }; + } + + swap(mac!(refmut func()), z); + //~^ ERROR: swapping with a temporary value is inefficient + swap(&mut mac!(funcall func), &mut mac!(funcall func)); + //~^ ERROR: swapping temporary values has no effect + swap(mac!(refmut), mac!(refmut)); + //~^ ERROR: swapping temporary values has no effect + swap(mac!(refmut y), mac!(refmut)); + //~^ ERROR: swapping with a temporary value is inefficient +} + +fn bug(v1: &mut [i32], v2: &mut [i32]) { + // Incorrect: swapping temporary references (`&mut &mut` passed to swap) + std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + //~^ ERROR: swapping temporary values has no effect + + // Correct + std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap()); +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.stderr b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.stderr new file mode 100644 index 0000000000000..856c5415d676c --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.stderr @@ -0,0 +1,125 @@ +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:20:5 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:20:15 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:20:28 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^ + = note: `-D clippy::swap-with-temporary` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::swap_with_temporary)]` + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:23:17 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:23:27 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:23:40 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:28:17 + | +LL | if matches!(swap(z, &mut func()), ()) { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:28:30 + | +LL | if matches!(swap(z, &mut func()), ()) { + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:45:5 + | +LL | swap(mac!(refmut func()), z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:45:10 + | +LL | swap(mac!(refmut func()), z); + | ^^^^^^^^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:47:5 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:47:15 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:47:40 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:49:5 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:49:10 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^ +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:49:24 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:51:5 + | +LL | swap(mac!(refmut y), mac!(refmut)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:51:26 + | +LL | swap(mac!(refmut y), mac!(refmut)); + | ^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:57:5 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:57:25 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:57:54 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 3aecde398dc3f..2b8b6c539ad3c 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -3,6 +3,7 @@ #![allow( dead_code, clippy::borrow_as_ptr, + unnecessary_transmutes, clippy::needless_lifetimes, clippy::missing_transmute_annotations )] @@ -23,19 +24,21 @@ fn my_vec() -> MyVec { #[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { - // FIXME: should lint - // let _: &'a T = core::mem::transmute(t); + unsafe { + // FIXME: should lint + // let _: &'a T = core::mem::transmute(t); - let _: &'a U = core::mem::transmute(t); + let _: &'a U = core::mem::transmute(t); - let _: *const T = core::mem::transmute(t); - //~^ useless_transmute + let _: *const T = core::mem::transmute(t); + //~^ useless_transmute - let _: *mut T = core::mem::transmute(t); - //~^ useless_transmute + let _: *mut T = core::mem::transmute(t); + //~^ useless_transmute - let _: *const U = core::mem::transmute(t); - //~^ useless_transmute + let _: *const U = core::mem::transmute(t); + //~^ useless_transmute + } } #[warn(clippy::useless_transmute)] @@ -59,7 +62,7 @@ fn useless() { let _: *const usize = std::mem::transmute(5_isize); //~^ useless_transmute - let _ = 5_isize as *const usize; + let _ = std::ptr::dangling::(); let _: *const usize = std::mem::transmute(1 + 1usize); //~^ useless_transmute @@ -68,19 +71,19 @@ fn useless() { } unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator + 'a)) -> *const (dyn Iterator + 'b) { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } } diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index e0d28437aafc8..1bb70151965cd 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,68 +1,68 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:31:23 + --> tests/ui/transmute.rs:33:27 | -LL | let _: *const T = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` +LL | let _: *const T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` | = note: `-D clippy::useless-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:34:21 + --> tests/ui/transmute.rs:36:25 | -LL | let _: *mut T = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` +LL | let _: *mut T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:37:23 + --> tests/ui/transmute.rs:39:27 | -LL | let _: *const U = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +LL | let _: *const U = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:44:27 + --> tests/ui/transmute.rs:47:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:47:27 + --> tests/ui/transmute.rs:50:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:50:27 + --> tests/ui/transmute.rs:53:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:53:27 + --> tests/ui/transmute.rs:56:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:56:27 + --> tests/ui/transmute.rs:59:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:59:31 + --> tests/ui/transmute.rs:62:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:64:31 + --> tests/ui/transmute.rs:67:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:96:24 + --> tests/ui/transmute.rs:99:24 | LL | let _: Usize = core::mem::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::mem::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:99:24 + --> tests/ui/transmute.rs:102:24 | LL | let _: Usize = core::mem::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:102:31 + --> tests/ui/transmute.rs:105:31 | LL | let _: *const Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:105:29 + --> tests/ui/transmute.rs:108:29 | LL | let _: *mut Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:112:28 + --> tests/ui/transmute.rs:115:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -98,7 +98,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:119:31 + --> tests/ui/transmute.rs:122:31 | LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` @@ -107,97 +107,97 @@ LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:122:31 + --> tests/ui/transmute.rs:125:31 | LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:125:31 + --> tests/ui/transmute.rs:128:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:128:31 + --> tests/ui/transmute.rs:131:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:131:31 + --> tests/ui/transmute.rs:134:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:134:31 + --> tests/ui/transmute.rs:137:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:137:32 + --> tests/ui/transmute.rs:140:32 | LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:140:32 + --> tests/ui/transmute.rs:143:32 | LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:145:39 + --> tests/ui/transmute.rs:148:39 | LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:148:39 + --> tests/ui/transmute.rs:151:39 | LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:151:39 + --> tests/ui/transmute.rs:154:39 | LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:154:41 + --> tests/ui/transmute.rs:157:41 | LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:158:22 + --> tests/ui/transmute.rs:161:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:163:22 + --> tests/ui/transmute.rs:166:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:168:22 + --> tests/ui/transmute.rs:171:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:173:22 + --> tests/ui/transmute.rs:176:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:182:30 + --> tests/ui/transmute.rs:185:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -206,121 +206,121 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:185:30 + --> tests/ui/transmute.rs:188:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:188:31 + --> tests/ui/transmute.rs:191:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:191:30 + --> tests/ui/transmute.rs:194:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:194:30 + --> tests/ui/transmute.rs:197:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:197:31 + --> tests/ui/transmute.rs:200:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:200:30 + --> tests/ui/transmute.rs:203:30 | LL | let _: [u8; 2] = std::mem::transmute(0.0f16); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:203:30 + --> tests/ui/transmute.rs:206:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:206:30 + --> tests/ui/transmute.rs:209:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:209:31 + --> tests/ui/transmute.rs:212:31 | LL | let _: [u8; 16] = std::mem::transmute(0.0f128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:215:30 + --> tests/ui/transmute.rs:218:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:218:30 + --> tests/ui/transmute.rs:221:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:221:31 + --> tests/ui/transmute.rs:224:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:224:30 + --> tests/ui/transmute.rs:227:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:227:30 + --> tests/ui/transmute.rs:230:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:230:31 + --> tests/ui/transmute.rs:233:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:233:30 + --> tests/ui/transmute.rs:236:30 | LL | let _: [u8; 2] = std::mem::transmute(0.0f16); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:236:30 + --> tests/ui/transmute.rs:239:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:239:30 + --> tests/ui/transmute.rs:242:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:242:31 + --> tests/ui/transmute.rs:245:31 | LL | let _: [u8; 16] = std::mem::transmute(0.0f128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:251:28 + --> tests/ui/transmute.rs:254:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -329,13 +329,13 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:254:32 + --> tests/ui/transmute.rs:257:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:257:30 + --> tests/ui/transmute.rs:260:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed index 1f97b997eaa0e..844445907d7c2 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed @@ -1,5 +1,5 @@ #![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] #![feature(f128)] #![feature(f16)] diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs index 788a7e1026c67..a1f3b15bbfee4 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] #![feature(f128)] #![feature(f16)] diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed index b5425a2e9e854..28644aa9ebbb7 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed @@ -1,5 +1,5 @@ #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] fn int_to_char() { let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.rs b/src/tools/clippy/tests/ui/transmute_int_to_char.rs index b24bb177c9fc0..8c83ecc8914b6 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] fn int_to_char() { let _: char = unsafe { std::mem::transmute(0_u32) }; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed index e525751e306ea..e6e09a2be4bf5 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed @@ -1,7 +1,7 @@ #![no_std] #![feature(lang_items)] #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs index 7cb508ceaf3bc..0f2106df00e6c 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(lang_items)] #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/transmute_null_to_fn.rs b/src/tools/clippy/tests/ui/transmute_null_to_fn.rs index e88f05bb662e2..4712374af934f 100644 --- a/src/tools/clippy/tests/ui/transmute_null_to_fn.rs +++ b/src/tools/clippy/tests/ui/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::transmute_null_to_fn)] #![allow(clippy::zero_ptr, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr b/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr index f7d80147445d8..b5b0d4ecc7c03 100644 --- a/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr +++ b/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:8:23 + --> tests/ui/transmute_null_to_fn.rs:9:23 | LL | let _: fn() = std::mem::transmute(0 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -9,7 +9,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const ()); = help: to override `-D warnings` add `#[allow(clippy::transmute_null_to_fn)]` error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:11:23 + --> tests/ui/transmute_null_to_fn.rs:12:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -17,7 +17,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:22:23 + --> tests/ui/transmute_null_to_fn.rs:23:23 | LL | let _: fn() = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -25,7 +25,7 @@ LL | let _: fn() = std::mem::transmute(ZPTR); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:32:23 + --> tests/ui/transmute_null_to_fn.rs:33:23 | LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -33,7 +33,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:35:23 + --> tests/ui/transmute_null_to_fn.rs:36:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -41,7 +41,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:38:23 + --> tests/ui/transmute_null_to_fn.rs:39:23 | LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed index 3a67be5f45d0b..476e7e35a1f61 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed @@ -8,12 +8,12 @@ use std::mem::transmute; // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - transmute::<&'a T, &'static T>(t) + unsafe { transmute::<&'a T, &'static T>(t) } } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - transmute::<&'a T, &'b T>(t) + unsafe { transmute::<&'a T, &'b T>(t) } } struct LifetimeParam<'a> { diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs index 01ad3a3296b17..7356668bcab5a 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -8,12 +8,12 @@ use std::mem::transmute; // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - transmute::<&'a T, &'static T>(t) + unsafe { transmute::<&'a T, &'static T>(t) } } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - transmute::<&'a T, &'b T>(t) + unsafe { transmute::<&'a T, &'b T>(t) } } struct LifetimeParam<'a> { diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed index 1bd45bc10a39b..61e3ac2fe88e3 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed @@ -6,33 +6,35 @@ )] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { - let _: &T = &*p; - //~^ transmute_ptr_to_ref - let _: &T = &*p; + unsafe { + let _: &T = &*p; + //~^ transmute_ptr_to_ref + let _: &T = &*p; - let _: &mut T = &mut *m; - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *m; + let _: &mut T = &mut *m; + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *m; - let _: &T = &*m; - //~^ transmute_ptr_to_ref - let _: &T = &*m; + let _: &T = &*m; + //~^ transmute_ptr_to_ref + let _: &T = &*m; - let _: &mut T = &mut *(p as *mut T); - //~^ transmute_ptr_to_ref - let _ = &mut *(p as *mut T); + let _: &mut T = &mut *(p as *mut T); + //~^ transmute_ptr_to_ref + let _ = &mut *(p as *mut T); - let _: &T = &*(o as *const T); - //~^ transmute_ptr_to_ref - let _: &T = &*(o as *const T); + let _: &T = &*(o as *const T); + //~^ transmute_ptr_to_ref + let _: &T = &*(o as *const T); - let _: &mut T = &mut *(om as *mut T); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *(om as *mut T); + let _: &mut T = &mut *(om as *mut T); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *(om as *mut T); - let _: &T = &*(om as *const T); - //~^ transmute_ptr_to_ref - let _: &T = &*(om as *const T); + let _: &T = &*(om as *const T); + //~^ transmute_ptr_to_ref + let _: &T = &*(om as *const T); + } } fn _issue1231() { @@ -54,47 +56,53 @@ fn _issue1231() { } unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { - match 0 { - 0 => &*x.cast::<&u32>(), - //~^ transmute_ptr_to_ref - 1 => &*y.cast::<&u32>(), - //~^ transmute_ptr_to_ref - 2 => &*x.cast::<&'b u32>(), - //~^ transmute_ptr_to_ref - _ => &*y.cast::<&'b u32>(), - //~^ transmute_ptr_to_ref + unsafe { + match 0 { + 0 => &*x.cast::<&u32>(), + //~^ transmute_ptr_to_ref + 1 => &*y.cast::<&u32>(), + //~^ transmute_ptr_to_ref + 2 => &*x.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + _ => &*y.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = &*a; - //~^ transmute_ptr_to_ref - let _: &u32 = &*a.cast::(); - //~^ transmute_ptr_to_ref - match 0 { - 0 => &*x.cast::<&u32>(), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; //~^ transmute_ptr_to_ref - _ => &*x.cast::<&'b u32>(), + let _: &u32 = &*a.cast::(); //~^ transmute_ptr_to_ref + match 0 { + 0 => &*x.cast::<&u32>(), + //~^ transmute_ptr_to_ref + _ => &*x.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = &*a; - //~^ transmute_ptr_to_ref - let _: &u32 = &*(a as *const u32); - //~^ transmute_ptr_to_ref - match 0 { - 0 => &*(x as *const () as *const &u32), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; //~^ transmute_ptr_to_ref - _ => &*(x as *const () as *const &'b u32), + let _: &u32 = &*(a as *const u32); //~^ transmute_ptr_to_ref + match 0 { + 0 => &*(x as *const () as *const &u32), + //~^ transmute_ptr_to_ref + _ => &*(x as *const () as *const &'b u32), + //~^ transmute_ptr_to_ref + } } } diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs index cbe64bf1ea6bc..48e2f527b554c 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs @@ -6,33 +6,35 @@ )] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { - let _: &T = std::mem::transmute(p); - //~^ transmute_ptr_to_ref - let _: &T = &*p; + unsafe { + let _: &T = std::mem::transmute(p); + //~^ transmute_ptr_to_ref + let _: &T = &*p; - let _: &mut T = std::mem::transmute(m); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *m; + let _: &mut T = std::mem::transmute(m); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *m; - let _: &T = std::mem::transmute(m); - //~^ transmute_ptr_to_ref - let _: &T = &*m; + let _: &T = std::mem::transmute(m); + //~^ transmute_ptr_to_ref + let _: &T = &*m; - let _: &mut T = std::mem::transmute(p as *mut T); - //~^ transmute_ptr_to_ref - let _ = &mut *(p as *mut T); + let _: &mut T = std::mem::transmute(p as *mut T); + //~^ transmute_ptr_to_ref + let _ = &mut *(p as *mut T); - let _: &T = std::mem::transmute(o); - //~^ transmute_ptr_to_ref - let _: &T = &*(o as *const T); + let _: &T = std::mem::transmute(o); + //~^ transmute_ptr_to_ref + let _: &T = &*(o as *const T); - let _: &mut T = std::mem::transmute(om); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *(om as *mut T); + let _: &mut T = std::mem::transmute(om); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *(om as *mut T); - let _: &T = std::mem::transmute(om); - //~^ transmute_ptr_to_ref - let _: &T = &*(om as *const T); + let _: &T = std::mem::transmute(om); + //~^ transmute_ptr_to_ref + let _: &T = &*(om as *const T); + } } fn _issue1231() { @@ -54,47 +56,53 @@ fn _issue1231() { } unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { - match 0 { - 0 => std::mem::transmute(x), - //~^ transmute_ptr_to_ref - 1 => std::mem::transmute(y), - //~^ transmute_ptr_to_ref - 2 => std::mem::transmute::<_, &&'b u32>(x), - //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(y), - //~^ transmute_ptr_to_ref + unsafe { + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + 1 => std::mem::transmute(y), + //~^ transmute_ptr_to_ref + 2 => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(y), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = std::mem::transmute(a); - //~^ transmute_ptr_to_ref - let _: &u32 = std::mem::transmute::<_, &u32>(a); - //~^ transmute_ptr_to_ref - match 0 { - 0 => std::mem::transmute(x), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = std::mem::transmute(a); - //~^ transmute_ptr_to_ref - let _: &u32 = std::mem::transmute::<_, &u32>(a); - //~^ transmute_ptr_to_ref - match 0 { - 0 => std::mem::transmute(x), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + } } } diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr index 7fad9b4065a56..7685c345c8619 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr @@ -1,137 +1,137 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:9:17 + --> tests/ui/transmute_ptr_to_ref.rs:10:21 | -LL | let _: &T = std::mem::transmute(p); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` +LL | let _: &T = std::mem::transmute(p); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` | = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ref)]` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:13:21 + --> tests/ui/transmute_ptr_to_ref.rs:14:25 | -LL | let _: &mut T = std::mem::transmute(m); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` +LL | let _: &mut T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:17:17 + --> tests/ui/transmute_ptr_to_ref.rs:18:21 | -LL | let _: &T = std::mem::transmute(m); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` +LL | let _: &T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:21:21 + --> tests/ui/transmute_ptr_to_ref.rs:22:25 | -LL | let _: &mut T = std::mem::transmute(p as *mut T); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:25:17 + --> tests/ui/transmute_ptr_to_ref.rs:26:21 | -LL | let _: &T = std::mem::transmute(o); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` +LL | let _: &T = std::mem::transmute(o); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:29:21 + --> tests/ui/transmute_ptr_to_ref.rs:30:25 | -LL | let _: &mut T = std::mem::transmute(om); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` +LL | let _: &mut T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:33:17 + --> tests/ui/transmute_ptr_to_ref.rs:34:21 | -LL | let _: &T = std::mem::transmute(om); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` +LL | let _: &T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:44:32 + --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:47:33 + --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> tests/ui/transmute_ptr_to_ref.rs:52:14 + --> tests/ui/transmute_ptr_to_ref.rs:54:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:58:14 + --> tests/ui/transmute_ptr_to_ref.rs:61:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:60:14 + --> tests/ui/transmute_ptr_to_ref.rs:63:18 | -LL | 1 => std::mem::transmute(y), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` +LL | 1 => std::mem::transmute(y), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:62:14 + --> tests/ui/transmute_ptr_to_ref.rs:65:18 | -LL | 2 => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` +LL | 2 => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:64:14 + --> tests/ui/transmute_ptr_to_ref.rs:67:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(y), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` +LL | _ => std::mem::transmute::<_, &&'b u32>(y), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:73:19 + --> tests/ui/transmute_ptr_to_ref.rs:78:23 | -LL | let _: &u32 = std::mem::transmute(a); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:75:19 + --> tests/ui/transmute_ptr_to_ref.rs:80:23 | -LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:78:14 + --> tests/ui/transmute_ptr_to_ref.rs:83:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:80:14 + --> tests/ui/transmute_ptr_to_ref.rs:85:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:89:19 + --> tests/ui/transmute_ptr_to_ref.rs:96:23 | -LL | let _: &u32 = std::mem::transmute(a); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:91:19 + --> tests/ui/transmute_ptr_to_ref.rs:98:23 | -LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:94:14 + --> tests/ui/transmute_ptr_to_ref.rs:101:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:96:14 + --> tests/ui/transmute_ptr_to_ref.rs:103:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs index bcd35bbd4e72a..f3eb5060cd0d3 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.rs +++ b/src/tools/clippy/tests/ui/transmuting_null.rs @@ -3,6 +3,7 @@ #![allow(clippy::zero_ptr)] #![allow(clippy::transmute_ptr_to_ref)] #![allow(clippy::eq_op, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr index 84e6e374d5253..c68e4102e405b 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.stderr +++ b/src/tools/clippy/tests/ui/transmuting_null.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:10:23 + --> tests/ui/transmuting_null.rs:11:23 | LL | let _: &u64 = std::mem::transmute(0 as *const u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | let _: &u64 = std::mem::transmute(0 as *const u64); = help: to override `-D warnings` add `#[allow(clippy::transmuting_null)]` error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:13:23 + --> tests/ui/transmuting_null.rs:14:23 | LL | let _: &u64 = std::mem::transmute(std::ptr::null::()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:24:23 + --> tests/ui/transmuting_null.rs:25:23 | LL | let _: &u64 = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/type_complexity.rs b/src/tools/clippy/tests/ui/type_complexity.rs index 89c4955c9f6f0..9d145516d6107 100644 --- a/src/tools/clippy/tests/ui/type_complexity.rs +++ b/src/tools/clippy/tests/ui/type_complexity.rs @@ -1,6 +1,5 @@ -#![warn(clippy::all)] -#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)] #![feature(associated_type_defaults)] +#![allow(clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)] type Alias = Vec>>; // no warning here diff --git a/src/tools/clippy/tests/ui/type_complexity.stderr b/src/tools/clippy/tests/ui/type_complexity.stderr index 181e04d38e9a4..a7f6a074a4a4d 100644 --- a/src/tools/clippy/tests/ui/type_complexity.stderr +++ b/src/tools/clippy/tests/ui/type_complexity.stderr @@ -1,5 +1,5 @@ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:7:12 + --> tests/ui/type_complexity.rs:6:12 | LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,85 +8,85 @@ LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:10:12 + --> tests/ui/type_complexity.rs:9:12 | LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:14:8 + --> tests/ui/type_complexity.rs:13:8 | LL | f: Vec>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:18:11 + --> tests/ui/type_complexity.rs:17:11 | LL | struct Ts(Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:22:11 + --> tests/ui/type_complexity.rs:21:11 | LL | Tuple(Vec>>), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:24:17 + --> tests/ui/type_complexity.rs:23:17 | LL | Struct { f: Vec>> }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:29:14 + --> tests/ui/type_complexity.rs:28:14 | LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:32:30 + --> tests/ui/type_complexity.rs:31:30 | LL | fn impl_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:37:14 + --> tests/ui/type_complexity.rs:36:14 | LL | const A: Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:40:14 + --> tests/ui/type_complexity.rs:39:14 | LL | type B = Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:43:25 + --> tests/ui/type_complexity.rs:42:25 | LL | fn method(&self, p: Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:46:29 + --> tests/ui/type_complexity.rs:45:29 | LL | fn def_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:59:15 + --> tests/ui/type_complexity.rs:58:15 | LL | fn test1() -> Vec>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:65:14 + --> tests/ui/type_complexity.rs:64:14 | LL | fn test2(_x: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:69:13 + --> tests/ui/type_complexity.rs:68:13 | LL | let _y: Vec>> = vec![]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index a48397137992f..eeb281322da92 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -15,9 +15,17 @@ union MyOwnMaybeUninit { // https://github.com/rust-lang/rust/issues/119620 unsafe fn requires_paramenv() { - let mut vec = Vec::>::with_capacity(1); + unsafe { + let mut vec = Vec::>::with_capacity(1); + //~^ uninit_vec + vec.set_len(1); + } + + let mut vec = Vec::>::with_capacity(2); //~^ uninit_vec - vec.set_len(1); + unsafe { + vec.set_len(2); + } } fn main() { diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 7ff6140a2c3ec..1b821ef004e6f 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -1,18 +1,29 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:18:5 + --> tests/ui/uninit_vec.rs:24:5 | -LL | let mut vec = Vec::>::with_capacity(1); +LL | let mut vec = Vec::>::with_capacity(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | vec.set_len(1); - | ^^^^^^^^^^^^^^ +... +LL | vec.set_len(2); + | ^^^^^^^^^^^^^^ | = help: initialize the buffer or wrap the content in `MaybeUninit` = note: `-D clippy::uninit-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:25:5 + --> tests/ui/uninit_vec.rs:19:9 + | +LL | let mut vec = Vec::>::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:33:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +34,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:33:5 + --> tests/ui/uninit_vec.rs:41:5 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -34,7 +45,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:41:5 + --> tests/ui/uninit_vec.rs:49:5 | LL | let mut vec: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +54,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:49:5 + --> tests/ui/uninit_vec.rs:57:5 | LL | let mut vec: Vec = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +63,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:56:5 + --> tests/ui/uninit_vec.rs:64:5 | LL | let mut vec: Vec = Vec::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +72,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:76:5 + --> tests/ui/uninit_vec.rs:84:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +83,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:87:5 + --> tests/ui/uninit_vec.rs:95:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +94,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:94:5 + --> tests/ui/uninit_vec.rs:102:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +105,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:65:9 + --> tests/ui/uninit_vec.rs:73:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +116,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:70:9 + --> tests/ui/uninit_vec.rs:78:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -116,7 +127,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:150:9 + --> tests/ui/uninit_vec.rs:158:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +138,7 @@ LL | vec.set_len(10); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:178:9 + --> tests/ui/uninit_vec.rs:186:9 | LL | let mut vec: Vec> = Vec::with_capacity(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +149,7 @@ LL | vec.set_len(1); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:192:9 + --> tests/ui/uninit_vec.rs:200:9 | LL | let mut vec: Vec> = Vec::with_capacity(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,5 +159,5 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs index 2bb64c3e80e34..4b1f4f76cc45e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs @@ -17,8 +17,10 @@ mod issue11113 { impl TearOff { unsafe fn query(&self) { - ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() - //~^ unnecessary_cast + unsafe { + ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + //~^ unnecessary_cast + } } } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr index 6ba1c78730667..6b26bea9de2a7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr @@ -8,10 +8,10 @@ LL | let _ = std::ptr::null() as *const u8; = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting raw pointers to the same type and constness is unnecessary (`*mut issue11113::Vtbl` -> `*mut issue11113::Vtbl`) - --> tests/ui/unnecessary_cast_unfixable.rs:20:16 + --> tests/ui/unnecessary_cast_unfixable.rs:21:20 | -LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` +LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs index c4f1b6bc7e3d7..85582c399ce5f 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs @@ -1,5 +1,4 @@ -//@no-rustfix -#![allow(dead_code)] +#![allow(clippy::redundant_closure)] fn main() { let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); @@ -27,9 +26,7 @@ fn main() { let _ = (0..4).filter_map(Some); let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - //~^ redundant_closure - //~| unnecessary_filter_map - //~| unnecessary_filter_map + //~^ unnecessary_filter_map } fn filter_map_none_changes_item_type() -> impl Iterator { diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr index 6683444b72730..a879633e10f2a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr @@ -1,14 +1,14 @@ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:5:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:4:13 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:8:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:7:13 | LL | let _ = (0..4).filter_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | if x > 1 { ... | LL | | None LL | | }); - | |______^ help: try instead: `filter` + | |______^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:16:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:15:13 | LL | let _ = (0..4).filter_map(|x| match x { | _____________^ @@ -29,40 +29,25 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ help: try instead: `filter` + | |______^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:22:13 +error: this `.filter_map(..)` can be written more simply using `.map(..)` + --> tests/ui/unnecessary_filter_map.rs:21:13 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: redundant closure - --> tests/ui/unnecessary_filter_map.rs:29:57 +error: this call to `.filter_map(..)` is unnecessary + --> tests/ui/unnecessary_filter_map.rs:28:61 | LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^^^^^^^^ help: replace the closure with the function itself: `Some` - | - = note: `-D clippy::redundant-closure` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` - -error: filter_map is unnecessary - --> tests/ui/unnecessary_filter_map.rs:29:61 - | -LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^ help: try removing the filter_map - -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:29:13 - | -LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + | ^^^^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:169:14 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:166:14 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.rs b/src/tools/clippy/tests/ui/unnecessary_find_map.rs index 8c8a3799f0216..33ba7074d623b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![allow(dead_code)] fn main() { diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr index 94e320773a6fe..3754a3d99538e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr @@ -1,14 +1,14 @@ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:5:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:4:13 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:8:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:7:13 | LL | let _ = (0..4).find_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | if x > 1 { ... | LL | | None LL | | }); - | |______^ help: try instead: `find` + | |______^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:16:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:15:13 | LL | let _ = (0..4).find_map(|x| match x { | _____________^ @@ -29,19 +29,19 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ help: try instead: `find` + | |______^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:22:13 +error: this `.find_map(..)` can be written more simply using `.map(..).next()` + --> tests/ui/unnecessary_find_map.rs:21:13 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map(..).next()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:34:14 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:33:14 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed index aed2dbe1f1ce4..61f2e3745ad05 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, clippy::uninlined_format_args)] #![warn(clippy::unnecessary_to_owned)] #[allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs index 12fdd150e4233..b90ca00a5fece 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, clippy::uninlined_format_args)] #![warn(clippy::unnecessary_to_owned)] #[allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 9a32908163897..409a8efbfeb95 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -321,3 +321,7 @@ fn panicky_arithmetic_ops(x: usize, y: isize) { let _x = false.then_some(f1 + f2); //~^ unnecessary_lazy_evaluations } + +fn issue14578() { + let _: Box> = Box::new(true.then(async || 42).unwrap()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 2d05ef5c29175..54735023a935d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -321,3 +321,7 @@ fn panicky_arithmetic_ops(x: usize, y: isize) { let _x = false.then(|| f1 + f2); //~^ unnecessary_lazy_evaluations } + +fn issue14578() { + let _: Box> = Box::new(true.then(async || 42).unwrap()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed index 05dfb72f48d2d..645b56fe95e74 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -1,9 +1,10 @@ #![allow( clippy::deref_addrof, - dead_code, - unused, clippy::no_effect, - clippy::unnecessary_struct_initialization + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization, + dead_code, + unused )] #![warn(clippy::unnecessary_operation)] diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs index 6ef74c3eb1c1e..97e90269c5c0c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.rs +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -1,9 +1,10 @@ #![allow( clippy::deref_addrof, - dead_code, - unused, clippy::no_effect, - clippy::unnecessary_struct_initialization + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization, + dead_code, + unused )] #![warn(clippy::unnecessary_operation)] diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr index eb98af09e7a3d..0fda1dfde1903 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:70:5 + --> tests/ui/unnecessary_operation.rs:71:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -8,103 +8,103 @@ LL | Tuple(get_number()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:72:5 + --> tests/ui/unnecessary_operation.rs:73:5 | LL | Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:74:5 + --> tests/ui/unnecessary_operation.rs:75:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:76:5 + --> tests/ui/unnecessary_operation.rs:77:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:78:5 + --> tests/ui/unnecessary_operation.rs:79:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:80:5 + --> tests/ui/unnecessary_operation.rs:81:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:82:5 + --> tests/ui/unnecessary_operation.rs:83:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:84:5 + --> tests/ui/unnecessary_operation.rs:85:5 | LL | &get_number(); | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:86:5 + --> tests/ui/unnecessary_operation.rs:87:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:88:5 + --> tests/ui/unnecessary_operation.rs:89:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:90:5 + --> tests/ui/unnecessary_operation.rs:91:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:92:5 + --> tests/ui/unnecessary_operation.rs:93:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:94:5 + --> tests/ui/unnecessary_operation.rs:95:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:96:5 + --> tests/ui/unnecessary_operation.rs:97:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:98:5 + --> tests/ui/unnecessary_operation.rs:99:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:100:5 + --> tests/ui/unnecessary_operation.rs:101:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:102:5 + --> tests/ui/unnecessary_operation.rs:103:5 | LL | [42; 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:104:5 + --> tests/ui/unnecessary_operation.rs:105:5 | LL | / { LL | | @@ -113,7 +113,7 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:108:5 + --> tests/ui/unnecessary_operation.rs:109:5 | LL | / FooString { LL | | @@ -122,7 +122,7 @@ LL | | }; | |______^ help: statement can be reduced to: `String::from("blah");` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:149:5 + --> tests/ui/unnecessary_operation.rs:150:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` diff --git a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs index 12663ec9a528f..6652efd9ae1d8 100644 --- a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs +++ b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs @@ -1,4 +1,5 @@ #![warn(clippy::unnecessary_debug_formatting)] +#![allow(clippy::uninlined_format_args)] use std::ffi::{OsStr, OsString}; diff --git a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr index 001309ab817a1..382e59b046193 100644 --- a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr @@ -1,5 +1,5 @@ error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:14:22 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:15:22 | LL | println!("{:?}", os_str); | ^^^^^^ @@ -10,7 +10,7 @@ LL | println!("{:?}", os_str); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_debug_formatting)]` error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:15:22 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:16:22 | LL | println!("{:?}", os_string); | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", os_string); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:17:16 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:18:16 | LL | println!("{os_str:?}"); | ^^^^^^ @@ -28,7 +28,7 @@ LL | println!("{os_str:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:18:16 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:19:16 | LL | println!("{os_string:?}"); | ^^^^^^^^^ @@ -37,7 +37,7 @@ LL | println!("{os_string:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:20:37 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:21:37 | LL | let _: String = format!("{:?}", os_str); | ^^^^^^ @@ -46,7 +46,7 @@ LL | let _: String = format!("{:?}", os_str); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:21:37 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:22:37 | LL | let _: String = format!("{:?}", os_string); | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs index f14f6085c9a14..215e0d5d7802e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs +++ b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs @@ -1,4 +1,5 @@ #![warn(clippy::unnecessary_debug_formatting)] +#![allow(clippy::uninlined_format_args)] use std::ffi::{OsStr, OsString}; use std::ops::Deref; diff --git a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr index f12fa72c84b35..d244b9ad6716a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr @@ -1,5 +1,5 @@ error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:29:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:30:22 | LL | println!("{:?}", os_str); | ^^^^^^ @@ -10,7 +10,7 @@ LL | println!("{:?}", os_str); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_debug_formatting)]` error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:30:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:31:22 | LL | println!("{:?}", os_string); | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", os_string); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:32:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:33:22 | LL | println!("{:?}", path); | ^^^^ @@ -28,7 +28,7 @@ LL | println!("{:?}", path); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:33:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:34:22 | LL | println!("{:?}", path_buf); | ^^^^^^^^ @@ -37,7 +37,7 @@ LL | println!("{:?}", path_buf); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:35:16 + --> tests/ui/unnecessary_path_debug_formatting.rs:36:16 | LL | println!("{path:?}"); | ^^^^ @@ -46,7 +46,7 @@ LL | println!("{path:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:36:16 + --> tests/ui/unnecessary_path_debug_formatting.rs:37:16 | LL | println!("{path_buf:?}"); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | println!("{path_buf:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:38:37 + --> tests/ui/unnecessary_path_debug_formatting.rs:39:37 | LL | let _: String = format!("{:?}", path); | ^^^^ @@ -64,7 +64,7 @@ LL | let _: String = format!("{:?}", path); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:39:37 + --> tests/ui/unnecessary_path_debug_formatting.rs:40:37 | LL | let _: String = format!("{:?}", path_buf); | ^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _: String = format!("{:?}", path_buf); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:42:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:43:22 | LL | println!("{:?}", &*deref_path); | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index 5410033dbd8f4..b064a8b8f46fb 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -1,10 +1,11 @@ #![allow( + clippy::manual_async_fn, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, clippy::needless_lifetimes, - clippy::owned_cow + clippy::owned_cow, + clippy::ptr_arg, + clippy::uninlined_format_args )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 0619dd4ddec09..7954a4ad4ce77 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -1,10 +1,11 @@ #![allow( + clippy::manual_async_fn, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, clippy::needless_lifetimes, - clippy::owned_cow + clippy::owned_cow, + clippy::ptr_arg, + clippy::uninlined_format_args )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 8926db34da8c8..6c52be8393010 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:217:64 + --> tests/ui/unnecessary_to_owned.rs:218:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:217:20 + --> tests/ui/unnecessary_to_owned.rs:218:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:219:40 + --> tests/ui/unnecessary_to_owned.rs:220:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:219:21 + --> tests/ui/unnecessary_to_owned.rs:220:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:221:48 + --> tests/ui/unnecessary_to_owned.rs:222:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:221:19 + --> tests/ui/unnecessary_to_owned.rs:222:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:223:35 + --> tests/ui/unnecessary_to_owned.rs:224:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:223:18 + --> tests/ui/unnecessary_to_owned.rs:224:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:225:39 + --> tests/ui/unnecessary_to_owned.rs:226:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:225:20 + --> tests/ui/unnecessary_to_owned.rs:226:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:65:36 + --> tests/ui/unnecessary_to_owned.rs:66:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,391 +70,391 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:67:19 + --> tests/ui/unnecessary_to_owned.rs:68:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> tests/ui/unnecessary_to_owned.rs:70:20 + --> tests/ui/unnecessary_to_owned.rs:71:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:72:38 + --> tests/ui/unnecessary_to_owned.rs:73:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:74:20 + --> tests/ui/unnecessary_to_owned.rs:75:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> tests/ui/unnecessary_to_owned.rs:77:18 + --> tests/ui/unnecessary_to_owned.rs:78:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:79:34 + --> tests/ui/unnecessary_to_owned.rs:80:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:81:18 + --> tests/ui/unnecessary_to_owned.rs:82:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:84:17 + --> tests/ui/unnecessary_to_owned.rs:85:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:86:30 + --> tests/ui/unnecessary_to_owned.rs:87:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:88:17 + --> tests/ui/unnecessary_to_owned.rs:89:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:90:17 + --> tests/ui/unnecessary_to_owned.rs:91:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:93:19 + --> tests/ui/unnecessary_to_owned.rs:94:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:95:36 + --> tests/ui/unnecessary_to_owned.rs:96:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:97:19 + --> tests/ui/unnecessary_to_owned.rs:98:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:99:19 + --> tests/ui/unnecessary_to_owned.rs:100:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:101:19 + --> tests/ui/unnecessary_to_owned.rs:102:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:105:42 + --> tests/ui/unnecessary_to_owned.rs:106:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:109:25 + --> tests/ui/unnecessary_to_owned.rs:110:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:111:26 + --> tests/ui/unnecessary_to_owned.rs:112:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:113:24 + --> tests/ui/unnecessary_to_owned.rs:114:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:115:23 + --> tests/ui/unnecessary_to_owned.rs:116:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:117:25 + --> tests/ui/unnecessary_to_owned.rs:118:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:120:30 + --> tests/ui/unnecessary_to_owned.rs:121:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:122:31 + --> tests/ui/unnecessary_to_owned.rs:123:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:29 + --> tests/ui/unnecessary_to_owned.rs:125:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:28 + --> tests/ui/unnecessary_to_owned.rs:127:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:30 + --> tests/ui/unnecessary_to_owned.rs:129:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:131:29 + --> tests/ui/unnecessary_to_owned.rs:132:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:131:43 + --> tests/ui/unnecessary_to_owned.rs:132:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:134:29 + --> tests/ui/unnecessary_to_owned.rs:135:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:134:47 + --> tests/ui/unnecessary_to_owned.rs:135:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:138:26 + --> tests/ui/unnecessary_to_owned.rs:139:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:140:27 + --> tests/ui/unnecessary_to_owned.rs:141:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:142:25 + --> tests/ui/unnecessary_to_owned.rs:143:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:144:24 + --> tests/ui/unnecessary_to_owned.rs:145:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:146:24 + --> tests/ui/unnecessary_to_owned.rs:147:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:148:26 + --> tests/ui/unnecessary_to_owned.rs:149:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:150:26 + --> tests/ui/unnecessary_to_owned.rs:151:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:152:26 + --> tests/ui/unnecessary_to_owned.rs:153:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:155:31 + --> tests/ui/unnecessary_to_owned.rs:156:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:157:32 + --> tests/ui/unnecessary_to_owned.rs:158:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:159:30 + --> tests/ui/unnecessary_to_owned.rs:160:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:161:29 + --> tests/ui/unnecessary_to_owned.rs:162:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:163:29 + --> tests/ui/unnecessary_to_owned.rs:164:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:165:31 + --> tests/ui/unnecessary_to_owned.rs:166:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:167:31 + --> tests/ui/unnecessary_to_owned.rs:168:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:169:31 + --> tests/ui/unnecessary_to_owned.rs:170:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:172:30 + --> tests/ui/unnecessary_to_owned.rs:173:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:172:44 + --> tests/ui/unnecessary_to_owned.rs:173:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:175:30 + --> tests/ui/unnecessary_to_owned.rs:176:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:175:44 + --> tests/ui/unnecessary_to_owned.rs:176:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:178:30 + --> tests/ui/unnecessary_to_owned.rs:179:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:178:44 + --> tests/ui/unnecessary_to_owned.rs:179:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:181:30 + --> tests/ui/unnecessary_to_owned.rs:182:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:181:48 + --> tests/ui/unnecessary_to_owned.rs:182:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:184:30 + --> tests/ui/unnecessary_to_owned.rs:185:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:184:52 + --> tests/ui/unnecessary_to_owned.rs:185:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:187:30 + --> tests/ui/unnecessary_to_owned.rs:188:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:187:48 + --> tests/ui/unnecessary_to_owned.rs:188:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:191:20 + --> tests/ui/unnecessary_to_owned.rs:192:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:194:13 + --> tests/ui/unnecessary_to_owned.rs:195:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:196:13 + --> tests/ui/unnecessary_to_owned.rs:197:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:199:13 + --> tests/ui/unnecessary_to_owned.rs:200:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:201:13 + --> tests/ui/unnecessary_to_owned.rs:202:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:229:26 + --> tests/ui/unnecessary_to_owned.rs:230:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:231:26 + --> tests/ui/unnecessary_to_owned.rs:232:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:233:26 + --> tests/ui/unnecessary_to_owned.rs:234:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:291:14 + --> tests/ui/unnecessary_to_owned.rs:292:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -503,49 +503,49 @@ LL ~ let path = match get_file_path(t) { | error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:357:24 + --> tests/ui/unnecessary_to_owned.rs:358:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:467:12 + --> tests/ui/unnecessary_to_owned.rs:468:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:611:37 + --> tests/ui/unnecessary_to_owned.rs:612:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:622:18 + --> tests/ui/unnecessary_to_owned.rs:623:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:627:14 + --> tests/ui/unnecessary_to_owned.rs:628:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:629:14 + --> tests/ui/unnecessary_to_owned.rs:630:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:635:14 + --> tests/ui/unnecessary_to_owned.rs:636:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:637:14 + --> tests/ui/unnecessary_to_owned.rs:638:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed index 791b2fa131f23..2081772d06b36 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -64,3 +64,16 @@ fn msrv_1_53() { if let [1 | 53] = [0] {} //~^ unnested_or_patterns } + +mod issue9952 { + fn or_in_local() { + let (0 | 1 | _) = 0; + //~^ unnested_or_patterns + + if let (0 | 1 | _) = 0 {} + //~^ unnested_or_patterns + } + + fn or_in_param((x | x | x): i32) {} + //~^ unnested_or_patterns +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs index e7e7c7cd2e494..6bf8fce36616c 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -64,3 +64,16 @@ fn msrv_1_53() { if let [1] | [53] = [0] {} //~^ unnested_or_patterns } + +mod issue9952 { + fn or_in_local() { + let (0 | (1 | _)) = 0; + //~^ unnested_or_patterns + + if let (0 | (1 | _)) = 0 {} + //~^ unnested_or_patterns + } + + fn or_in_param((x | (x | x)): i32) {} + //~^ unnested_or_patterns +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr index ec5eb983c5a01..c805dc992b1c2 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr @@ -204,5 +204,41 @@ LL - if let [1] | [53] = [0] {} LL + if let [1 | 53] = [0] {} | -error: aborting due to 17 previous errors +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:70:13 + | +LL | let (0 | (1 | _)) = 0; + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - let (0 | (1 | _)) = 0; +LL + let (0 | 1 | _) = 0; + | + +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:73:16 + | +LL | if let (0 | (1 | _)) = 0 {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - if let (0 | (1 | _)) = 0 {} +LL + if let (0 | 1 | _) = 0 {} + | + +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:77:20 + | +LL | fn or_in_param((x | (x | x)): i32) {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - fn or_in_param((x | (x | x)): i32) {} +LL + fn or_in_param((x | x | x): i32) {} + | + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/unwrap_or.fixed b/src/tools/clippy/tests/ui/unwrap_or.fixed index c794ed577032d..e550484b5d9f0 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/unwrap_or.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::or_fun_call)] +#![warn(clippy::or_fun_call)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs index 11a6883b7403f..cdd61ac898e6d 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.rs +++ b/src/tools/clippy/tests/ui/unwrap_or.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::or_fun_call)] +#![warn(clippy::or_fun_call)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { diff --git a/src/tools/clippy/tests/ui/used_underscore_items.rs b/src/tools/clippy/tests/ui/used_underscore_items.rs index 3401df6ae7438..7e8289f1406ba 100644 --- a/src/tools/clippy/tests/ui/used_underscore_items.rs +++ b/src/tools/clippy/tests/ui/used_underscore_items.rs @@ -73,7 +73,7 @@ fn external_item_call() { // should not lint foreign functions. // issue #14156 -extern "C" { +unsafe extern "C" { pub fn _exit(code: i32) -> !; } diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 33d3b0728f3d1..f27b109e99536 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -9,11 +9,32 @@ allow-unauthenticated = [ # See https://forge.rust-lang.org/triagebot/shortcuts.html [shortcut] +[merge-conflicts] + +[note] + +[canonicalize-issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] + # Have rustbot inform users about the *No Merge Policy* [no-merges] exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust labels = ["has-merge-commits", "S-waiting-on-author"] +[review-requested] +# Those labels are removed when PR author requests a review from an assignee +remove_labels = ["S-waiting-on-author"] +# Those labels are added when PR author requests a review from an assignee +add_labels = ["S-waiting-on-review"] + +[review-submitted] +# These labels are removed when a review is submitted. +review_labels = ["S-waiting-on-review"] +# This label is added when a review is submitted. +reviewed_label = "S-waiting-on-author" + [autolabel."S-waiting-on-review"] new_pr = true @@ -21,10 +42,12 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", + "samueltardieu", ] [assign.owners] "/.github" = ["@flip1995"] +"/triagebot.toml" = ["@flip1995"] "/book" = ["@flip1995"] "*" = [ "@Manishearth", @@ -34,4 +57,5 @@ users_on_vacation = [ "@Jarcho", "@blyxyas", "@y21", + "@samueltardieu", ] diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index ba1b8f256586e..93f7b1cb7cf2c 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -18,6 +18,7 @@ glob = "0.3.0" home = "0.5.5" indexmap = "2.0.0" miropt-test-tools = { path = "../miropt-test-tools" } +rayon = "1.10.0" regex = "1.0" rustfix = "0.8.1" semver = { version = "1.0.23", features = ["serde"] } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 31c696ed41ff4..b5bbe70c48ce4 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -399,7 +399,6 @@ pub struct Config { pub nocapture: bool, // Needed both to construct build_helper::git::GitConfig - pub git_repository: String, pub nightly_branch: String, pub git_merge_commit_email: String, @@ -511,7 +510,6 @@ impl Config { pub fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.git_repository, nightly_branch: &self.nightly_branch, git_merge_commit_email: &self.git_merge_commit_email, } diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index 0c173d476affa..990be56ce0c5e 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -57,9 +57,11 @@ pub(crate) fn run_tests(config: &Config, tests: Vec) -> bool { } let completion = deadline_queue - .read_channel_while_checking_deadlines(&completion_rx, |_id, test| { - listener.test_timed_out(test); - }) + .read_channel_while_checking_deadlines( + &completion_rx, + |id| running_tests.contains_key(&id), + |_id, test| listener.test_timed_out(test), + ) .expect("receive channel should never be closed early"); let RunningTest { test, join_handle } = running_tests.remove(&completion.id).unwrap(); diff --git a/src/tools/compiletest/src/executor/deadline.rs b/src/tools/compiletest/src/executor/deadline.rs index 83b8591a41642..3536eff2fd80d 100644 --- a/src/tools/compiletest/src/executor/deadline.rs +++ b/src/tools/compiletest/src/executor/deadline.rs @@ -21,16 +21,26 @@ impl<'a> DeadlineQueue<'a> { Self { queue: VecDeque::with_capacity(capacity) } } + /// All calls to [`Instant::now`] go through this wrapper method. + /// This makes it easier to find all places that read the current time. + fn now(&self) -> Instant { + Instant::now() + } + pub(crate) fn push(&mut self, id: TestId, test: &'a CollectedTest) { - let deadline = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); + let deadline = self.now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); + if let Some(back) = self.queue.back() { + assert!(back.deadline <= deadline); + } self.queue.push_back(DeadlineEntry { id, test, deadline }); } - /// Equivalent to `rx.read()`, except that if any test exceeds its deadline + /// Equivalent to `rx.recv()`, except that if a test exceeds its deadline /// during the wait, the given callback will also be called for that test. pub(crate) fn read_channel_while_checking_deadlines( &mut self, rx: &mpsc::Receiver, + is_running: impl Fn(TestId) -> bool, mut on_deadline_passed: impl FnMut(TestId, &CollectedTest), ) -> Result { loop { @@ -39,18 +49,18 @@ impl<'a> DeadlineQueue<'a> { // deadline, so do a normal receive. return rx.recv(); }; - let wait_duration = next_deadline.saturating_duration_since(Instant::now()); + let next_deadline_timeout = next_deadline.saturating_duration_since(self.now()); + + let recv_result = rx.recv_timeout(next_deadline_timeout); + // Process deadlines after every receive attempt, regardless of + // outcome, so that we don't build up an unbounded backlog of stale + // entries due to a constant stream of tests finishing. + self.for_each_entry_past_deadline(&is_running, &mut on_deadline_passed); - let recv_result = rx.recv_timeout(wait_duration); match recv_result { Ok(value) => return Ok(value), - Err(RecvTimeoutError::Timeout) => { - // Notify the callback of tests that have exceeded their - // deadline, then loop and do annother channel read. - for DeadlineEntry { id, test, .. } in self.remove_tests_past_deadline() { - on_deadline_passed(id, test); - } - } + // Deadlines have already been processed, so loop and do another receive. + Err(RecvTimeoutError::Timeout) => {} Err(RecvTimeoutError::Disconnected) => return Err(RecvError), } } @@ -60,14 +70,28 @@ impl<'a> DeadlineQueue<'a> { Some(self.queue.front()?.deadline) } - fn remove_tests_past_deadline(&mut self) -> Vec> { - let now = Instant::now(); - let mut timed_out = vec![]; - while let Some(deadline_entry) = pop_front_if(&mut self.queue, |entry| now < entry.deadline) - { - timed_out.push(deadline_entry); + fn for_each_entry_past_deadline( + &mut self, + is_running: impl Fn(TestId) -> bool, + mut on_deadline_passed: impl FnMut(TestId, &CollectedTest), + ) { + let now = self.now(); + + // Clear out entries that are past their deadline, but only invoke the + // callback for tests that are still considered running. + while let Some(entry) = pop_front_if(&mut self.queue, |entry| entry.deadline <= now) { + if is_running(entry.id) { + on_deadline_passed(entry.id, entry.test); + } + } + + // Also clear out any leading entries that are no longer running, even + // if their deadline hasn't been reached. + while let Some(_) = pop_front_if(&mut self.queue, |entry| !is_running(entry.id)) {} + + if let Some(front) = self.queue.front() { + assert!(now < front.deadline); } - timed_out } } diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 2525e0adc8385..e7e5ff0ab0093 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -175,7 +175,6 @@ impl ConfigBuilder { self.host.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), "--target", self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), - "--git-repository=", "--nightly-branch=", "--git-merge-commit-email=", "--minicore-path=", diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 4bbd4ab4790d8..159b81cefa082 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -33,6 +33,7 @@ use std::{env, fs, vec}; use build_helper::git::{get_git_modified_files, get_git_untracked_files}; use camino::{Utf8Path, Utf8PathBuf}; use getopts::Options; +use rayon::iter::{ParallelBridge, ParallelIterator}; use tracing::*; use walkdir::WalkDir; @@ -188,7 +189,6 @@ pub fn parse_config(args: Vec) -> Config { "run tests which rely on commit version being compiled into the binaries", ) .optopt("", "edition", "default Rust edition", "EDITION") - .reqopt("", "git-repository", "name of the git repository", "ORG/REPO") .reqopt("", "nightly-branch", "name of the git branch for nightly", "BRANCH") .reqopt( "", @@ -440,7 +440,6 @@ pub fn parse_config(args: Vec) -> Config { nocapture: matches.opt_present("no-capture"), - git_repository: matches.opt_str("git-repository").unwrap(), nightly_branch: matches.opt_str("nightly-branch").unwrap(), git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(), @@ -640,6 +639,18 @@ struct TestCollector { poisoned: bool, } +impl TestCollector { + fn new() -> Self { + TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false } + } + + fn merge(&mut self, mut other: Self) { + self.tests.append(&mut other.tests); + self.found_path_stems.extend(other.found_path_stems); + self.poisoned |= other.poisoned; + } +} + /// Creates test structures for every test/revision in the test suite directory. /// /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests), @@ -658,10 +669,7 @@ pub(crate) fn collect_and_make_tests(config: Arc) -> Vec let cache = HeadersCache::load(&config); let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests }; - let mut collector = - TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; - - collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Utf8Path::new("")) + let collector = collect_tests_from_dir(&cx, &cx.config.src_test_suite_root, Utf8Path::new("")) .unwrap_or_else(|reason| { panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root) }); @@ -745,7 +753,7 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result, S &vec!["rs", "stderr", "fixed"], )?; // Add new test cases to the list, it will be convenient in daily development. - let untracked_files = get_git_untracked_files(&config.git_config(), None)?.unwrap_or(vec![]); + let untracked_files = get_git_untracked_files(Some(dir.as_std_path()))?.unwrap_or(vec![]); let all_paths = [&files[..], &untracked_files[..]].concat(); let full_paths = { @@ -767,25 +775,25 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result, S /// that will be handed over to libtest. fn collect_tests_from_dir( cx: &TestCollectorCx, - collector: &mut TestCollector, dir: &Utf8Path, relative_dir_path: &Utf8Path, -) -> io::Result<()> { +) -> io::Result { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { - return Ok(()); + return Ok(TestCollector::new()); } // For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`. if cx.config.mode == Mode::RunMake { + let mut collector = TestCollector::new(); if dir.join("rmake.rs").exists() { let paths = TestPaths { file: dir.to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; - make_test(cx, collector, &paths); + make_test(cx, &mut collector, &paths); // This directory is a test, so don't try to find other tests inside it. - return Ok(()); + return Ok(collector); } } @@ -802,36 +810,47 @@ fn collect_tests_from_dir( // subdirectories we find, except for `auxiliary` directories. // FIXME: this walks full tests tree, even if we have something to ignore // use walkdir/ignore like in tidy? - for file in fs::read_dir(dir.as_std_path())? { - let file = file?; - let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); - let file_name = file_path.file_name().unwrap(); - - if is_test(file_name) - && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) - { - // We found a test file, so create the corresponding libtest structures. - debug!(%file_path, "found test file"); - - // Record the stem of the test file, to check for overlaps later. - let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); - collector.found_path_stems.insert(rel_test_path); - - let paths = - TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; - make_test(cx, collector, &paths); - } else if file_path.is_dir() { - // Recurse to find more tests in a subdirectory. - let relative_file_path = relative_dir_path.join(file_name); - if file_name != "auxiliary" { - debug!(%file_path, "found directory"); - collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?; + fs::read_dir(dir.as_std_path())? + .par_bridge() + .map(|file| { + let mut collector = TestCollector::new(); + let file = file?; + let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); + let file_name = file_path.file_name().unwrap(); + + if is_test(file_name) + && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) + { + // We found a test file, so create the corresponding libtest structures. + debug!(%file_path, "found test file"); + + // Record the stem of the test file, to check for overlaps later. + let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); + collector.found_path_stems.insert(rel_test_path); + + let paths = + TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; + make_test(cx, &mut collector, &paths); + } else if file_path.is_dir() { + // Recurse to find more tests in a subdirectory. + let relative_file_path = relative_dir_path.join(file_name); + if file_name != "auxiliary" { + debug!(%file_path, "found directory"); + collector.merge(collect_tests_from_dir(cx, &file_path, &relative_file_path)?); + } + } else { + debug!(%file_path, "found other file/directory"); } - } else { - debug!(%file_path, "found other file/directory"); - } - } - Ok(()) + Ok(collector) + }) + .reduce( + || Ok(TestCollector::new()), + |a, b| { + let mut a = a?; + a.merge(b?); + Ok(a) + }, + ) } /// Returns true if `file_name` looks like a proper test file name. diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index d11f5c1a3a6f4..fe23cce81e908 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1395,14 +1395,6 @@ impl<'test> TestCx<'test> { matches!(self.config.suite.as_str(), "rustdoc-ui" | "rustdoc-js" | "rustdoc-json") } - fn get_mir_dump_dir(&self) -> Utf8PathBuf { - let mut mir_dump_dir = self.config.build_test_suite_root.clone(); - debug!("input_file: {}", self.testpaths.file); - mir_dump_dir.push(&self.testpaths.relative_dir); - mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); - mir_dump_dir - } - fn make_compile_args( &self, input_file: &Utf8Path, @@ -1511,10 +1503,7 @@ impl<'test> TestCx<'test> { } let set_mir_dump_dir = |rustc: &mut Command| { - let mir_dump_dir = self.get_mir_dump_dir(); - remove_and_create_dir_all(&mir_dump_dir).unwrap_or_else(|e| { - panic!("failed to remove and recreate output directory `{mir_dump_dir}`: {e}") - }); + let mir_dump_dir = self.output_base_dir(); let mut dir_opt = "-Zdump-mir-dir=".to_string(); dir_opt.push_str(mir_dump_dir.as_str()); debug!("dir_opt: {:?}", dir_opt); @@ -1721,12 +1710,16 @@ impl<'test> TestCx<'test> { rustc.args(&self.props.compile_flags); // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with - // something that's not `abort`, however, by moving this last we should override previous - // `-Cpanic=`s + // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`, + // however, by moving this last we should override previous `-Cpanic`s and + // `-Cforce-unwind-tables`s. Note that checking here is very fragile, because we'd have to + // account for all possible compile flag splittings (they have some... intricacies and are + // not yet normalized). // // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics. if self.props.add_core_stubs { rustc.arg("-Cpanic=abort"); + rustc.arg("-Cforce-unwind-tables=yes"); } rustc diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs index ded6a68fe5876..efdb131bf14a8 100644 --- a/src/tools/compiletest/src/runtest/mir_opt.rs +++ b/src/tools/compiletest/src/runtest/mir_opt.rs @@ -56,7 +56,7 @@ impl TestCx<'_> { self.diff_mir_files(from_file.into(), after.into()) } else { let mut output_file = Utf8PathBuf::new(); - output_file.push(self.get_mir_dump_dir()); + output_file.push(self.output_base_dir()); output_file.push(&from_file); debug!("comparing the contents of: {} with {:?}", output_file, expected_file); if !output_file.exists() { @@ -100,7 +100,7 @@ impl TestCx<'_> { fn diff_mir_files(&self, before: Utf8PathBuf, after: Utf8PathBuf) -> String { let to_full_path = |path: Utf8PathBuf| { - let full = self.get_mir_dump_dir().join(&path); + let full = self.output_base_dir().join(&path); if !full.exists() { panic!( "the mir dump file for {} does not exist (requested in {})", diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 79e419884c68e..65ad38da98b08 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -154,6 +154,7 @@ impl CommandKind { static LINE_PATTERN: LazyLock = LazyLock::new(|| { RegexBuilder::new( r#" + ^\s* //@\s+ (?P!?) (?P[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 7d9f77f3752d9..322ef0a6c2aa8 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -53,7 +53,7 @@ fn main() { // with `RustcPhase::Rustdoc`. There we perform a check-build (needed to get the expected // build failures for `compile_fail` doctests) and then store a JSON file with the // information needed to run this test. - // - We also set `--runtool` to ourselves, which ends up in `phase_runner` with + // - We also set `--test-runtool` to ourselves, which ends up in `phase_runner` with // `RunnerPhase::Rustdoc`. There we parse the JSON file written in `phase_rustc` and invoke // the Miri driver for interpretation. diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 71ea07f34636a..cb62e12413c84 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -666,8 +666,8 @@ pub fn phase_rustdoc(mut args: impl Iterator) { if arg == "--extern" { // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files. forward_patched_extern_arg(&mut args, &mut cmd); - } else if arg == "--runtool" { - // An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support. + } else if arg == "--test-runtool" { + // An existing --test-runtool flag indicates cargo is running in cross-target mode, which we don't support. // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag; // otherwise, we won't be called as rustdoc at all. show_error!("cross-interpreting doctests is not currently supported by Miri."); @@ -693,8 +693,8 @@ pub fn phase_rustdoc(mut args: impl Iterator) { // to let phase_cargo_rustc know to expect that. We'll use this environment variable as a flag: cmd.env("MIRI_CALLED_FROM_RUSTDOC", "1"); - // The `--test-builder` and `--runtool` arguments are unstable rustdoc features, - // which are disabled by default. We first need to enable them explicitly: + // The `--test-builder` is an unstable rustdoc features, + // which is disabled by default. We first need to enable them explicitly: cmd.arg("-Zunstable-options"); // rustdoc needs to know the right sysroot. @@ -705,7 +705,7 @@ pub fn phase_rustdoc(mut args: impl Iterator) { // Make rustdoc call us back. let cargo_miri_path = env::current_exe().expect("current executable path invalid"); cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments - cmd.arg("--runtool").arg(&cargo_miri_path); // invoked with just a single path argument + cmd.arg("--test-runtool").arg(&cargo_miri_path); // invoked with just a single path argument debug_cmd("[cargo-miri rustdoc]", verbose, &cmd); exec(cmd) diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index a3525dcc77ae6..7d60a7e5c4895 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -391,32 +391,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } - #[rustfmt::skip] - | "fadd_algebraic" - | "fsub_algebraic" - | "fmul_algebraic" - | "fdiv_algebraic" - | "frem_algebraic" - => { - let [a, b] = check_intrinsic_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_algebraic" => mir::BinOp::Add, - "fsub_algebraic" => mir::BinOp::Sub, - "fmul_algebraic" => mir::BinOp::Mul, - "fdiv_algebraic" => mir::BinOp::Div, - "frem_algebraic" => mir::BinOp::Rem, - _ => bug!(), - }; - let res = this.binary_op(op, &a, &b)?; - // `binary_op` already called `generate_nan` if needed. - // Apply a relative error of 4ULP to simulate non-deterministic precision loss - // due to optimizations. - let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?; - this.write_immediate(*res, dest)?; - } - #[rustfmt::skip] | "fadd_fast" | "fsub_fast" diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index b6b2684dc6da0..e03611e9b509e 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,6 +1,5 @@ #![feature(rustc_private)] #![feature(cfg_match)] -#![feature(cell_update)] #![feature(float_gamma)] #![feature(float_erf)] #![feature(map_try_insert)] diff --git a/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr b/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr index aadb9976609c3..1dcdb4a399680 100644 --- a/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr +++ b/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr @@ -1,7 +1,7 @@ thread $NAME panicked at tests/fail/panic/tls_macro_const_drop_panic.rs:LL:CC: ow -fatal runtime error: thread local panicked on drop +fatal runtime error: thread local panicked on drop, aborting error: abnormal termination: the program aborted execution error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr b/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr index 546ee7e1ed214..7e4907abd9336 100644 --- a/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr +++ b/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr @@ -1,7 +1,7 @@ thread $NAME panicked at tests/fail/panic/tls_macro_drop_panic.rs:LL:CC: ow -fatal runtime error: thread local panicked on drop +fatal runtime error: thread local panicked on drop, aborting error: abnormal termination: the program aborted execution error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/validity/invalid_bool.rs b/src/tools/miri/tests/fail/validity/invalid_bool.rs index 4f11bb2629f5f..bd448af834df7 100644 --- a/src/tools/miri/tests/fail/validity/invalid_bool.rs +++ b/src/tools/miri/tests/fail/validity/invalid_bool.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] fn main() { let _b = unsafe { std::mem::transmute::(2) }; //~ ERROR: expected a boolean } diff --git a/src/tools/miri/tests/fail/validity/invalid_bool_op.rs b/src/tools/miri/tests/fail/validity/invalid_bool_op.rs index fe9bb3bed7f01..0cbe2d76dc6ca 100644 --- a/src/tools/miri/tests/fail/validity/invalid_bool_op.rs +++ b/src/tools/miri/tests/fail/validity/invalid_bool_op.rs @@ -2,6 +2,7 @@ // Make sure we find these even with many checks disabled. //@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +#![allow(unnecessary_transmutes)] fn main() { let b = unsafe { std::mem::transmute::(2) }; let _x = b == std::hint::black_box(true); //~ ERROR: interpreting an invalid 8-bit value as a bool diff --git a/src/tools/miri/tests/fail/validity/invalid_char.rs b/src/tools/miri/tests/fail/validity/invalid_char.rs index 568892e591096..d57c933dac17e 100644 --- a/src/tools/miri/tests/fail/validity/invalid_char.rs +++ b/src/tools/miri/tests/fail/validity/invalid_char.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] fn main() { assert!(std::char::from_u32(-1_i32 as u32).is_none()); let _val = match unsafe { std::mem::transmute::(-1) } { diff --git a/src/tools/miri/tests/fail/validity/invalid_char_op.rs b/src/tools/miri/tests/fail/validity/invalid_char_op.rs index 699248229445f..e3a5f837e1891 100644 --- a/src/tools/miri/tests/fail/validity/invalid_char_op.rs +++ b/src/tools/miri/tests/fail/validity/invalid_char_op.rs @@ -2,6 +2,7 @@ // Make sure we find these even with many checks disabled. //@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +#![allow(unnecessary_transmutes)] fn main() { let c = 0xFFFFFFu32; assert!(std::char::from_u32(c).is_none()); diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index cd60b6bd563d7..575d70579a4e3 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -6,6 +6,7 @@ #![feature(f16)] #![allow(arithmetic_overflow)] #![allow(internal_features)] +#![allow(unnecessary_transmutes)] use std::any::type_name; use std::cmp::min; diff --git a/src/tools/miri/tests/pass/fn_align.rs b/src/tools/miri/tests/pass/fn_align.rs new file mode 100644 index 0000000000000..550bb1cb4d718 --- /dev/null +++ b/src/tools/miri/tests/pass/fn_align.rs @@ -0,0 +1,21 @@ +//@compile-flags: -Zmin-function-alignment=8 +#![feature(fn_align)] + +// When a function uses `repr(align(N))`, the function address should be a multiple of `N`. + +#[repr(align(256))] +fn foo() {} + +#[repr(align(16))] +fn bar() {} + +#[repr(align(4))] +fn baz() {} + +fn main() { + assert!((foo as usize).is_multiple_of(256)); + assert!((bar as usize).is_multiple_of(16)); + + // The maximum of `repr(align(N))` and `-Zmin-function-alignment=N` is used. + assert!((baz as usize).is_multiple_of(8)); +} diff --git a/src/tools/miri/tests/pass/issues/issue-miri-184.rs b/src/tools/miri/tests/pass/issues/issue-miri-184.rs index 39c841403ef0c..964d850298fbf 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-184.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-184.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] pub fn main() { let bytes: [u8; 8] = unsafe { ::std::mem::transmute(0u64) }; let _val: &[u8] = &bytes; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index a629e2acfe998..882b5e3f79524 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -368,7 +368,7 @@ unsafe fn load_m256i_word(data: &[T], word_index: usize) -> __m256i { #[target_feature(enable = "avx512f")] unsafe fn load_m512i_word(data: &[T], word_index: usize) -> __m512i { let byte_offset = word_index * 64 / size_of::(); - let pointer = data.as_ptr().add(byte_offset) as *const i32; + let pointer = data.as_ptr().add(byte_offset) as *const __m512i; _mm512_loadu_si512(black_box(pointer)) } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs index 6f7ab3b3c9fb9..be3f961e10ffa 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +#![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index c14372dd6aec4..fb584103df5d2 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata", @@ -156,9 +156,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cc" -version = "1.2.18" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "shlex", ] @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -775,9 +775,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488" dependencies = [ "jiff-static", "log", @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19" dependencies = [ "proc-macro2", "quote", @@ -840,9 +840,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -2022,9 +2022,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 1e16aace30417..0ff0aad7a2d0c 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -722,7 +722,7 @@ fn last_tok(tt: &TokenTree) -> Token { match *tt { TokenTree::Token(ref t, _) => t.clone(), TokenTree::Delimited(delim_span, _, delim, _) => Token { - kind: TokenKind::CloseDelim(delim), + kind: delim.as_open_token_kind(), span: delim_span.close, }, } @@ -1124,8 +1124,14 @@ fn next_space(tok: &TokenKind) -> SpaceState { TokenKind::PathSep | TokenKind::Pound | TokenKind::Dollar - | TokenKind::OpenDelim(_) - | TokenKind::CloseDelim(_) => SpaceState::Never, + | TokenKind::OpenParen + | TokenKind::CloseParen + | TokenKind::OpenBrace + | TokenKind::CloseBrace + | TokenKind::OpenBracket + | TokenKind::CloseBracket + | TokenKind::OpenInvisible(_) + | TokenKind::CloseInvisible(_) => SpaceState::Never, TokenKind::Literal(..) | TokenKind::Ident(..) | TokenKind::Lifetime(..) => { SpaceState::Ident diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index 0b7b6c4d36147..30b83373c1702 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -1,7 +1,7 @@ use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; -use rustc_ast::token::{Delimiter, TokenKind}; +use rustc_ast::token::TokenKind; use rustc_parse::exp; use rustc_parse::parser::ForceCollect; use rustc_span::symbol::kw; @@ -60,9 +60,7 @@ fn parse_cfg_if_inner<'a>( return Err("Expected an opening brace"); } - while parser.token != TokenKind::CloseDelim(Delimiter::Brace) - && parser.token.kind != TokenKind::Eof - { + while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof { let item = match parser.parse_item(ForceCollect::No) { Ok(Some(item_ptr)) => item_ptr.into_inner(), Ok(None) => continue, diff --git a/src/tools/suggest-tests/src/main.rs b/src/tools/suggest-tests/src/main.rs index ee212af36260a..d84f8e9fa1bab 100644 --- a/src/tools/suggest-tests/src/main.rs +++ b/src/tools/suggest-tests/src/main.rs @@ -6,7 +6,6 @@ use suggest_tests::get_suggestions; fn main() -> ExitCode { let modified_files = get_git_modified_files( &GitConfig { - git_repository: &env("SUGGEST_TESTS_GIT_REPOSITORY"), nightly_branch: &env("SUGGEST_TESTS_NIGHTLY_BRANCH"), git_merge_commit_email: &env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL"), }, diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs new file mode 100644 index 0000000000000..952ebe9e0cffe --- /dev/null +++ b/src/tools/tidy/src/gcc_submodule.rs @@ -0,0 +1,47 @@ +//! Tidy check to ensure that the commit SHA of the `src/gcc` submodule is the same as the +//! required GCC version of the GCC codegen backend. + +use std::path::Path; +use std::process::Command; + +pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { + let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version"); + let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path) + .expect(&format!("Cannot read GCC version from {}", cg_gcc_version_path.display())) + .trim() + .to_string(); + + let git_output = Command::new("git") + .current_dir(root_path) + .arg("submodule") + .arg("status") + // --cached asks for the version that is actually committed in the repository, not the one + // that is currently checked out. + .arg("--cached") + .arg("src/gcc") + .output() + .expect("Cannot determine git SHA of the src/gcc checkout"); + + // This can return e.g. + // -e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc + // e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be) + // +e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be) + let git_output = String::from_utf8_lossy(&git_output.stdout) + .trim() + .split_whitespace() + .next() + .unwrap_or_default() + .to_string(); + + // The SHA can start with + if the submodule is modified or - if it is not checked out. + let gcc_submodule_sha = git_output.trim_start_matches(&['+', '-']); + if gcc_submodule_sha != cg_gcc_version { + *bad = true; + eprintln!( + r#"Commit SHA of the src/gcc submodule (`{gcc_submodule_sha}`) does not match the required GCC version of the GCC codegen backend (`{cg_gcc_version}`). +Make sure to set the src/gcc submodule to commit {cg_gcc_version}. +The GCC codegen backend commit is configured at {}."#, + cg_gcc_version_path.display(), + ); + } +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 66856f5247bfd..ca45f8bb84ba9 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -75,6 +75,7 @@ pub mod features; pub mod fluent_alphabetical; pub mod fluent_period; mod fluent_used; +pub mod gcc_submodule; pub(crate) mod iter_header; pub mod known_bug; pub mod mir_opt_tests; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 4078d462f5584..48122129b0174 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -116,6 +116,7 @@ fn main() { check!(fluent_alphabetical, &compiler_path, bless); check!(fluent_period, &compiler_path); check!(target_policy, &root_path); + check!(gcc_submodule, &root_path, &compiler_path); // Checks that only make sense for the std libs. check!(pal, &library_path); diff --git a/tests/assembly/naked-functions/aarch64-naked-fn-no-bti-prolog.rs b/tests/assembly/naked-functions/aarch64-naked-fn-no-bti-prolog.rs index 46acf7c6501a9..860ecc3cfcd04 100644 --- a/tests/assembly/naked-functions/aarch64-naked-fn-no-bti-prolog.rs +++ b/tests/assembly/naked-functions/aarch64-naked-fn-no-bti-prolog.rs @@ -4,7 +4,7 @@ //@ only-aarch64 #![crate_type = "lib"] -#![feature(naked_functions)] + use std::arch::naked_asm; // The problem at hand: Rust has adopted a fairly strict meaning for "naked functions", diff --git a/tests/assembly/naked-functions/aix.rs b/tests/assembly/naked-functions/aix.rs index 9aa9edc39e78b..57ff0e183bed5 100644 --- a/tests/assembly/naked-functions/aix.rs +++ b/tests/assembly/naked-functions/aix.rs @@ -9,7 +9,7 @@ //@[aix] needs-llvm-components: powerpc #![crate_type = "lib"] -#![feature(no_core, naked_functions, asm_experimental_arch, f128, linkage, fn_align)] +#![feature(no_core, asm_experimental_arch, f128, linkage, fn_align)] #![no_core] // tests that naked functions work for the `powerpc64-ibm-aix` target. diff --git a/tests/assembly/naked-functions/wasm32.rs b/tests/assembly/naked-functions/wasm32.rs index c114cb385be17..71e4d80764a3a 100644 --- a/tests/assembly/naked-functions/wasm32.rs +++ b/tests/assembly/naked-functions/wasm32.rs @@ -9,7 +9,7 @@ //@ [wasm32-wasip1] needs-llvm-components: webassembly #![crate_type = "lib"] -#![feature(no_core, naked_functions, asm_experimental_arch, f128, linkage, fn_align)] +#![feature(no_core, asm_experimental_arch, f128, linkage, fn_align)] #![no_core] extern crate minicore; diff --git a/tests/assembly/naked-functions/x86_64-naked-fn-no-cet-prolog.rs b/tests/assembly/naked-functions/x86_64-naked-fn-no-cet-prolog.rs index df6a2e91c51ea..81ee9b13b4eca 100644 --- a/tests/assembly/naked-functions/x86_64-naked-fn-no-cet-prolog.rs +++ b/tests/assembly/naked-functions/x86_64-naked-fn-no-cet-prolog.rs @@ -4,7 +4,7 @@ //@ only-x86_64 #![crate_type = "lib"] -#![feature(naked_functions)] + use std::arch::naked_asm; // The problem at hand: Rust has adopted a fairly strict meaning for "naked functions", diff --git a/tests/assembly/simd-bitmask.rs b/tests/assembly/simd-bitmask.rs index e412246108700..d3e20f6ae1a8e 100644 --- a/tests/assembly/simd-bitmask.rs +++ b/tests/assembly/simd-bitmask.rs @@ -65,8 +65,9 @@ pub unsafe extern "C" fn bitmask_m8x16(mask: m8x16) -> u16 { simd_bitmask(mask) } -// CHECK-LABEL: bitmask_m8x64 +// x86-avx512-LABEL: bitmask_m8x64 #[no_mangle] +#[cfg(x86_avx512)] pub unsafe extern "C" fn bitmask_m8x64(mask: m8x64) -> u64 { // The simd_bitmask intrinsic already uses the most significant bit, so no shift is necessary. // Note that x86 has no byte shift, llvm uses a word shift to move the least significant bit @@ -128,8 +129,10 @@ pub unsafe extern "C" fn bitmask_m64x2(mask: m64x2) -> u8 { simd_bitmask(mask) } -// CHECK-LABEL: bitmask_m64x4 +// x86-avx2-LABEL: bitmask_m64x4 +// x86-avx512-LABEL: bitmask_m64x4 #[no_mangle] +#[cfg(any(x86_avx2, x86_avx512))] pub unsafe extern "C" fn bitmask_m64x4(mask: m64x4) -> u8 { // The simd_bitmask intrinsic already uses the most significant bit, so no shift is necessary. // diff --git a/tests/assembly/simd-intrinsic-select.rs b/tests/assembly/simd-intrinsic-select.rs index 4f8d6b825b611..e7c7b0db0d5e3 100644 --- a/tests/assembly/simd-intrinsic-select.rs +++ b/tests/assembly/simd-intrinsic-select.rs @@ -99,8 +99,10 @@ pub unsafe extern "C" fn select_f64x2(mask: m64x2, a: f64x2, b: f64x2) -> f64x2 simd_select(mask, a, b) } -// CHECK-LABEL: select_f64x4 +// x86-avx2-LABEL: select_f64x4 +// x86-avx512-LABEL: select_f64x4 #[no_mangle] +#[cfg(any(x86_avx2, x86_avx512))] pub unsafe extern "C" fn select_f64x4(mask: m64x4, a: f64x4, b: f64x4) -> f64x4 { // The parameter is a 256 bit vector which in the C abi is only valid for avx targets. // @@ -113,8 +115,9 @@ pub unsafe extern "C" fn select_f64x4(mask: m64x4, a: f64x4, b: f64x4) -> f64x4 simd_select(mask, a, b) } -// CHECK-LABEL: select_f64x8 +// x86-avx512-LABEL: select_f64x8 #[no_mangle] +#[cfg(x86_avx512)] pub unsafe extern "C" fn select_f64x8(mask: m64x8, a: f64x8, b: f64x8) -> f64x8 { // The parameter is a 256 bit vector which in the C abi is only valid for avx512 targets. // diff --git a/tests/assembly/x86-return-float.rs b/tests/assembly/x86-return-float.rs index 2c39c830684ec..165c11d228011 100644 --- a/tests/assembly/x86-return-float.rs +++ b/tests/assembly/x86-return-float.rs @@ -35,6 +35,7 @@ use minicore::*; pub fn return_f32(x: f32) -> f32 { // CHECK: movss {{.*}}(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } @@ -44,6 +45,7 @@ pub fn return_f32(x: f32) -> f32 { pub fn return_f64(x: f64) -> f64 { // CHECK: movsd {{.*}}(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } @@ -313,9 +315,13 @@ pub unsafe fn call_other_f64(x: &mut (usize, f64)) { #[no_mangle] pub fn return_f16(x: f16) -> f16 { // CHECK: pushl %ebp + // linux-NEXT: .cfi_def_cfa_offset + // linux-NEXT: .cfi_offset // CHECK-NEXT: movl %esp, %ebp + // linux-NEXT: .cfi_def_cfa_register // CHECK-NEXT: pinsrw $0, 8(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } @@ -324,10 +330,14 @@ pub fn return_f16(x: f16) -> f16 { #[no_mangle] pub fn return_f128(x: f128) -> f128 { // CHECK: pushl %ebp + // linux-NEXT: .cfi_def_cfa_offset + // linux-NEXT: .cfi_offset // CHECK-NEXT: movl %esp, %ebp + // linux-NEXT: .cfi_def_cfa_register // linux-NEXT: movaps 8(%ebp), %xmm0 // win-NEXT: movups 8(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 03aa847650823..941c4abed4679 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -22,7 +22,6 @@ negative_impls, rustc_attrs, decl_macro, - naked_functions, f16, f128, asm_experimental_arch, diff --git a/tests/codegen/align-struct.rs b/tests/codegen/align-struct.rs index cc65b08a9223c..402a184d4c07e 100644 --- a/tests/codegen/align-struct.rs +++ b/tests/codegen/align-struct.rs @@ -1,5 +1,7 @@ //@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 -// +// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480) +//@ ignore-i686-pc-windows-msvc +//@ ignore-i686-pc-windows-gnu #![crate_type = "lib"] diff --git a/tests/codegen/autodiff/identical_fnc.rs b/tests/codegen/autodiff/identical_fnc.rs new file mode 100644 index 0000000000000..1c3277f52b488 --- /dev/null +++ b/tests/codegen/autodiff/identical_fnc.rs @@ -0,0 +1,45 @@ +//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme +// +// Each autodiff invocation creates a new placeholder function, which we will replace on llvm-ir +// level. If a user tries to differentiate two identical functions within the same compilation unit, +// then LLVM might merge them in release mode before AD. In that case we can't rewrite one of the +// merged placeholder function anymore, and compilation would fail. We prevent this by disabling +// LLVM's merge_function pass before AD. Here we implicetely test that our solution keeps working. +// We also explicetly test that we keep running merge_function after AD, by checking for two +// identical function calls in the LLVM-IR, while having two different calls in the Rust code. +#![feature(autodiff)] + +use std::autodiff::autodiff; + +#[autodiff(d_square, Reverse, Duplicated, Active)] +fn square(x: &f64) -> f64 { + x * x +} + +#[autodiff(d_square2, Reverse, Duplicated, Active)] +fn square2(x: &f64) -> f64 { + x * x +} + +// CHECK:; identical_fnc::main +// CHECK-NEXT:; Function Attrs: +// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17hf4dbc69c8d2f9130E() +// CHECK-NEXT:start: +// CHECK-NOT:br +// CHECK-NOT:ret +// CHECK:; call identical_fnc::d_square +// CHECK-NEXT: call fastcc void @_ZN13identical_fnc8d_square17h4c364207a2f8e06dE(double %x.val, ptr noalias noundef nonnull align 8 dereferenceable(8) %dx1) +// CHECK-NEXT:; call identical_fnc::d_square +// CHECK-NEXT: call fastcc void @_ZN13identical_fnc8d_square17h4c364207a2f8e06dE(double %x.val, ptr noalias noundef nonnull align 8 dereferenceable(8) %dx2) + +fn main() { + let x = std::hint::black_box(3.0); + let mut dx1 = std::hint::black_box(1.0); + let mut dx2 = std::hint::black_box(1.0); + let _ = d_square(&x, &mut dx1, 1.0); + let _ = d_square2(&x, &mut dx2, 1.0); + assert_eq!(dx1, 6.0); + assert_eq!(dx2, 6.0); +} diff --git a/tests/codegen/cffi/c-variadic-naked.rs b/tests/codegen/cffi/c-variadic-naked.rs index 05d48e52dc006..5843628b6330a 100644 --- a/tests/codegen/cffi/c-variadic-naked.rs +++ b/tests/codegen/cffi/c-variadic-naked.rs @@ -5,7 +5,6 @@ #![crate_type = "lib"] #![feature(c_variadic)] -#![feature(naked_functions)] #![no_std] #[unsafe(naked)] diff --git a/tests/codegen/const-vector.rs b/tests/codegen/const-vector.rs index 8343594e5d23c..1d4edc39b1c88 100644 --- a/tests/codegen/const-vector.rs +++ b/tests/codegen/const-vector.rs @@ -8,6 +8,7 @@ #![feature(repr_simd)] #![feature(rustc_attrs)] #![feature(simd_ffi)] +#![feature(arm_target_feature)] #![allow(non_camel_case_types)] // Setting up structs that can be used as const vectors @@ -28,33 +29,12 @@ pub struct Simd([T; N]); extern "unadjusted" { fn test_i8x2(a: i8x2); -} - -extern "unadjusted" { fn test_i8x2_two_args(a: i8x2, b: i8x2); -} - -extern "unadjusted" { fn test_i8x2_mixed_args(a: i8x2, c: i32, b: i8x2); -} - -extern "unadjusted" { fn test_i8x2_arr(a: i8x2); -} - -extern "unadjusted" { fn test_f32x2(a: f32x2); -} - -extern "unadjusted" { fn test_f32x2_arr(a: f32x2); -} - -extern "unadjusted" { fn test_simd(a: Simd); -} - -extern "unadjusted" { fn test_simd_unaligned(a: Simd); } @@ -62,6 +42,9 @@ extern "unadjusted" { // if the size is not a power of 2 // CHECK: %"Simd" = type { [3 x i32] } +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] pub fn do_call() { unsafe { // CHECK: call void @test_i8x2(<2 x i8> diff --git a/tests/codegen/issues/issue-56927.rs b/tests/codegen/issues/issue-56927.rs index a40718689b3e0..415ef073e03ac 100644 --- a/tests/codegen/issues/issue-56927.rs +++ b/tests/codegen/issues/issue-56927.rs @@ -1,4 +1,7 @@ //@ compile-flags: -C no-prepopulate-passes +// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480) +//@ ignore-i686-pc-windows-msvc +//@ ignore-i686-pc-windows-gnu #![crate_type = "rlib"] diff --git a/tests/codegen/naked-asan.rs b/tests/codegen/naked-asan.rs index 52b3e709cd35c..223c41b15bb35 100644 --- a/tests/codegen/naked-asan.rs +++ b/tests/codegen/naked-asan.rs @@ -6,7 +6,7 @@ #![crate_type = "lib"] #![no_std] -#![feature(abi_x86_interrupt, naked_functions)] +#![feature(abi_x86_interrupt)] pub fn caller() { page_fault_handler(1, 2); diff --git a/tests/codegen/naked-fn/aligned.rs b/tests/codegen/naked-fn/aligned.rs index 6183461fedaec..47ef779f1b217 100644 --- a/tests/codegen/naked-fn/aligned.rs +++ b/tests/codegen/naked-fn/aligned.rs @@ -3,7 +3,7 @@ //@ ignore-arm no "ret" mnemonic #![crate_type = "lib"] -#![feature(naked_functions, fn_align)] +#![feature(fn_align)] use std::arch::naked_asm; // CHECK: .balign 16 diff --git a/tests/codegen/naked-fn/generics.rs b/tests/codegen/naked-fn/generics.rs index 4427586777168..865be00d91ee4 100644 --- a/tests/codegen/naked-fn/generics.rs +++ b/tests/codegen/naked-fn/generics.rs @@ -2,7 +2,6 @@ //@ only-x86_64 #![crate_type = "lib"] -#![feature(naked_functions, asm_const)] use std::arch::naked_asm; diff --git a/tests/codegen/naked-fn/instruction-set.rs b/tests/codegen/naked-fn/instruction-set.rs index 2ccd47d645858..67560c5aba758 100644 --- a/tests/codegen/naked-fn/instruction-set.rs +++ b/tests/codegen/naked-fn/instruction-set.rs @@ -6,7 +6,7 @@ //@ [thumb-mode] needs-llvm-components: arm #![crate_type = "lib"] -#![feature(no_core, lang_items, rustc_attrs, naked_functions)] +#![feature(no_core, lang_items, rustc_attrs)] #![no_core] extern crate minicore; diff --git a/tests/codegen/naked-fn/min-function-alignment.rs b/tests/codegen/naked-fn/min-function-alignment.rs index 4a9142288248b..1d778be8c90dc 100644 --- a/tests/codegen/naked-fn/min-function-alignment.rs +++ b/tests/codegen/naked-fn/min-function-alignment.rs @@ -2,7 +2,7 @@ //@ needs-asm-support //@ ignore-arm no "ret" mnemonic -#![feature(naked_functions, fn_align)] +#![feature(fn_align)] #![crate_type = "lib"] // functions without explicit alignment use the global minimum diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 1bcdd6de373e5..344af6eb42fa8 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -13,7 +13,7 @@ //@[thumb] needs-llvm-components: arm #![crate_type = "lib"] -#![feature(no_core, lang_items, rustc_attrs, naked_functions)] +#![feature(no_core, lang_items, rustc_attrs)] #![no_core] extern crate minicore; diff --git a/tests/codegen/regparm-inreg.rs b/tests/codegen/regparm-inreg.rs index 8dae3a83e4ed1..15702804dfd04 100644 --- a/tests/codegen/regparm-inreg.rs +++ b/tests/codegen/regparm-inreg.rs @@ -3,7 +3,7 @@ // x86 only. //@ add-core-stubs -//@ compile-flags: --target i686-unknown-linux-gnu -Cno-prepopulate-passes -Copt-level=3 +//@ compile-flags: --target i686-unknown-linux-gnu -Cno-prepopulate-passes -Copt-level=3 -Ctarget-feature=+avx //@ needs-llvm-components: x86 //@ revisions:regparm0 regparm1 regparm2 regparm3 diff --git a/tests/codegen/repr/transparent.rs b/tests/codegen/repr/transparent.rs index e7e4c40a09917..5475bfb6b65ef 100644 --- a/tests/codegen/repr/transparent.rs +++ b/tests/codegen/repr/transparent.rs @@ -9,7 +9,7 @@ // For LoongArch: see codegen/loongarch-abi #![crate_type = "lib"] -#![feature(repr_simd, transparent_unions)] +#![feature(repr_simd, transparent_unions, arm_target_feature)] use std::marker::PhantomData; @@ -139,6 +139,9 @@ pub struct Vector(f32x4); // CHECK: define{{.*}}<4 x float> @test_Vector(<4 x float> %_1) #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] pub extern "C" fn test_Vector(_: Vector) -> Vector { loop {} } diff --git a/tests/codegen/simd/extract-insert-dyn.rs b/tests/codegen/simd/extract-insert-dyn.rs index 584e2c7887adc..2c64f5d3c0939 100644 --- a/tests/codegen/simd/extract-insert-dyn.rs +++ b/tests/codegen/simd/extract-insert-dyn.rs @@ -1,6 +1,6 @@ //@compile-flags: -C opt-level=3 -C no-prepopulate-passes -#![feature(core_intrinsics, repr_simd)] +#![feature(core_intrinsics, repr_simd, arm_target_feature)] #![no_std] #![crate_type = "lib"] #![allow(non_camel_case_types)] @@ -21,6 +21,9 @@ pub struct i8x16([i8; 16]); // CHECK-LABEL: dyn_simd_extract // CHECK: extractelement <16 x i8> %x, i32 %idx #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn dyn_simd_extract(x: i8x16, idx: u32) -> i8 { simd_extract_dyn(x, idx) } @@ -28,6 +31,9 @@ unsafe extern "C" fn dyn_simd_extract(x: i8x16, idx: u32) -> i8 { // CHECK-LABEL: literal_dyn_simd_extract // CHECK: extractelement <16 x i8> %x, i32 7 #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn literal_dyn_simd_extract(x: i8x16) -> i8 { simd_extract_dyn(x, 7) } @@ -35,6 +41,9 @@ unsafe extern "C" fn literal_dyn_simd_extract(x: i8x16) -> i8 { // CHECK-LABEL: const_dyn_simd_extract // CHECK: extractelement <16 x i8> %x, i32 7 #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn const_dyn_simd_extract(x: i8x16) -> i8 { simd_extract_dyn(x, const { 3 + 4 }) } @@ -42,6 +51,9 @@ unsafe extern "C" fn const_dyn_simd_extract(x: i8x16) -> i8 { // CHECK-LABEL: const_simd_extract // CHECK: extractelement <16 x i8> %x, i32 7 #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn const_simd_extract(x: i8x16) -> i8 { simd_extract(x, const { 3 + 4 }) } @@ -49,6 +61,9 @@ unsafe extern "C" fn const_simd_extract(x: i8x16) -> i8 { // CHECK-LABEL: dyn_simd_insert // CHECK: insertelement <16 x i8> %x, i8 %e, i32 %idx #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn dyn_simd_insert(x: i8x16, e: i8, idx: u32) -> i8x16 { simd_insert_dyn(x, idx, e) } @@ -56,6 +71,9 @@ unsafe extern "C" fn dyn_simd_insert(x: i8x16, e: i8, idx: u32) -> i8x16 { // CHECK-LABEL: literal_dyn_simd_insert // CHECK: insertelement <16 x i8> %x, i8 %e, i32 7 #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn literal_dyn_simd_insert(x: i8x16, e: i8) -> i8x16 { simd_insert_dyn(x, 7, e) } @@ -63,6 +81,9 @@ unsafe extern "C" fn literal_dyn_simd_insert(x: i8x16, e: i8) -> i8x16 { // CHECK-LABEL: const_dyn_simd_insert // CHECK: insertelement <16 x i8> %x, i8 %e, i32 7 #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn const_dyn_simd_insert(x: i8x16, e: i8) -> i8x16 { simd_insert_dyn(x, const { 3 + 4 }, e) } @@ -70,6 +91,9 @@ unsafe extern "C" fn const_dyn_simd_insert(x: i8x16, e: i8) -> i8x16 { // CHECK-LABEL: const_simd_insert // CHECK: insertelement <16 x i8> %x, i8 %e, i32 7 #[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] unsafe extern "C" fn const_simd_insert(x: i8x16, e: i8) -> i8x16 { simd_insert(x, const { 3 + 4 }, e) } diff --git a/tests/crashes/133868.rs b/tests/crashes/133868.rs deleted file mode 100644 index dc25cb9df288e..0000000000000 --- a/tests/crashes/133868.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #133868 - -trait Foo { - type Assoc; -} - -trait Bar { - fn method() -> impl Sized; -} -impl Bar for T where ::Assoc: Sized -{ - fn method() {} -} diff --git a/tests/mir-opt/building/logical_or_in_conditional.rs b/tests/mir-opt/building/logical_or_in_conditional.rs index e6872e6f2ec8b..249ccf728044c 100644 --- a/tests/mir-opt/building/logical_or_in_conditional.rs +++ b/tests/mir-opt/building/logical_or_in_conditional.rs @@ -1,6 +1,6 @@ // skip-filecheck //@ compile-flags: -Z validate-mir -#![feature(let_chains)] +//@ edition: 2024 struct Droppy(u8); impl Drop for Droppy { fn drop(&mut self) { diff --git a/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir b/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir index 72bd4605b2641..327b3b618a03a 100644 --- a/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir +++ b/tests/mir-opt/building/logical_or_in_conditional.test_complex.built.after.mir @@ -19,7 +19,7 @@ fn test_complex() -> () { bb0: { StorageLive(_1); StorageLive(_2); - _2 = E::f() -> [return: bb1, unwind: bb34]; + _2 = E::f() -> [return: bb1, unwind: bb35]; } bb1: { @@ -42,7 +42,7 @@ fn test_complex() -> () { bb5: { StorageLive(_4); - _4 = always_true() -> [return: bb6, unwind: bb34]; + _4 = always_true() -> [return: bb6, unwind: bb35]; } bb6: { @@ -64,7 +64,7 @@ fn test_complex() -> () { } bb9: { - drop(_7) -> [return: bb11, unwind: bb34]; + drop(_7) -> [return: bb11, unwind: bb35]; } bb10: { @@ -78,7 +78,7 @@ fn test_complex() -> () { } bb12: { - drop(_7) -> [return: bb13, unwind: bb34]; + drop(_7) -> [return: bb13, unwind: bb35]; } bb13: { @@ -98,7 +98,7 @@ fn test_complex() -> () { } bb15: { - drop(_10) -> [return: bb17, unwind: bb34]; + drop(_10) -> [return: bb17, unwind: bb35]; } bb16: { @@ -113,11 +113,12 @@ fn test_complex() -> () { bb18: { _1 = const (); + StorageDead(_2); goto -> bb22; } bb19: { - drop(_10) -> [return: bb20, unwind: bb34]; + drop(_10) -> [return: bb20, unwind: bb35]; } bb20: { @@ -127,6 +128,7 @@ fn test_complex() -> () { } bb21: { + StorageDead(_2); _1 = const (); goto -> bb22; } @@ -135,10 +137,9 @@ fn test_complex() -> () { StorageDead(_8); StorageDead(_5); StorageDead(_4); - StorageDead(_2); StorageDead(_1); StorageLive(_11); - _11 = always_true() -> [return: bb23, unwind: bb34]; + _11 = always_true() -> [return: bb23, unwind: bb35]; } bb23: { @@ -146,7 +147,7 @@ fn test_complex() -> () { } bb24: { - goto -> bb32; + goto -> bb33; } bb25: { @@ -155,7 +156,7 @@ fn test_complex() -> () { bb26: { StorageLive(_12); - _12 = E::f() -> [return: bb27, unwind: bb34]; + _12 = E::f() -> [return: bb27, unwind: bb35]; } bb27: { @@ -178,21 +179,26 @@ fn test_complex() -> () { bb31: { _0 = const (); - goto -> bb33; + StorageDead(_12); + goto -> bb34; } bb32: { - _0 = const (); + StorageDead(_12); goto -> bb33; } bb33: { + _0 = const (); + goto -> bb34; + } + + bb34: { StorageDead(_11); - StorageDead(_12); return; } - bb34 (cleanup): { + bb35 (cleanup): { resume; } } diff --git a/tests/pretty/autodiff_forward.pp b/tests/pretty/autodiff/autodiff_forward.pp similarity index 100% rename from tests/pretty/autodiff_forward.pp rename to tests/pretty/autodiff/autodiff_forward.pp diff --git a/tests/pretty/autodiff_forward.rs b/tests/pretty/autodiff/autodiff_forward.rs similarity index 100% rename from tests/pretty/autodiff_forward.rs rename to tests/pretty/autodiff/autodiff_forward.rs diff --git a/tests/pretty/autodiff_reverse.pp b/tests/pretty/autodiff/autodiff_reverse.pp similarity index 100% rename from tests/pretty/autodiff_reverse.pp rename to tests/pretty/autodiff/autodiff_reverse.pp diff --git a/tests/pretty/autodiff_reverse.rs b/tests/pretty/autodiff/autodiff_reverse.rs similarity index 100% rename from tests/pretty/autodiff_reverse.rs rename to tests/pretty/autodiff/autodiff_reverse.rs diff --git a/tests/pretty/autodiff/inherent_impl.pp b/tests/pretty/autodiff/inherent_impl.pp new file mode 100644 index 0000000000000..97ac766b6b99e --- /dev/null +++ b/tests/pretty/autodiff/inherent_impl.pp @@ -0,0 +1,41 @@ +#![feature(prelude_import)] +#![no_std] +//@ needs-enzyme + +#![feature(autodiff)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:inherent_impl.pp + +use std::autodiff::autodiff; + +struct Foo { + a: f64, +} + +trait MyTrait { + fn f(&self, x: f64) + -> f64; + fn df(&self, x: f64, seed: f64) + -> (f64, f64); +} + +impl MyTrait for Foo { + #[rustc_autodiff] + #[inline(never)] + fn f(&self, x: f64) -> f64 { + self.a * 0.25 * (x * x - 1.0 - 2.0 * x.ln()) + } + #[rustc_autodiff(Reverse, 1, Const, Active, Active)] + #[inline(never)] + fn df(&self, x: f64, dret: f64) -> (f64, f64) { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(self.f(x)); + ::core::hint::black_box((dret,)); + ::core::hint::black_box((self.f(x), f64::default())) + } +} diff --git a/tests/pretty/autodiff/inherent_impl.rs b/tests/pretty/autodiff/inherent_impl.rs new file mode 100644 index 0000000000000..59de93f7e0ffd --- /dev/null +++ b/tests/pretty/autodiff/inherent_impl.rs @@ -0,0 +1,24 @@ +//@ needs-enzyme + +#![feature(autodiff)] +//@ pretty-mode:expanded +//@ pretty-compare-only +//@ pp-exact:inherent_impl.pp + +use std::autodiff::autodiff; + +struct Foo { + a: f64, +} + +trait MyTrait { + fn f(&self, x: f64) -> f64; + fn df(&self, x: f64, seed: f64) -> (f64, f64); +} + +impl MyTrait for Foo { + #[autodiff(df, Reverse, Const, Active, Active)] + fn f(&self, x: f64) -> f64 { + self.a * 0.25 * (x * x - 1.0 - 2.0 * x.ln()) + } +} diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs index ab9aa445402b0..3a48af8a3665a 100644 --- a/tests/run-make/c-link-to-rust-dylib/rmake.rs +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -23,7 +23,11 @@ fn main() { if path.is_file() && path.extension().is_some_and(|ext| ext == expected_extension) && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| { - name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib") + if cfg!(target_os = "aix") { + name.ends_with(".a") + } else { + name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib") + } }) { rfs::remove_file(path); diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs index c7be829c215e1..817001c514b52 100644 --- a/tests/run-make/doctests-runtool/rmake.rs +++ b/tests/run-make/doctests-runtool/rmake.rs @@ -1,4 +1,4 @@ -// Tests behavior of rustdoc `--runtool`. +// Tests behavior of rustdoc `--test-runtool`. use std::path::PathBuf; @@ -11,7 +11,7 @@ fn mkdir(name: &str) -> PathBuf { dir } -// Behavior with --runtool with relative paths and --test-run-directory. +// Behavior with --test-runtool with relative paths and --test-run-directory. fn main() { let run_dir_name = "rundir"; let run_dir = mkdir(run_dir_name); @@ -27,7 +27,7 @@ fn main() { .arg("--test") .arg("--test-run-directory") .arg(run_dir_name) - .arg("--runtool") + .arg("--test-runtool") .arg(&run_tool_binary) .extern_("t", "libt.rlib") .run(); diff --git a/tests/run-make/metadata-dep-info/dash-separated_something-extra.expected.d b/tests/run-make/metadata-dep-info/dash-separated_something-extra.expected.d index 497d76b4ea12e..43e8f9185a301 100644 --- a/tests/run-make/metadata-dep-info/dash-separated_something-extra.expected.d +++ b/tests/run-make/metadata-dep-info/dash-separated_something-extra.expected.d @@ -1,5 +1,5 @@ -libdash_separated_something-extra.rmeta: dash-separated.rs - dash-separated_something-extra.d: dash-separated.rs +libdash_separated_something-extra.rmeta: dash-separated.rs + dash-separated.rs: diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index ae75519525363..ce787f83ade6d 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -1,4 +1,4 @@ -#![feature(naked_functions, linkage)] +#![feature(linkage)] #![crate_type = "dylib"] use std::arch::naked_asm; diff --git a/tests/run-make/rustc-help/help-v.diff b/tests/run-make/rustc-help/help-v.diff index 30703f6424e7e..60a9dfbe201d9 100644 --- a/tests/run-make/rustc-help/help-v.diff +++ b/tests/run-make/rustc-help/help-v.diff @@ -1,22 +1,23 @@ -@@ -53,10 +53,27 @@ +@@ -65,10 +65,28 @@ Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output -+ --extern NAME[=PATH] ++ --extern [=] + Specify where an external rust library is located -+ --sysroot PATH Override the system root -+ --error-format human|json|short ++ --sysroot ++ Override the system root ++ --error-format + How errors and other messages are produced -+ --json CONFIG Configure the JSON output of the compiler -+ --color auto|always|never ++ --json Configure the JSON output of the compiler ++ --color + Configure coloring of output: -+ auto = colorize, if output goes to a tty (default); -+ always = always colorize output; -+ never = never colorize output -+ --diagnostic-width WIDTH ++ * auto = colorize, if output goes to a tty (default); ++ * always = always colorize output; ++ * never = never colorize output ++ --diagnostic-width + Inform rustc of the width of the output so that + diagnostics can be truncated to fit -+ --remap-path-prefix FROM=TO ++ --remap-path-prefix = + Remap source names in all output (compiler messages + and output files) + @path Read newline separated options from `path` diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index 13af6e21060b5..3fc297fb08e7d 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -2,72 +2,85 @@ Usage: rustc [OPTIONS] INPUT Options: -h, --help Display this message - --cfg SPEC Configure the compilation environment. - SPEC supports the syntax `NAME[="VALUE"]`. - --check-cfg SPEC + --cfg Configure the compilation environment. + SPEC supports the syntax `[=""]`. + --check-cfg Provide list of expected cfgs for checking - -L [KIND=]PATH Add a directory to the library search path. The - optional KIND can be one of dependency, crate, native, - framework, or all (the default). - -l [KIND[:MODIFIERS]=]NAME[:RENAME] + -L [=] Add a directory to the library search path. The + optional KIND can be one of + (default: + all). + -l [[:]=][:] Link the generated crate(s) to the specified native library NAME. The optional KIND can be one of - static, framework, or dylib (the default). + (default: dylib). Optional comma separated MODIFIERS - (bundle|verbatim|whole-archive|as-needed) + may be specified each with a prefix of either '+' to enable or '-' to disable. - --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + --crate-type Comma separated list of types of crates for the compiler to emit - --crate-name NAME + --crate-name Specify the name of the crate being built - --edition 2015|2018|2021|2024 + --edition <2015|2018|2021|2024|future> Specify which edition of the compiler to use when compiling code. The default is 2015 and the latest stable edition is 2024. - --emit [asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir] + --emit [=] Comma separated list of types of output for the - compiler to emit - --print INFO[=FILE] + compiler to emit. + Each TYPE has the default FILE name: + * asm - CRATE_NAME.s + * llvm-bc - CRATE_NAME.bc + * dep-info - CRATE_NAME.d + * link - (platform and crate-type dependent) + * llvm-ir - CRATE_NAME.ll + * metadata - libCRATE_NAME.rmeta + * mir - CRATE_NAME.mir + * obj - CRATE_NAME.o + * thin-link-bitcode - CRATE_NAME.indexing.o + --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - (all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models). + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 - -o FILENAME Write output to - --out-dir DIR Write output to compiler-chosen filename in - --explain OPT Provide a detailed explanation of an error message + -o Write output to FILENAME + --out-dir Write output to compiler-chosen filename in DIR + --explain Provide a detailed explanation of an error message --test Build a test harness - --target TARGET Target triple for which the code is compiled - -A, --allow LINT Set lint allowed - -W, --warn LINT Set lint warnings - --force-warn LINT + --target + Target triple for which the code is compiled + -A, --allow Set lint allowed + -W, --warn Set lint warnings + --force-warn Set lint force-warn - -D, --deny LINT Set lint denied - -F, --forbid LINT Set lint forbidden - --cap-lints LEVEL + -D, --deny Set lint denied + -F, --forbid Set lint forbidden + --cap-lints Set the most restrictive lint level. More restrictive lints are capped at this level - -C, --codegen OPT[=VALUE] + -C, --codegen [=] Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output - --extern NAME[=PATH] + --extern [=] Specify where an external rust library is located - --sysroot PATH Override the system root - --error-format human|json|short + --sysroot + Override the system root + --error-format How errors and other messages are produced - --json CONFIG Configure the JSON output of the compiler - --color auto|always|never + --json Configure the JSON output of the compiler + --color Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output - --diagnostic-width WIDTH + * auto = colorize, if output goes to a tty (default); + * always = always colorize output; + * never = never colorize output + --diagnostic-width Inform rustc of the width of the output so that diagnostics can be truncated to fit - --remap-path-prefix FROM=TO + --remap-path-prefix = Remap source names in all output (compiler messages and output files) @path Read newline separated options from `path` diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index 62757d989eb3b..caffe28f49899 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -2,54 +2,66 @@ Usage: rustc [OPTIONS] INPUT Options: -h, --help Display this message - --cfg SPEC Configure the compilation environment. - SPEC supports the syntax `NAME[="VALUE"]`. - --check-cfg SPEC + --cfg Configure the compilation environment. + SPEC supports the syntax `[=""]`. + --check-cfg Provide list of expected cfgs for checking - -L [KIND=]PATH Add a directory to the library search path. The - optional KIND can be one of dependency, crate, native, - framework, or all (the default). - -l [KIND[:MODIFIERS]=]NAME[:RENAME] + -L [=] Add a directory to the library search path. The + optional KIND can be one of + (default: + all). + -l [[:]=][:] Link the generated crate(s) to the specified native library NAME. The optional KIND can be one of - static, framework, or dylib (the default). + (default: dylib). Optional comma separated MODIFIERS - (bundle|verbatim|whole-archive|as-needed) + may be specified each with a prefix of either '+' to enable or '-' to disable. - --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + --crate-type Comma separated list of types of crates for the compiler to emit - --crate-name NAME + --crate-name Specify the name of the crate being built - --edition 2015|2018|2021|2024 + --edition <2015|2018|2021|2024|future> Specify which edition of the compiler to use when compiling code. The default is 2015 and the latest stable edition is 2024. - --emit [asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir] + --emit [=] Comma separated list of types of output for the - compiler to emit - --print INFO[=FILE] + compiler to emit. + Each TYPE has the default FILE name: + * asm - CRATE_NAME.s + * llvm-bc - CRATE_NAME.bc + * dep-info - CRATE_NAME.d + * link - (platform and crate-type dependent) + * llvm-ir - CRATE_NAME.ll + * metadata - libCRATE_NAME.rmeta + * mir - CRATE_NAME.mir + * obj - CRATE_NAME.o + * thin-link-bitcode - CRATE_NAME.indexing.o + --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - (all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models). + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 - -o FILENAME Write output to - --out-dir DIR Write output to compiler-chosen filename in - --explain OPT Provide a detailed explanation of an error message + -o Write output to FILENAME + --out-dir Write output to compiler-chosen filename in DIR + --explain Provide a detailed explanation of an error message --test Build a test harness - --target TARGET Target triple for which the code is compiled - -A, --allow LINT Set lint allowed - -W, --warn LINT Set lint warnings - --force-warn LINT + --target + Target triple for which the code is compiled + -A, --allow Set lint allowed + -W, --warn Set lint warnings + --force-warn Set lint force-warn - -D, --deny LINT Set lint denied - -F, --forbid LINT Set lint forbidden - --cap-lints LEVEL + -D, --deny Set lint denied + -F, --forbid Set lint forbidden + --cap-lints Set the most restrictive lint level. More restrictive lints are capped at this level - -C, --codegen OPT[=VALUE] + -C, --codegen [=] Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 01f470f6e162b..78ca8c863ebab 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -11,7 +11,7 @@ Options: -o, --out-dir PATH which directory to place the output --crate-name NAME specify the name of this crate - --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + --crate-type Comma separated list of types of crates for the compiler to emit -L, --library-path DIR @@ -138,12 +138,9 @@ Options: --show-coverage calculate percentage of public items with documentation - --enable-per-target-ignores - parse ignore-foo for ignoring doctests on a per-target - basis - --runtool The tool to run tests with when building for a different target than host + --test-runtool The tool to run tests with when building for a different target than host - --runtool-arg One (of possibly many) arguments to pass to the runtool + --test-runtool-arg One argument (of possibly many) to pass to the runtool --test-builder PATH The rustc-like binary to use as the test builder diff --git a/tests/run-make/simd-ffi/rmake.rs b/tests/run-make/simd-ffi/rmake.rs index ef71dfa4c3021..c6315073fa8cf 100644 --- a/tests/run-make/simd-ffi/rmake.rs +++ b/tests/run-make/simd-ffi/rmake.rs @@ -52,11 +52,20 @@ fn main() { // enabled by-default for i686 and ARM; these features will be invalid // on some platforms, but LLVM just prints a warning so that's fine for // now. + let target_feature = if target.starts_with("i686") || target.starts_with("x86") { + "+sse2" + } else if target.starts_with("arm") || target.starts_with("aarch64") { + "-soft-float,+neon" + } else if target.starts_with("mips") { + "+msa,+fp64" + } else { + panic!("missing target_feature case for {target}"); + }; rustc() .target(&target) .emit("llvm-ir,asm") .input("simd.rs") - .arg("-Ctarget-feature=-soft-float,+neon,+sse") + .arg(format!("-Ctarget-feature={target_feature}")) .arg(&format!("-Cextra-filename=-{target}")) .run(); } diff --git a/tests/rustdoc-gui/docblock-table-overflow.goml b/tests/rustdoc-gui/docblock-table-overflow.goml index 18e5b4d7f3590..e603c3a4d22e0 100644 --- a/tests/rustdoc-gui/docblock-table-overflow.goml +++ b/tests/rustdoc-gui/docblock-table-overflow.goml @@ -11,7 +11,7 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"}) // Checking it works on other doc blocks as well... // Logically, the ".docblock" and the "

" should have the same scroll width (if we exclude the margin). -assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816}) +assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 835}) assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835}) // However, since there is overflow in the , its scroll width is bigger. assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"}) diff --git a/tests/rustdoc-gui/impl-doc-indent.goml b/tests/rustdoc-gui/impl-doc-indent.goml new file mode 100644 index 0000000000000..d647fec6d732d --- /dev/null +++ b/tests/rustdoc-gui/impl-doc-indent.goml @@ -0,0 +1,16 @@ +// Checks the impl block docs have the correct indent. +go-to: "file://" + |DOC_PATH| + "/test_docs/impls_indent/struct.Context.html" + +// First we ensure that the impl items are indent (more on the right of the screen) than the +// impl itself. +store-position: ("#impl-Context", {"x": impl_x}) +store-position: ("#impl-Context > .item-info", {"x": impl_item_x}) +assert: |impl_x| < |impl_item_x| + +// And we ensure that all impl items have the same indent. +assert-position: ("#impl-Context > .docblock", {"x": |impl_item_x|}) +assert-position: ("#impl-Context + .docblock", {"x": |impl_item_x|}) + +// Same with the collapsible impl block. +assert-position: ("#impl-Context-1 > .docblock", {"x": |impl_item_x|}) +assert-position: (".implementors-toggle > summary + .docblock", {"x": |impl_item_x|}) diff --git a/tests/rustdoc-gui/item-info-overflow.goml b/tests/rustdoc-gui/item-info-overflow.goml index c325beb6d0669..2c4e06e297c7d 100644 --- a/tests/rustdoc-gui/item-info-overflow.goml +++ b/tests/rustdoc-gui/item-info-overflow.goml @@ -21,7 +21,7 @@ compare-elements-property: ( ) assert-property: ( "#impl-SimpleTrait-for-LongItemInfo2 .item-info", - {"scrollWidth": "916"}, + {"scrollWidth": "935"}, ) // Just to be sure we're comparing the correct "item-info": assert-text: ( diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 31f6b7f09b7d0..bb0015b8f9c43 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -740,3 +740,29 @@ pub mod SidebarSort { impl Sort for Cell {} impl<'a> Sort for &'a str {} } + +pub mod impls_indent { + pub struct Context; + + /// Working with objects. + /// + /// # Safety + /// + /// Functions that take indices of locals do not check bounds on these indices; + /// the caller must ensure that the indices are less than the number of locals + /// in the current stack frame. + impl Context { + } + + /// Working with objects. + /// + /// # Safety + /// + /// Functions that take indices of locals do not check bounds on these indices; + /// the caller must ensure that the indices are less than the number of locals + /// in the current stack frame. + impl Context { + /// bla + pub fn bar() {} + } +} diff --git a/tests/rustdoc-json/fns/return_type_alias.rs b/tests/rustdoc-json/fns/return_type_alias.rs index 0aa1db47b7f91..c39abc7589458 100644 --- a/tests/rustdoc-json/fns/return_type_alias.rs +++ b/tests/rustdoc-json/fns/return_type_alias.rs @@ -1,6 +1,6 @@ // Regression test for -///@ set foo = "$.index[?(@.name=='Foo')].id" +//@ set foo = "$.index[?(@.name=='Foo')].id" pub type Foo = i32; //@ is "$.index[?(@.name=='demo')].inner.function.sig.output.resolved_path.id" $foo diff --git a/tests/rustdoc-json/impls/auto.rs b/tests/rustdoc-json/impls/auto.rs index ce47d1be690f7..5440301f96501 100644 --- a/tests/rustdoc-json/impls/auto.rs +++ b/tests/rustdoc-json/impls/auto.rs @@ -17,6 +17,8 @@ impl Foo { // Testing spans, so all tests below code //@ is "$.index[?(@.docs=='has span')].span.begin" "[13, 1]" //@ is "$.index[?(@.docs=='has span')].span.end" "[15, 2]" -// FIXME: this doesn't work due to https://github.com/freestrings/jsonpath/issues/91 -// is "$.index[?(@.inner.impl.is_synthetic==true)].span" null +//@ is "$.index[?(@.docs=='has span')].inner.impl.is_synthetic" false +//@ is "$.index[?(@.inner.impl.is_synthetic==true)].span" null +//@ is "$.index[?(@.inner.impl.is_synthetic==true)].inner.impl.for.resolved_path.path" '"Foo"' +//@ is "$.index[?(@.inner.impl.is_synthetic==true)].inner.impl.trait.path" '"Bar"' pub struct Foo; diff --git a/tests/rustdoc-ui/intra-doc/empty-associated-items.rs b/tests/rustdoc-ui/intra-doc/empty-associated-items.rs new file mode 100644 index 0000000000000..ea94cb349ad29 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/empty-associated-items.rs @@ -0,0 +1,8 @@ +// This test ensures that an empty associated item will not crash rustdoc. +// This is a regression test for . + +#[deny(rustdoc::broken_intra_doc_links)] + +/// [`String::`] +//~^ ERROR +pub struct Foo; diff --git a/tests/rustdoc-ui/intra-doc/empty-associated-items.stderr b/tests/rustdoc-ui/intra-doc/empty-associated-items.stderr new file mode 100644 index 0000000000000..b0527916ab502 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/empty-associated-items.stderr @@ -0,0 +1,14 @@ +error: unresolved link to `String::` + --> $DIR/empty-associated-items.rs:6:7 + | +LL | /// [`String::`] + | ^^^^^^^^ the struct `String` has no field or associated item named `` + | +note: the lint level is defined here + --> $DIR/empty-associated-items.rs:4:8 + | +LL | #[deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs new file mode 100644 index 0000000000000..a21ae0664fe63 --- /dev/null +++ b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs @@ -0,0 +1,21 @@ +// For some reason on Windows, the PATH to the libstd dylib doesn't seem to +// carry over to running the runtool. +//@ no-prefer-dynamic + +use std::path::Path; +use std::process::Command; + +fn main() { + let args: Vec<_> = std::env::args().collect(); + eprintln!("{args:#?}"); + assert_eq!(args.len(), 4); + assert_eq!(args[1], "arg1"); + assert_eq!(args[2], "arg2 with space"); + let path = Path::new(&args[3]); + let output = Command::new(path).output().unwrap(); + // Should fail without env var. + assert!(!output.status.success()); + let output = Command::new(path).env("DOCTEST_RUNTOOL_CHECK", "xyz").output().unwrap(); + // Should pass with env var. + assert!(output.status.success()); +} diff --git a/tests/rustdoc/doctest/doctest-runtool.rs b/tests/rustdoc/doctest/doctest-runtool.rs new file mode 100644 index 0000000000000..c4fb02e5228cc --- /dev/null +++ b/tests/rustdoc/doctest/doctest-runtool.rs @@ -0,0 +1,13 @@ +// Tests that the --test-runtool argument works. + +//@ ignore-cross-compile +//@ aux-bin: doctest-runtool.rs +//@ compile-flags: --test +//@ compile-flags: --test-runtool=auxiliary/bin/doctest-runtool +//@ compile-flags: --test-runtool-arg=arg1 --test-runtool-arg +//@ compile-flags: 'arg2 with space' + +/// ``` +/// assert_eq!(std::env::var("DOCTEST_RUNTOOL_CHECK"), Ok("xyz".to_string())); +/// ``` +pub fn main() {} diff --git a/tests/ui-fulldeps/stable-mir/check_abi.rs b/tests/ui-fulldeps/stable-mir/check_abi.rs index ebf2e333f085a..71edf813a7be2 100644 --- a/tests/ui-fulldeps/stable-mir/check_abi.rs +++ b/tests/ui-fulldeps/stable-mir/check_abi.rs @@ -145,7 +145,7 @@ fn get_item<'a>( fn main() { let path = "alloc_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs index ae2609bbc1221..692c24f054451 100644 --- a/tests/ui-fulldeps/stable-mir/check_allocation.rs +++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs @@ -219,7 +219,7 @@ fn get_item<'a>( fn main() { let path = "alloc_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--edition=2021".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_assoc_items.rs b/tests/ui-fulldeps/stable-mir/check_assoc_items.rs index 9d611543b5aa7..755bec8747bc8 100644 --- a/tests/ui-fulldeps/stable-mir/check_assoc_items.rs +++ b/tests/ui-fulldeps/stable-mir/check_assoc_items.rs @@ -85,7 +85,7 @@ fn check_items(items: &[T], expected: &[&str]) { fn main() { let path = "assoc_items.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs index 4148fc0cb6a07..81d5399d88aa6 100644 --- a/tests/ui-fulldeps/stable-mir/check_attribute.rs +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -57,7 +57,7 @@ fn get_item<'a>( fn main() { let path = "attribute_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_binop.rs b/tests/ui-fulldeps/stable-mir/check_binop.rs index 6a141e9c5775c..f9559d9958d28 100644 --- a/tests/ui-fulldeps/stable-mir/check_binop.rs +++ b/tests/ui-fulldeps/stable-mir/check_binop.rs @@ -81,7 +81,7 @@ impl<'a> MirVisitor for Visitor<'a> { fn main() { let path = "binop_input.rs"; generate_input(&path).unwrap(); - let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + let args = &["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; run!(args, test_binops).unwrap(); } diff --git a/tests/ui-fulldeps/stable-mir/check_crate_defs.rs b/tests/ui-fulldeps/stable-mir/check_crate_defs.rs index 31c47192d090f..6863242f22571 100644 --- a/tests/ui-fulldeps/stable-mir/check_crate_defs.rs +++ b/tests/ui-fulldeps/stable-mir/check_crate_defs.rs @@ -84,7 +84,7 @@ fn contains(items: &[T], expected: &[&str]) { fn main() { let path = "crate_definitions.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_def_ty.rs b/tests/ui-fulldeps/stable-mir/check_def_ty.rs index 00a34f138673b..f86a8e0ae6189 100644 --- a/tests/ui-fulldeps/stable-mir/check_def_ty.rs +++ b/tests/ui-fulldeps/stable-mir/check_def_ty.rs @@ -76,7 +76,7 @@ fn check_fn_def(ty: Ty) { fn main() { let path = "defs_ty_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_defs.rs b/tests/ui-fulldeps/stable-mir/check_defs.rs index 1ba73377d6e9f..ab741378bb713 100644 --- a/tests/ui-fulldeps/stable-mir/check_defs.rs +++ b/tests/ui-fulldeps/stable-mir/check_defs.rs @@ -112,7 +112,7 @@ fn get_instances(body: mir::Body) -> Vec { fn main() { let path = "defs_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_foreign.rs b/tests/ui-fulldeps/stable-mir/check_foreign.rs index 4419050ceb2c1..398024c4ff082 100644 --- a/tests/ui-fulldeps/stable-mir/check_foreign.rs +++ b/tests/ui-fulldeps/stable-mir/check_foreign.rs @@ -58,7 +58,7 @@ fn test_foreign() -> ControlFlow<()> { fn main() { let path = "foreign_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index 1510a622cdfdb..b19e5b033c469 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -87,7 +87,7 @@ fn test_body(body: mir::Body) { fn main() { let path = "instance_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs index 3f04abbb9d76d..52424857dc196 100644 --- a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs +++ b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs @@ -115,7 +115,7 @@ impl<'a> MirVisitor for CallsVisitor<'a> { fn main() { let path = "binop_input.rs"; generate_input(&path).unwrap(); - let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + let args = &["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; run!(args, test_intrinsics).unwrap(); } diff --git a/tests/ui-fulldeps/stable-mir/check_item_kind.rs b/tests/ui-fulldeps/stable-mir/check_item_kind.rs index bb8c00c64c955..d1124c75a8997 100644 --- a/tests/ui-fulldeps/stable-mir/check_item_kind.rs +++ b/tests/ui-fulldeps/stable-mir/check_item_kind.rs @@ -47,7 +47,7 @@ fn test_item_kind() -> ControlFlow<()> { fn main() { let path = "item_kind_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_normalization.rs b/tests/ui-fulldeps/stable-mir/check_normalization.rs index 797cb4cd5d051..16e8c0339ed47 100644 --- a/tests/ui-fulldeps/stable-mir/check_normalization.rs +++ b/tests/ui-fulldeps/stable-mir/check_normalization.rs @@ -61,7 +61,7 @@ fn check_ty(ty: Ty) { fn main() { let path = "normalization_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs index d9170d0c40811..fcf04a1fc3a3f 100644 --- a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs +++ b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs @@ -72,7 +72,7 @@ fn assert_impl(impl_names: &HashSet, target: &str) { fn main() { let path = "trait_queries.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_transform.rs b/tests/ui-fulldeps/stable-mir/check_transform.rs index 604cc72c3418e..9087c1cf45027 100644 --- a/tests/ui-fulldeps/stable-mir/check_transform.rs +++ b/tests/ui-fulldeps/stable-mir/check_transform.rs @@ -120,7 +120,7 @@ fn get_item<'a>( fn main() { let path = "transform_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs index 23233f8406cf5..18b9e32e4e809 100644 --- a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs +++ b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs @@ -78,7 +78,7 @@ impl<'a> MirVisitor for PlaceVisitor<'a> { fn main() { let path = "ty_fold_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/compilation-result.rs b/tests/ui-fulldeps/stable-mir/compilation-result.rs index 39416636fd673..19b9c8b7de508 100644 --- a/tests/ui-fulldeps/stable-mir/compilation-result.rs +++ b/tests/ui-fulldeps/stable-mir/compilation-result.rs @@ -25,40 +25,42 @@ use std::io::Write; fn main() { let path = "input_compilation_result_test.rs"; generate_input(&path).unwrap(); - let args = vec!["rustc".to_string(), path.to_string()]; - test_continue(args.clone()); - test_break(args.clone()); - test_failed(args.clone()); - test_skipped(args.clone()); + let args = &["rustc".to_string(), path.to_string()]; + test_continue(args); + test_break(args); + test_failed(args); + test_skipped(args); test_captured(args) } -fn test_continue(args: Vec) { +fn test_continue(args: &[String]) { let result = run!(args, || ControlFlow::Continue::<(), bool>(true)); assert_eq!(result, Ok(true)); } -fn test_break(args: Vec) { +fn test_break(args: &[String]) { let result = run!(args, || ControlFlow::Break::(false)); assert_eq!(result, Err(stable_mir::CompilerError::Interrupted(false))); } #[allow(unreachable_code)] -fn test_skipped(mut args: Vec) { +fn test_skipped(args: &[String]) { + let mut args = args.to_vec(); args.push("--version".to_string()); - let result = run!(args, || unreachable!() as ControlFlow<()>); + let result = run!(&args, || unreachable!() as ControlFlow<()>); assert_eq!(result, Err(stable_mir::CompilerError::Skipped)); } #[allow(unreachable_code)] -fn test_failed(mut args: Vec) { +fn test_failed(args: &[String]) { + let mut args = args.to_vec(); args.push("--cfg=broken".to_string()); - let result = run!(args, || unreachable!() as ControlFlow<()>); + let result = run!(&args, || unreachable!() as ControlFlow<()>); assert_eq!(result, Err(stable_mir::CompilerError::Failed)); } /// Test that we are able to pass a closure and set the return according to the captured value. -fn test_captured(args: Vec) { +fn test_captured(args: &[String]) { let captured = "10".to_string(); let result = run!(args, || ControlFlow::Continue::<(), usize>(captured.len())); assert_eq!(result, Ok(captured.len())); diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index e2086d5e57907..7fc4edafb9338 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -186,7 +186,7 @@ fn get_item<'a>( fn main() { let path = "input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/projections.rs b/tests/ui-fulldeps/stable-mir/projections.rs index f3bd894ac6903..103c97bc48e17 100644 --- a/tests/ui-fulldeps/stable-mir/projections.rs +++ b/tests/ui-fulldeps/stable-mir/projections.rs @@ -146,7 +146,7 @@ fn get_item<'a>( fn main() { let path = "input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/smir_internal.rs b/tests/ui-fulldeps/stable-mir/smir_internal.rs index f9972dc27e3ff..0519b9de68050 100644 --- a/tests/ui-fulldeps/stable-mir/smir_internal.rs +++ b/tests/ui-fulldeps/stable-mir/smir_internal.rs @@ -40,7 +40,7 @@ fn test_translation(tcx: TyCtxt<'_>) -> ControlFlow<()> { fn main() { let path = "internal_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), diff --git a/tests/ui-fulldeps/stable-mir/smir_serde.rs b/tests/ui-fulldeps/stable-mir/smir_serde.rs index 3b3d743ad3263..0b39ec050024e 100644 --- a/tests/ui-fulldeps/stable-mir/smir_serde.rs +++ b/tests/ui-fulldeps/stable-mir/smir_serde.rs @@ -46,7 +46,7 @@ fn serialize_to_json(_tcx: TyCtxt<'_>) -> ControlFlow<()> { fn main() { let path = "internal_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs index d225d9773fe51..caf71de2556c4 100644 --- a/tests/ui-fulldeps/stable-mir/smir_visitor.rs +++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs @@ -183,14 +183,14 @@ impl mir::MutMirVisitor for TestMutVisitor { fn main() { let path = "sim_visitor_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), path.to_string(), ]; - run!(args.clone(), test_visitor).unwrap(); + run!(args, test_visitor).unwrap(); run!(args, test_mut_visitor).unwrap(); } diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs index c31af6460fc83..772512702ece1 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -1,6 +1,5 @@ //@ only-x86_64 -//@ build-pass -//@ ignore-pass (test emits codegen-time warnings) +//@ build-fail //@ compile-flags: -C target-feature=-avx #![feature(avx512_target_feature)] @@ -14,20 +13,17 @@ use std::arch::x86_64::*; struct Wrapper(__m256); unsafe extern "C" fn w(_: Wrapper) { - //~^ WARN requires the `avx` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: requires the `avx` target feature, which is not enabled todo!() } unsafe extern "C" fn f(_: __m256) { - //~^ WARN requires the `avx` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: requires the `avx` target feature, which is not enabled todo!() } unsafe extern "C" fn g() -> __m256 { - //~^ WARN requires the `avx` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: requires the `avx` target feature, which is not enabled todo!() } @@ -56,25 +52,20 @@ unsafe fn test() { unsafe fn in_closure() -> impl FnOnce() -> __m256 { #[inline(always)] // this disables target-feature inheritance || g() - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller } fn main() { unsafe { f(g()); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller + //~| ERROR requires the `avx` target feature, which is not enabled in the caller } unsafe { gavx(favx()); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller + //~| ERROR requires the `avx` target feature, which is not enabled in the caller } unsafe { @@ -83,10 +74,8 @@ fn main() { unsafe { w(Wrapper(g())); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller + //~| ERROR requires the `avx` target feature, which is not enabled in the caller } unsafe { @@ -99,8 +88,7 @@ fn main() { fn some_extern() -> __m256; } some_extern(); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller } } diff --git a/tests/ui/abi/simd-abi-checks-avx.stderr b/tests/ui/abi/simd-abi-checks-avx.stderr index 5419970f8093b..48db30bf45371 100644 --- a/tests/ui/abi/simd-abi-checks-avx.stderr +++ b/tests/ui/abi/simd-abi-checks-avx.stderr @@ -1,245 +1,96 @@ -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:65:11 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:60:11 | LL | f(g()); | ^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:65:9 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:60:9 | LL | f(g()); | ^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:73:14 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:66:14 | LL | gavx(favx()); | ^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:73:9 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:66:9 | LL | gavx(favx()); | ^^^^^^^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:85:19 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:76:19 | LL | w(Wrapper(g())); | ^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:85:9 +error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:76:9 | LL | w(Wrapper(g())); | ^^^^^^^^^^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:101:9 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:90:9 | LL | some_extern(); | ^^^^^^^^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:28:1 +error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled + --> $DIR/simd-abi-checks-avx.rs:25:1 | LL | unsafe extern "C" fn g() -> __m256 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:22:1 +error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled + --> $DIR/simd-abi-checks-avx.rs:20:1 | LL | unsafe extern "C" fn f(_: __m256) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:16:1 +error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled + --> $DIR/simd-abi-checks-avx.rs:15:1 | LL | unsafe extern "C" fn w(_: Wrapper) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:58:8 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:54:8 | LL | || g() | ^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: 11 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:65:11 - | -LL | f(g()); - | ^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:65:9 - | -LL | f(g()); - | ^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:73:14 - | -LL | gavx(favx()); - | ^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:73:9 - | -LL | gavx(favx()); - | ^^^^^^^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:85:19 - | -LL | w(Wrapper(g())); - | ^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:85:9 - | -LL | w(Wrapper(g())); - | ^^^^^^^^^^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:101:9 - | -LL | some_extern(); - | ^^^^^^^^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:28:1 - | -LL | unsafe extern "C" fn g() -> __m256 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:22:1 - | -LL | unsafe extern "C" fn f(_: __m256) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:16:1 - | -LL | unsafe extern "C" fn w(_: Wrapper) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here +note: the above error was encountered while instantiating `fn in_closure::{closure#0}` + --> $DIR/simd-abi-checks-avx.rs:82:9 | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default +LL | in_closure()(); + | ^^^^^^^^^^^^^^ -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:58:8 - | -LL | || g() - | ^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default +error: aborting due to 11 previous errors diff --git a/tests/ui/abi/simd-abi-checks-empty-list.rs b/tests/ui/abi/simd-abi-checks-empty-list.rs index ba1b38af5b34a..d00445b29e055 100644 --- a/tests/ui/abi/simd-abi-checks-empty-list.rs +++ b/tests/ui/abi/simd-abi-checks-empty-list.rs @@ -1,8 +1,9 @@ +//! At the time of writing, the list of "which target feature enables which vector size" is empty +//! for SPARC. Ensure that this leads to all vector sizes causing an error. //@ add-core-stubs //@ needs-llvm-components: sparc //@ compile-flags: --target=sparc-unknown-none-elf --crate-type=rlib -//@ build-pass -//@ ignore-pass (test emits codegen-time warnings) +//@ build-fail #![no_core] #![feature(no_core, repr_simd)] #![allow(improper_ctypes_definitions)] @@ -14,5 +15,4 @@ use minicore::*; pub struct SimdVec([i32; 4]); pub extern "C" fn pass_by_vec(_: SimdVec) {} -//~^ WARN this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI -//~| WARNING this was previously accepted by the compiler +//~^ ERROR: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI diff --git a/tests/ui/abi/simd-abi-checks-empty-list.stderr b/tests/ui/abi/simd-abi-checks-empty-list.stderr index 111dda42f33f2..780afb3844fc5 100644 --- a/tests/ui/abi/simd-abi-checks-empty-list.stderr +++ b/tests/ui/abi/simd-abi-checks-empty-list.stderr @@ -1,23 +1,8 @@ -warning: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI - --> $DIR/simd-abi-checks-empty-list.rs:16:1 +error: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI + --> $DIR/simd-abi-checks-empty-list.rs:17:1 | LL | pub extern "C" fn pass_by_vec(_: SimdVec) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -warning: 1 warning emitted -Future incompatibility report: Future breakage diagnostic: -warning: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI - --> $DIR/simd-abi-checks-empty-list.rs:16:1 - | -LL | pub extern "C" fn pass_by_vec(_: SimdVec) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = note: `#[warn(abi_unsupported_vector_types)]` on by default +error: aborting due to 1 previous error diff --git a/tests/ui/abi/simd-abi-checks-s390x.rs b/tests/ui/abi/simd-abi-checks-s390x.rs index 3743c75bf1ed5..2d4eb7a350f25 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.rs +++ b/tests/ui/abi/simd-abi-checks-s390x.rs @@ -13,7 +13,6 @@ #![no_core] #![crate_type = "lib"] #![allow(non_camel_case_types, improper_ctypes_definitions)] -#![deny(abi_unsupported_vector_types)] extern crate minicore; use minicore::*; @@ -38,13 +37,11 @@ impl Copy for TransparentWrapper {} #[no_mangle] extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted *x } #[no_mangle] extern "C" fn vector_ret(x: &i8x16) -> i8x16 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted *x } #[no_mangle] @@ -93,7 +90,6 @@ extern "C" fn vector_transparent_wrapper_ret_small( x: &TransparentWrapper, ) -> TransparentWrapper { //~^^^ ERROR requires the `vector` target feature, which is not enabled - //~^^^^ WARN this was previously accepted *x } #[no_mangle] @@ -101,7 +97,6 @@ extern "C" fn vector_transparent_wrapper_ret( x: &TransparentWrapper, ) -> TransparentWrapper { //~^^^ ERROR requires the `vector` target feature, which is not enabled - //~^^^^ WARN this was previously accepted *x } #[no_mangle] @@ -115,13 +110,11 @@ extern "C" fn vector_transparent_wrapper_ret_large( #[no_mangle] extern "C" fn vector_arg_small(x: i8x8) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const i8x8 as *const i64) } } #[no_mangle] extern "C" fn vector_arg(x: i8x16) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const i8x16 as *const i64) } } #[no_mangle] @@ -133,13 +126,11 @@ extern "C" fn vector_arg_large(x: i8x32) -> i64 { #[no_mangle] extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const Wrapper as *const i64) } } #[no_mangle] extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const Wrapper as *const i64) } } #[no_mangle] @@ -151,13 +142,11 @@ extern "C" fn vector_wrapper_arg_large(x: Wrapper) -> i64 { #[no_mangle] extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const TransparentWrapper as *const i64) } } #[no_mangle] extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const TransparentWrapper as *const i64) } } #[no_mangle] diff --git a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr index d2f7abb7c3221..c1c4e90f3cf89 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr @@ -1,275 +1,86 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 + --> $DIR/simd-abi-checks-s390x.rs:38:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 + --> $DIR/simd-abi-checks-s390x.rs:43:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 + --> $DIR/simd-abi-checks-s390x.rs:89:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |_____________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 + --> $DIR/simd-abi-checks-s390x.rs:96:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |______________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:111:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 + --> $DIR/simd-abi-checks-s390x.rs:116:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 + --> $DIR/simd-abi-checks-s390x.rs:127:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 + --> $DIR/simd-abi-checks-s390x.rs:132:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 + --> $DIR/simd-abi-checks-s390x.rs:143:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 + --> $DIR/simd-abi-checks-s390x.rs:148:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: aborting due to 10 previous errors -Future incompatibility report: Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 - | -LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 - | -LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret_small( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |_____________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |______________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 - | -LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 - | -LL | extern "C" fn vector_arg(x: i8x16) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 - | -LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 - | -LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr index d2f7abb7c3221..c1c4e90f3cf89 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr @@ -1,275 +1,86 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 + --> $DIR/simd-abi-checks-s390x.rs:38:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 + --> $DIR/simd-abi-checks-s390x.rs:43:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 + --> $DIR/simd-abi-checks-s390x.rs:89:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |_____________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 + --> $DIR/simd-abi-checks-s390x.rs:96:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |______________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:111:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 + --> $DIR/simd-abi-checks-s390x.rs:116:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 + --> $DIR/simd-abi-checks-s390x.rs:127:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 + --> $DIR/simd-abi-checks-s390x.rs:132:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 + --> $DIR/simd-abi-checks-s390x.rs:143:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 + --> $DIR/simd-abi-checks-s390x.rs:148:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: aborting due to 10 previous errors -Future incompatibility report: Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 - | -LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 - | -LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret_small( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |_____________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |______________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 - | -LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 - | -LL | extern "C" fn vector_arg(x: i8x16) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 - | -LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 - | -LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr index d2f7abb7c3221..c1c4e90f3cf89 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr @@ -1,275 +1,86 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 + --> $DIR/simd-abi-checks-s390x.rs:38:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 + --> $DIR/simd-abi-checks-s390x.rs:43:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 + --> $DIR/simd-abi-checks-s390x.rs:89:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |_____________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 + --> $DIR/simd-abi-checks-s390x.rs:96:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |______________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:111:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 + --> $DIR/simd-abi-checks-s390x.rs:116:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 + --> $DIR/simd-abi-checks-s390x.rs:127:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 + --> $DIR/simd-abi-checks-s390x.rs:132:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 + --> $DIR/simd-abi-checks-s390x.rs:143:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 + --> $DIR/simd-abi-checks-s390x.rs:148:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: aborting due to 10 previous errors -Future incompatibility report: Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 - | -LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 - | -LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret_small( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |_____________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |______________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 - | -LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 - | -LL | extern "C" fn vector_arg(x: i8x16) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 - | -LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 - | -LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/abi/simd-abi-checks-sse.rs b/tests/ui/abi/simd-abi-checks-sse.rs index d5fa9c0c0a3cd..817f9b6d13bc6 100644 --- a/tests/ui/abi/simd-abi-checks-sse.rs +++ b/tests/ui/abi/simd-abi-checks-sse.rs @@ -3,8 +3,7 @@ //@ compile-flags: --crate-type=rlib --target=i586-unknown-linux-gnu //@ compile-flags: -Ctarget-cpu=pentium4 -C target-feature=-sse,-sse2 //@ add-core-stubs -//@ build-pass -//@ ignore-pass (test emits codegen-time warnings) +//@ build-fail //@ needs-llvm-components: x86 #![feature(no_core, repr_simd)] #![no_core] @@ -18,6 +17,5 @@ pub struct SseVector([i64; 2]); #[no_mangle] pub unsafe extern "C" fn f(_: SseVector) { - //~^ WARN this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled } diff --git a/tests/ui/abi/simd-abi-checks-sse.stderr b/tests/ui/abi/simd-abi-checks-sse.stderr index c0f2e6e1e1b14..a5a2ba808c660 100644 --- a/tests/ui/abi/simd-abi-checks-sse.stderr +++ b/tests/ui/abi/simd-abi-checks-sse.stderr @@ -1,25 +1,10 @@ -warning: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled - --> $DIR/simd-abi-checks-sse.rs:20:1 +error: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled + --> $DIR/simd-abi-checks-sse.rs:19:1 | LL | pub unsafe extern "C" fn f(_: SseVector) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+sse`) or locally (`#[target_feature(enable="sse")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled - --> $DIR/simd-abi-checks-sse.rs:20:1 - | -LL | pub unsafe extern "C" fn f(_: SseVector) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+sse`) or locally (`#[target_feature(enable="sse")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default +error: aborting due to 1 previous error diff --git a/tests/ui/amdgpu-require-explicit-cpu.rs b/tests/ui/amdgpu-require-explicit-cpu.rs deleted file mode 100644 index d40cb97977d94..0000000000000 --- a/tests/ui/amdgpu-require-explicit-cpu.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ revisions: nocpu cpu -//@ no-prefer-dynamic -//@ compile-flags: --crate-type=cdylib --target=amdgcn-amd-amdhsa -//@ needs-llvm-components: amdgpu -//@ needs-rust-lld -//@[nocpu] build-fail -//@[cpu] compile-flags: -Ctarget-cpu=gfx900 -//@[cpu] build-pass - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang="sized"] -trait Sized {} - -pub fn foo() {} - -//[nocpu]~? ERROR target requires explicitly specifying a cpu with `-C target-cpu` diff --git a/tests/ui/asm/naked-asm-outside-naked-fn.rs b/tests/ui/asm/naked-asm-outside-naked-fn.rs index a7680cc63ae03..0a15b21f4d014 100644 --- a/tests/ui/asm/naked-asm-outside-naked-fn.rs +++ b/tests/ui/asm/naked-asm-outside-naked-fn.rs @@ -3,7 +3,6 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(naked_functions)] #![crate_type = "lib"] use std::arch::naked_asm; diff --git a/tests/ui/asm/naked-asm-outside-naked-fn.stderr b/tests/ui/asm/naked-asm-outside-naked-fn.stderr index 85a50a49fecfc..2cebaa9ea285d 100644 --- a/tests/ui/asm/naked-asm-outside-naked-fn.stderr +++ b/tests/ui/asm/naked-asm-outside-naked-fn.stderr @@ -1,17 +1,17 @@ error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` - --> $DIR/naked-asm-outside-naked-fn.rs:21:5 + --> $DIR/naked-asm-outside-naked-fn.rs:20:5 | LL | naked_asm!("") | ^^^^^^^^^^^^^^ error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` - --> $DIR/naked-asm-outside-naked-fn.rs:26:9 + --> $DIR/naked-asm-outside-naked-fn.rs:25:9 | LL | (|| naked_asm!(""))() | ^^^^^^^^^^^^^^ error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` - --> $DIR/naked-asm-outside-naked-fn.rs:32:9 + --> $DIR/naked-asm-outside-naked-fn.rs:31:9 | LL | naked_asm!(""); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/naked-functions-ffi.rs b/tests/ui/asm/naked-functions-ffi.rs index 8fd0da01d72a7..565c440022db3 100644 --- a/tests/ui/asm/naked-functions-ffi.rs +++ b/tests/ui/asm/naked-functions-ffi.rs @@ -1,6 +1,5 @@ //@ check-pass //@ needs-asm-support -#![feature(naked_functions)] #![crate_type = "lib"] use std::arch::naked_asm; diff --git a/tests/ui/asm/naked-functions-ffi.stderr b/tests/ui/asm/naked-functions-ffi.stderr index 908881b194999..9df6185498ed6 100644 --- a/tests/ui/asm/naked-functions-ffi.stderr +++ b/tests/ui/asm/naked-functions-ffi.stderr @@ -1,5 +1,5 @@ warning: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/naked-functions-ffi.rs:9:28 + --> $DIR/naked-functions-ffi.rs:8:28 | LL | pub extern "C" fn naked(p: char) -> u128 { | ^^^^ not FFI-safe @@ -9,7 +9,7 @@ LL | pub extern "C" fn naked(p: char) -> u128 { = note: `#[warn(improper_ctypes_definitions)]` on by default warning: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/naked-functions-ffi.rs:9:37 + --> $DIR/naked-functions-ffi.rs:8:37 | LL | pub extern "C" fn naked(p: char) -> u128 { | ^^^^ not FFI-safe diff --git a/tests/ui/asm/naked-functions-inline.rs b/tests/ui/asm/naked-functions-inline.rs index 261401be64517..93741f26275bd 100644 --- a/tests/ui/asm/naked-functions-inline.rs +++ b/tests/ui/asm/naked-functions-inline.rs @@ -1,5 +1,4 @@ //@ needs-asm-support -#![feature(naked_functions)] #![crate_type = "lib"] use std::arch::naked_asm; diff --git a/tests/ui/asm/naked-functions-inline.stderr b/tests/ui/asm/naked-functions-inline.stderr index 6df5b08ae8534..07d5f3bc49a94 100644 --- a/tests/ui/asm/naked-functions-inline.stderr +++ b/tests/ui/asm/naked-functions-inline.stderr @@ -1,5 +1,5 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:13:1 + --> $DIR/naked-functions-inline.rs:12:1 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here @@ -7,7 +7,7 @@ LL | #[inline] | ^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:20:1 + --> $DIR/naked-functions-inline.rs:19:1 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here @@ -15,7 +15,7 @@ LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:27:1 + --> $DIR/naked-functions-inline.rs:26:1 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here @@ -23,7 +23,7 @@ LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/naked-functions-inline.rs:34:19 + --> $DIR/naked-functions-inline.rs:33:19 | LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index 6fd34b035edd0..69927a56aabb3 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -5,7 +5,7 @@ //@ build-pass #![crate_type = "lib"] -#![feature(no_core, naked_functions)] +#![feature(no_core)] #![no_core] extern crate minicore; diff --git a/tests/ui/asm/naked-functions-rustic-abi.rs b/tests/ui/asm/naked-functions-rustic-abi.rs index 99b8d2e19fe40..d9c1147482881 100644 --- a/tests/ui/asm/naked-functions-rustic-abi.rs +++ b/tests/ui/asm/naked-functions-rustic-abi.rs @@ -6,7 +6,7 @@ //@ build-pass //@ needs-asm-support -#![feature(naked_functions, naked_functions_rustic_abi, rust_cold_cc)] +#![feature(naked_functions_rustic_abi, rust_cold_cc)] #![crate_type = "lib"] use std::arch::{asm, naked_asm}; diff --git a/tests/ui/asm/naked-functions-target-feature.rs b/tests/ui/asm/naked-functions-target-feature.rs index d8dc2104c76e1..57ad79b1c315c 100644 --- a/tests/ui/asm/naked-functions-target-feature.rs +++ b/tests/ui/asm/naked-functions-target-feature.rs @@ -1,7 +1,7 @@ //@ build-pass //@ needs-asm-support -#![feature(naked_functions, naked_functions_target_feature)] +#![feature(naked_functions_target_feature)] #![crate_type = "lib"] use std::arch::{asm, naked_asm}; diff --git a/tests/ui/asm/naked-functions-testattrs.rs b/tests/ui/asm/naked-functions-testattrs.rs index c8539e8064088..6dc14a6840ecb 100644 --- a/tests/ui/asm/naked-functions-testattrs.rs +++ b/tests/ui/asm/naked-functions-testattrs.rs @@ -1,7 +1,6 @@ //@ needs-asm-support //@ compile-flags: --test -#![feature(naked_functions)] #![feature(test)] #![crate_type = "lib"] diff --git a/tests/ui/asm/naked-functions-testattrs.stderr b/tests/ui/asm/naked-functions-testattrs.stderr index ad2041ec118b9..8aab2f04ee29e 100644 --- a/tests/ui/asm/naked-functions-testattrs.stderr +++ b/tests/ui/asm/naked-functions-testattrs.stderr @@ -1,5 +1,5 @@ error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:11:1 + --> $DIR/naked-functions-testattrs.rs:10:1 | LL | #[test] | ------- function marked with testing attribute here @@ -7,7 +7,7 @@ LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:19:1 + --> $DIR/naked-functions-testattrs.rs:18:1 | LL | #[test] | ------- function marked with testing attribute here @@ -15,7 +15,7 @@ LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:27:1 + --> $DIR/naked-functions-testattrs.rs:26:1 | LL | #[test] | ------- function marked with testing attribute here @@ -23,7 +23,7 @@ LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:34:1 + --> $DIR/naked-functions-testattrs.rs:33:1 | LL | #[bench] | -------- function marked with testing attribute here diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions-unused.aarch64.stderr index ea63ced1aab04..bfb2923b0b8d6 100644 --- a/tests/ui/asm/naked-functions-unused.aarch64.stderr +++ b/tests/ui/asm/naked-functions-unused.aarch64.stderr @@ -1,5 +1,5 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:17:32 + --> $DIR/naked-functions-unused.rs:16:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,55 +12,55 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:17:42 + --> $DIR/naked-functions-unused.rs:16:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:28:38 + --> $DIR/naked-functions-unused.rs:27:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:28:48 + --> $DIR/naked-functions-unused.rs:27:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:36:41 + --> $DIR/naked-functions-unused.rs:35:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:36:51 + --> $DIR/naked-functions-unused.rs:35:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:46:40 + --> $DIR/naked-functions-unused.rs:45:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:46:50 + --> $DIR/naked-functions-unused.rs:45:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:54:43 + --> $DIR/naked-functions-unused.rs:53:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:54:53 + --> $DIR/naked-functions-unused.rs:53:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions-unused.rs index 67c05984be71c..945ab1a40ad0c 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions-unused.rs @@ -3,7 +3,6 @@ //@[x86_64] only-x86_64 //@[aarch64] only-aarch64 #![deny(unused)] -#![feature(naked_functions)] #![crate_type = "lib"] pub trait Trait { diff --git a/tests/ui/asm/naked-functions-unused.x86_64.stderr b/tests/ui/asm/naked-functions-unused.x86_64.stderr index ea63ced1aab04..bfb2923b0b8d6 100644 --- a/tests/ui/asm/naked-functions-unused.x86_64.stderr +++ b/tests/ui/asm/naked-functions-unused.x86_64.stderr @@ -1,5 +1,5 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:17:32 + --> $DIR/naked-functions-unused.rs:16:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,55 +12,55 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:17:42 + --> $DIR/naked-functions-unused.rs:16:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:28:38 + --> $DIR/naked-functions-unused.rs:27:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:28:48 + --> $DIR/naked-functions-unused.rs:27:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:36:41 + --> $DIR/naked-functions-unused.rs:35:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:36:51 + --> $DIR/naked-functions-unused.rs:35:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:46:40 + --> $DIR/naked-functions-unused.rs:45:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:46:50 + --> $DIR/naked-functions-unused.rs:45:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:54:43 + --> $DIR/naked-functions-unused.rs:53:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:54:53 + --> $DIR/naked-functions-unused.rs:53:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index b433c1b5389c4..1eeb716e98a1f 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -2,7 +2,6 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(naked_functions)] #![feature(asm_unwind, linkage)] #![crate_type = "lib"] diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index 2b67c3aecd73c..b94a09bb92ed9 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,107 +1,107 @@ error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:47:29 + --> $DIR/naked-functions.rs:46:29 | LL | naked_asm!("/* {0} */", in(reg) a) | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:68:10 + --> $DIR/naked-functions.rs:67:10 | LL | in(reg) a, | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `noreturn` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:88:28 + --> $DIR/naked-functions.rs:87:28 | LL | naked_asm!("", options(noreturn)); | ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly error: the `nomem` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:105:28 + --> $DIR/naked-functions.rs:104:28 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: the `preserves_flags` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:105:35 + --> $DIR/naked-functions.rs:104:35 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly error: the `readonly` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:112:28 + --> $DIR/naked-functions.rs:111:28 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly error: the `nostack` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:112:38 + --> $DIR/naked-functions.rs:111:38 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly error: the `pure` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:112:56 + --> $DIR/naked-functions.rs:111:56 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^ the `pure` option is not meaningful for global-scoped inline assembly error: the `may_unwind` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:120:28 + --> $DIR/naked-functions.rs:119:28 | LL | naked_asm!("", options(may_unwind)); | ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly error: this is a user specified error - --> $DIR/naked-functions.rs:151:5 + --> $DIR/naked-functions.rs:150:5 | LL | compile_error!("this is a user specified error") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this is a user specified error - --> $DIR/naked-functions.rs:157:5 + --> $DIR/naked-functions.rs:156:5 | LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:164:16 + --> $DIR/naked-functions.rs:163:16 | LL | naked_asm!(invalid_syntax) | ^^^^^^^^^^^^^^ error[E0787]: the `asm!` macro is not allowed in naked functions - --> $DIR/naked-functions.rs:13:14 + --> $DIR/naked-functions.rs:12:14 | LL | unsafe { asm!("", options(raw)) }; | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:25:5 + --> $DIR/naked-functions.rs:24:5 | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:27:5 + --> $DIR/naked-functions.rs:26:5 | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:29:6 + --> $DIR/naked-functions.rs:28:6 | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:31:5 + --> $DIR/naked-functions.rs:30:5 | LL | P { x, y }: P, | ^^^^^^^^^^ error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:40:5 + --> $DIR/naked-functions.rs:39:5 | LL | a + 1 | ^ @@ -109,7 +109,7 @@ LL | a + 1 = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:38:1 + --> $DIR/naked-functions.rs:37:1 | LL | pub extern "C" fn inc(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | a + 1 | ----- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:52:1 + --> $DIR/naked-functions.rs:51:1 | LL | pub extern "C" fn inc_closure(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | (|| a + 1)() | ------------ not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:58:1 + --> $DIR/naked-functions.rs:57:1 | LL | pub extern "C" fn unsupported_operands() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,13 +144,13 @@ LL | let mut e = 0usize; | ------------------- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:80:1 + --> $DIR/naked-functions.rs:79:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:85:1 + --> $DIR/naked-functions.rs:84:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL | naked_asm!(""); | -------------- multiple `naked_asm!` invocations are not allowed in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:97:11 + --> $DIR/naked-functions.rs:96:11 | LL | *&y | ^ @@ -167,7 +167,7 @@ LL | *&y = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:95:5 + --> $DIR/naked-functions.rs:94:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/naked-invalid-attr.rs b/tests/ui/asm/naked-invalid-attr.rs index 6c5fdbe74d82a..6ac9cb9e3a9c5 100644 --- a/tests/ui/asm/naked-invalid-attr.rs +++ b/tests/ui/asm/naked-invalid-attr.rs @@ -1,7 +1,6 @@ -// Checks that #[unsafe(naked)] attribute can be placed on function definitions only. +// Checks that the #[unsafe(naked)] attribute can be placed on function definitions only. // //@ needs-asm-support -#![feature(naked_functions)] #![unsafe(naked)] //~ ERROR should be applied to a function definition use std::arch::naked_asm; @@ -14,6 +13,7 @@ extern "C" { #[unsafe(naked)] //~ ERROR should be applied to a function definition #[repr(C)] struct S { + #[unsafe(naked)] //~ ERROR should be applied to a function definition a: u32, b: u32, } @@ -51,3 +51,12 @@ fn main() { #[unsafe(naked)] //~ ERROR should be applied to a function definition || {}; } + +// Check that the path of an attribute without a name is printed correctly (issue #140082) +#[::a] +//~^ ERROR attribute incompatible with `#[unsafe(naked)]` +//~| ERROR failed to resolve: use of unresolved module or unlinked crate `a` +#[unsafe(naked)] +extern "C" fn issue_140082() { + naked_asm!("") +} diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index 6e2746b568437..ef389e7d921b9 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -1,15 +1,30 @@ +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `a` + --> $DIR/naked-invalid-attr.rs:56:5 + | +LL | #[::a] + | ^ use of unresolved module or unlinked crate `a` + error: attribute should be applied to a function definition - --> $DIR/naked-invalid-attr.rs:14:1 + --> $DIR/naked-invalid-attr.rs:13:1 | LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ LL | #[repr(C)] LL | / struct S { +LL | | #[unsafe(naked)] LL | | a: u32, LL | | b: u32, LL | | } | |_- not a function definition +error: attribute should be applied to a function definition + --> $DIR/naked-invalid-attr.rs:16:5 + | +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ +LL | a: u32, + | ------ not a function definition + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:51:5 | @@ -18,6 +33,15 @@ LL | #[unsafe(naked)] LL | || {}; | ----- not a function definition +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-invalid-attr.rs:56:1 + | +LL | #[::a] + | ^^^^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]` +... +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:22:5 | @@ -27,7 +51,7 @@ LL | extern "C" fn invoke(&self); | ---------------------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/naked-invalid-attr.rs:10:5 + --> $DIR/naked-invalid-attr.rs:9:5 | LL | #[unsafe(naked)] | ^^^^^^^^^^^^^^^^ @@ -35,10 +59,12 @@ LL | fn f(); | ------- not a function definition error: attribute should be applied to a function definition - --> $DIR/naked-invalid-attr.rs:5:1 + --> $DIR/naked-invalid-attr.rs:4:1 | LL | #![unsafe(naked)] | ^^^^^^^^^^^^^^^^^ cannot be applied to crates -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors +Some errors have detailed explanations: E0433, E0736. +For more information about an error, try `rustc --explain E0433`. diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.rs b/tests/ui/asm/naked-with-invalid-repr-attr.rs index c9f335ea9506a..96eed70dc5504 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.rs +++ b/tests/ui/asm/naked-with-invalid-repr-attr.rs @@ -1,5 +1,4 @@ //@ needs-asm-support -#![feature(naked_functions)] #![feature(fn_align)] #![crate_type = "lib"] use std::arch::naked_asm; diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.stderr b/tests/ui/asm/naked-with-invalid-repr-attr.stderr index 219e32473beaa..f173a39e5bf6d 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.stderr +++ b/tests/ui/asm/naked-with-invalid-repr-attr.stderr @@ -1,5 +1,5 @@ error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:7:8 + --> $DIR/naked-with-invalid-repr-attr.rs:6:8 | LL | #[repr(C)] | ^ @@ -11,7 +11,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:15:8 + --> $DIR/naked-with-invalid-repr-attr.rs:14:8 | LL | #[repr(transparent)] | ^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:23:19 + --> $DIR/naked-with-invalid-repr-attr.rs:22:19 | LL | #[repr(align(16), C)] | ^ @@ -35,7 +35,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:32:8 + --> $DIR/naked-with-invalid-repr-attr.rs:31:8 | LL | #[repr(C, packed)] | ^ @@ -48,7 +48,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct or union - --> $DIR/naked-with-invalid-repr-attr.rs:32:11 + --> $DIR/naked-with-invalid-repr-attr.rs:31:11 | LL | #[repr(C, packed)] | ^^^^^^ @@ -61,7 +61,7 @@ LL | | } | |_- not a struct or union error[E0517]: attribute should be applied to an enum - --> $DIR/naked-with-invalid-repr-attr.rs:42:8 + --> $DIR/naked-with-invalid-repr-attr.rs:41:8 | LL | #[repr(u8)] | ^^ diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index d5c194452d75b..996fb82a944f2 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -10,8 +10,6 @@ // which causes less readable LLVM errors and in the worst cases causes ICEs // or segfaults based on system dependent behavior and codegen flags. -#![feature(naked_functions)] - use std::arch::{asm, global_asm, naked_asm}; #[no_mangle] diff --git a/tests/ui/asm/named-asm-labels.stderr b/tests/ui/asm/named-asm-labels.stderr index 0120d4948d51c..cd7e7a08c1d29 100644 --- a/tests/ui/asm/named-asm-labels.stderr +++ b/tests/ui/asm/named-asm-labels.stderr @@ -1,5 +1,5 @@ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:23:15 + --> $DIR/named-asm-labels.rs:21:15 | LL | asm!("bar: nop"); | ^^^ @@ -9,7 +9,7 @@ LL | asm!("bar: nop"); = note: `#[deny(named_asm_labels)]` on by default error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:26:15 + --> $DIR/named-asm-labels.rs:24:15 | LL | asm!("abcd:"); | ^^^^ @@ -18,7 +18,7 @@ LL | asm!("abcd:"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:29:15 + --> $DIR/named-asm-labels.rs:27:15 | LL | asm!("foo: bar1: nop"); | ^^^ @@ -27,7 +27,7 @@ LL | asm!("foo: bar1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:29:20 + --> $DIR/named-asm-labels.rs:27:20 | LL | asm!("foo: bar1: nop"); | ^^^^ @@ -36,7 +36,7 @@ LL | asm!("foo: bar1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:34:15 + --> $DIR/named-asm-labels.rs:32:15 | LL | asm!("foo1: nop", "nop"); | ^^^^ @@ -45,7 +45,7 @@ LL | asm!("foo1: nop", "nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:35:15 + --> $DIR/named-asm-labels.rs:33:15 | LL | asm!("foo2: foo3: nop", "nop"); | ^^^^ @@ -54,7 +54,7 @@ LL | asm!("foo2: foo3: nop", "nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:35:21 + --> $DIR/named-asm-labels.rs:33:21 | LL | asm!("foo2: foo3: nop", "nop"); | ^^^^ @@ -63,7 +63,7 @@ LL | asm!("foo2: foo3: nop", "nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:38:22 + --> $DIR/named-asm-labels.rs:36:22 | LL | asm!("nop", "foo4: nop"); | ^^^^ @@ -72,7 +72,7 @@ LL | asm!("nop", "foo4: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:39:15 + --> $DIR/named-asm-labels.rs:37:15 | LL | asm!("foo5: nop", "foo6: nop"); | ^^^^ @@ -81,7 +81,7 @@ LL | asm!("foo5: nop", "foo6: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:39:28 + --> $DIR/named-asm-labels.rs:37:28 | LL | asm!("foo5: nop", "foo6: nop"); | ^^^^ @@ -90,7 +90,7 @@ LL | asm!("foo5: nop", "foo6: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:44:15 + --> $DIR/named-asm-labels.rs:42:15 | LL | asm!("foo7: nop; foo8: nop"); | ^^^^ @@ -99,7 +99,7 @@ LL | asm!("foo7: nop; foo8: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:44:26 + --> $DIR/named-asm-labels.rs:42:26 | LL | asm!("foo7: nop; foo8: nop"); | ^^^^ @@ -108,7 +108,7 @@ LL | asm!("foo7: nop; foo8: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:47:15 + --> $DIR/named-asm-labels.rs:45:15 | LL | asm!("foo9: nop; nop"); | ^^^^ @@ -117,7 +117,7 @@ LL | asm!("foo9: nop; nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:48:20 + --> $DIR/named-asm-labels.rs:46:20 | LL | asm!("nop; foo10: nop"); | ^^^^^ @@ -126,7 +126,7 @@ LL | asm!("nop; foo10: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:51:15 + --> $DIR/named-asm-labels.rs:49:15 | LL | asm!("bar2: nop\n bar3: nop"); | ^^^^ @@ -135,7 +135,7 @@ LL | asm!("bar2: nop\n bar3: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:51:27 + --> $DIR/named-asm-labels.rs:49:27 | LL | asm!("bar2: nop\n bar3: nop"); | ^^^^ @@ -144,7 +144,7 @@ LL | asm!("bar2: nop\n bar3: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:54:15 + --> $DIR/named-asm-labels.rs:52:15 | LL | asm!("bar4: nop\n nop"); | ^^^^ @@ -153,7 +153,7 @@ LL | asm!("bar4: nop\n nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:55:21 + --> $DIR/named-asm-labels.rs:53:21 | LL | asm!("nop\n bar5: nop"); | ^^^^ @@ -162,7 +162,7 @@ LL | asm!("nop\n bar5: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:56:21 + --> $DIR/named-asm-labels.rs:54:21 | LL | asm!("nop\n bar6: bar7: nop"); | ^^^^ @@ -171,7 +171,7 @@ LL | asm!("nop\n bar6: bar7: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:56:27 + --> $DIR/named-asm-labels.rs:54:27 | LL | asm!("nop\n bar6: bar7: nop"); | ^^^^ @@ -180,7 +180,7 @@ LL | asm!("nop\n bar6: bar7: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:63:13 + --> $DIR/named-asm-labels.rs:61:13 | LL | blah2: nop | ^^^^^ @@ -189,7 +189,7 @@ LL | blah2: nop = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:64:13 + --> $DIR/named-asm-labels.rs:62:13 | LL | blah3: nop | ^^^^^ @@ -198,7 +198,7 @@ LL | blah3: nop = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:73:19 + --> $DIR/named-asm-labels.rs:71:19 | LL | nop ; blah4: nop | ^^^^^ @@ -207,7 +207,7 @@ LL | nop ; blah4: nop = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:87:15 + --> $DIR/named-asm-labels.rs:85:15 | LL | asm!("blah1: 2bar: nop"); | ^^^^^ @@ -216,7 +216,7 @@ LL | asm!("blah1: 2bar: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:90:15 + --> $DIR/named-asm-labels.rs:88:15 | LL | asm!("def: def: nop"); | ^^^ @@ -225,7 +225,7 @@ LL | asm!("def: def: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:90:15 + --> $DIR/named-asm-labels.rs:88:15 | LL | asm!("def: def: nop"); | ^^^ @@ -235,7 +235,7 @@ LL | asm!("def: def: nop"); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:93:15 + --> $DIR/named-asm-labels.rs:91:15 | LL | asm!("def: nop\ndef: nop"); | ^^^ @@ -244,7 +244,7 @@ LL | asm!("def: nop\ndef: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:93:15 + --> $DIR/named-asm-labels.rs:91:15 | LL | asm!("def: nop\ndef: nop"); | ^^^ @@ -254,7 +254,7 @@ LL | asm!("def: nop\ndef: nop"); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:96:15 + --> $DIR/named-asm-labels.rs:94:15 | LL | asm!("def: nop; def: nop"); | ^^^ @@ -263,7 +263,7 @@ LL | asm!("def: nop; def: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:96:15 + --> $DIR/named-asm-labels.rs:94:15 | LL | asm!("def: nop; def: nop"); | ^^^ @@ -273,7 +273,7 @@ LL | asm!("def: nop; def: nop"); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:106:15 + --> $DIR/named-asm-labels.rs:104:15 | LL | asm!("fooo\u{003A} nop"); | ^^^^^^^^^^^^^^^^ @@ -282,7 +282,7 @@ LL | asm!("fooo\u{003A} nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:107:15 + --> $DIR/named-asm-labels.rs:105:15 | LL | asm!("foooo\x3A nop"); | ^^^^^^^^^^^^^ @@ -291,7 +291,7 @@ LL | asm!("foooo\x3A nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:110:15 + --> $DIR/named-asm-labels.rs:108:15 | LL | asm!("fooooo:\u{000A} nop"); | ^^^^^^ @@ -300,7 +300,7 @@ LL | asm!("fooooo:\u{000A} nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:111:15 + --> $DIR/named-asm-labels.rs:109:15 | LL | asm!("foooooo:\x0A nop"); | ^^^^^^^ @@ -309,7 +309,7 @@ LL | asm!("foooooo:\x0A nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:115:14 + --> $DIR/named-asm-labels.rs:113:14 | LL | asm!("\x41\x42\x43\x3A\x20\x6E\x6F\x70"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | asm!("\x41\x42\x43\x3A\x20\x6E\x6F\x70"); = note: the label may be declared in the expansion of a macro error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:123:13 + --> $DIR/named-asm-labels.rs:121:13 | LL | ab: nop // ab: does foo | ^^ @@ -328,7 +328,7 @@ LL | ab: nop // ab: does foo = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:144:19 + --> $DIR/named-asm-labels.rs:142:19 | LL | asm!("test_{}: nop", in(reg) 10); | ^^^^^^^ @@ -338,7 +338,7 @@ LL | asm!("test_{}: nop", in(reg) 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:146:15 + --> $DIR/named-asm-labels.rs:144:15 | LL | asm!("test_{}: nop", const 10); | ^^^^^^^ @@ -348,7 +348,7 @@ LL | asm!("test_{}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:147:15 + --> $DIR/named-asm-labels.rs:145:15 | LL | asm!("test_{}: nop", sym main); | ^^^^^^^ @@ -358,7 +358,7 @@ LL | asm!("test_{}: nop", sym main); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:148:15 + --> $DIR/named-asm-labels.rs:146:15 | LL | asm!("{}_test: nop", const 10); | ^^^^^^^ @@ -368,7 +368,7 @@ LL | asm!("{}_test: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:149:15 + --> $DIR/named-asm-labels.rs:147:15 | LL | asm!("test_{}_test: nop", const 10); | ^^^^^^^^^^^^ @@ -378,7 +378,7 @@ LL | asm!("test_{}_test: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:150:15 + --> $DIR/named-asm-labels.rs:148:15 | LL | asm!("{}: nop", const 10); | ^^ @@ -388,7 +388,7 @@ LL | asm!("{}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:152:15 + --> $DIR/named-asm-labels.rs:150:15 | LL | asm!("{uwu}: nop", uwu = const 10); | ^^^^^ @@ -398,7 +398,7 @@ LL | asm!("{uwu}: nop", uwu = const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:153:15 + --> $DIR/named-asm-labels.rs:151:15 | LL | asm!("{0}: nop", const 10); | ^^^ @@ -408,7 +408,7 @@ LL | asm!("{0}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:154:15 + --> $DIR/named-asm-labels.rs:152:15 | LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); | ^^^ @@ -418,7 +418,7 @@ LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -428,7 +428,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: the label may be declared in the expansion of a macro error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -450,7 +450,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -461,7 +461,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:171:19 + --> $DIR/named-asm-labels.rs:169:19 | LL | asm!("warned: nop"); | ^^^^^^ @@ -469,13 +469,13 @@ LL | asm!("warned: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information note: the lint level is defined here - --> $DIR/named-asm-labels.rs:169:16 + --> $DIR/named-asm-labels.rs:167:16 | LL | #[warn(named_asm_labels)] | ^^^^^^^^^^^^^^^^ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:180:17 + --> $DIR/named-asm-labels.rs:178:17 | LL | naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) | ^^^^^ @@ -484,7 +484,7 @@ LL | naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:187:20 + --> $DIR/named-asm-labels.rs:185:20 | LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } | ^^^^^ @@ -493,7 +493,7 @@ LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:195:17 + --> $DIR/named-asm-labels.rs:193:17 | LL | naked_asm!(".Laaa: nop; ret;") | ^^^^^ @@ -502,7 +502,7 @@ LL | naked_asm!(".Laaa: nop; ret;") = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:205:21 + --> $DIR/named-asm-labels.rs:203:21 | LL | naked_asm!(".Lbbb: nop; ret;") | ^^^^^ @@ -511,7 +511,7 @@ LL | naked_asm!(".Lbbb: nop; ret;") = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:214:15 + --> $DIR/named-asm-labels.rs:212:15 | LL | asm!("closure1: nop"); | ^^^^^^^^ @@ -520,7 +520,7 @@ LL | asm!("closure1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:218:15 + --> $DIR/named-asm-labels.rs:216:15 | LL | asm!("closure2: nop"); | ^^^^^^^^ @@ -529,7 +529,7 @@ LL | asm!("closure2: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:228:19 + --> $DIR/named-asm-labels.rs:226:19 | LL | asm!("closure3: nop"); | ^^^^^^^^ diff --git a/tests/ui/asm/simple_global_asm.rs b/tests/ui/asm/simple_global_asm.rs index 9b193b3e44ce6..68b0b83858e18 100644 --- a/tests/ui/asm/simple_global_asm.rs +++ b/tests/ui/asm/simple_global_asm.rs @@ -1,7 +1,6 @@ //@ run-pass //@ needs-asm-support -#![feature(naked_functions)] #![allow(dead_code)] #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] diff --git a/tests/ui/async-await/async-closures/is-not-fn.next.stderr b/tests/ui/async-await/async-closures/is-not-fn.next.stderr index 0fab1c15f27df..970970a915114 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.next.stderr +++ b/tests/ui/async-await/async-closures/is-not-fn.next.stderr @@ -1,13 +1,11 @@ -error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` +error[E0271]: type mismatch resolving `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25} == ()` --> $DIR/is-not-fn.rs:8:14 | LL | needs_fn(async || {}); - | -------- ^^^^^^^^^^^ expected `()`, found `async` closure body + | -------- ^^^^^^^^^^^ types differ | | | required by a bound introduced by this call | - = note: expected unit type `()` - found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` note: required by a bound in `needs_fn` --> $DIR/is-not-fn.rs:7:25 | diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs index e5ab4742daba1..c09ccb3fc2b09 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.rs +++ b/tests/ui/async-await/async-closures/is-not-fn.rs @@ -6,5 +6,6 @@ fn main() { fn needs_fn(x: impl FnOnce()) {} needs_fn(async || {}); - //~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()` + //[current]~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()` + //[next]~^^ ERROR type mismatch resolving `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25} == ()` } diff --git a/tests/ui/auto-instantiate.rs b/tests/ui/auto-instantiate.rs deleted file mode 100644 index 73ad5d701e182..0000000000000 --- a/tests/ui/auto-instantiate.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] -#[derive(Debug)] -struct Pair { a: T, b: U } -struct Triple { x: isize, y: isize, z: isize } - -fn f(x: T, y: U) -> Pair { return Pair {a: x, b: y}; } - -pub fn main() { - println!("{}", f(Triple {x: 3, y: 4, z: 5}, 4).a.x); - println!("{}", f(5, 6).a); -} diff --git a/tests/ui/augmented-assignments-rpass.rs b/tests/ui/binop/augmented-assignment.rs similarity index 100% rename from tests/ui/augmented-assignments-rpass.rs rename to tests/ui/binop/augmented-assignment.rs diff --git a/tests/ui/augmented-assignments-feature-gate-cross.rs b/tests/ui/binop/augmented-assignments-cross-crate.rs similarity index 72% rename from tests/ui/augmented-assignments-feature-gate-cross.rs rename to tests/ui/binop/augmented-assignments-cross-crate.rs index d402d20061779..6dbb03509884d 100644 --- a/tests/ui/augmented-assignments-feature-gate-cross.rs +++ b/tests/ui/binop/augmented-assignments-cross-crate.rs @@ -1,3 +1,5 @@ +//! Smoke test for overloaded compound assignments cross-crate. + //@ run-pass //@ aux-build:augmented_assignments.rs diff --git a/tests/ui/auxiliary/augmented_assignments.rs b/tests/ui/binop/auxiliary/augmented_assignments.rs similarity index 100% rename from tests/ui/auxiliary/augmented_assignments.rs rename to tests/ui/binop/auxiliary/augmented_assignments.rs diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/borrowck/augmented-assignments.rs similarity index 83% rename from tests/ui/augmented-assignments.rs rename to tests/ui/borrowck/augmented-assignments.rs index 35ab2d454f7b7..d717dcc7935ed 100644 --- a/tests/ui/augmented-assignments.rs +++ b/tests/ui/borrowck/augmented-assignments.rs @@ -1,3 +1,6 @@ +//! Check that overloaded compound assignment operators respect usual borrowck rules and emit +//! reasonable diagnostics. + use std::ops::AddAssign; #[derive(Clone)] diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/borrowck/augmented-assignments.stderr similarity index 88% rename from tests/ui/augmented-assignments.stderr rename to tests/ui/borrowck/augmented-assignments.stderr index a4b75cbf6e8fc..4b945cd998a14 100644 --- a/tests/ui/augmented-assignments.stderr +++ b/tests/ui/borrowck/augmented-assignments.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/augmented-assignments.rs:17:5 + --> $DIR/augmented-assignments.rs:20:5 | LL | let mut x = Int(1); | ----- binding `x` declared here @@ -10,7 +10,7 @@ LL | x; | ^ move out of `x` occurs here error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable - --> $DIR/augmented-assignments.rs:24:5 + --> $DIR/augmented-assignments.rs:27:5 | LL | y | ^ cannot borrow as mutable diff --git a/tests/ui/borrowck/static-trait-bound-lost.rs b/tests/ui/borrowck/static-trait-bound-lost.rs new file mode 100644 index 0000000000000..0288acea0f604 --- /dev/null +++ b/tests/ui/borrowck/static-trait-bound-lost.rs @@ -0,0 +1,54 @@ +// This test is a reduced version of a bug introduced during work on type-tests for Polonius. +// The underlying problem is that the 'static bound is lost for a type parameter that is +// threaded deeply enough, causing an error. +// The bug was first observed in exr-1.4.1/src/image/read/mod.rs:124:5 during perf test. + +//@ check-pass + +use std::marker::PhantomData; + +struct ReadAllLayers { + px: PhantomData, +} + +trait ReadLayers<'s> {} + +impl<'s, C> ReadLayers<'s> for ReadAllLayers where C: ReadChannels<'s> {} + +fn make_builder( + _: Set, +) -> ReadAllLayers> +where + Set: Fn(&mut Pixels), +{ + todo!() +} + +struct CollectPixels { + px: PhantomData<(SetPixel, Pixel, PixelStorage)>, +} + +impl<'s, PixelStorage, SetPixel: 's> ReadChannels<'s> + for CollectPixels +where + SetPixel: Fn(&mut PixelStorage), +{ +} + +trait ReadChannels<'s> {} + +fn from_file(_: L) +where + for<'s> L: ReadLayers<'s>, +{ +} + +pub fn read_all_rgba_layers_from_file( + set_pixel: Set, +) where + Set: Fn(&mut Pixels), +{ + from_file(make_builder(set_pixel)); // Error triggered. +} + +pub fn main() {} diff --git a/tests/ui/writing-to-immutable-vec.rs b/tests/ui/borrowck/writing-to-immutable-vec.rs similarity index 100% rename from tests/ui/writing-to-immutable-vec.rs rename to tests/ui/borrowck/writing-to-immutable-vec.rs diff --git a/tests/ui/writing-to-immutable-vec.stderr b/tests/ui/borrowck/writing-to-immutable-vec.stderr similarity index 100% rename from tests/ui/writing-to-immutable-vec.stderr rename to tests/ui/borrowck/writing-to-immutable-vec.stderr diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr index a6f8ba65ab51a..0d4c90017cf11 100644 --- a/tests/ui/coercion/issue-73886.stderr +++ b/tests/ui/coercion/issue-73886.stderr @@ -8,9 +8,14 @@ error[E0605]: non-primitive cast: `u32` as `Option<_>` --> $DIR/issue-73886.rs:4:13 | LL | let _ = 7u32 as Option<_>; - | ^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `Option<_>::from(7u32)` + | ^^^^^^^^^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = 7u32 as Option<_>; +LL + let _ = Option::<_>::from(7u32); + | error: aborting due to 2 previous errors diff --git a/tests/ui/coercion/non-primitive-cast-135412.fixed b/tests/ui/coercion/non-primitive-cast-135412.fixed new file mode 100644 index 0000000000000..5cadc9368d52e --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix + +use std::sync::Arc; + +fn main() { + let _ = Option::<_>::from(7u32); + //~^ ERROR non-primitive cast: `u32` as `Option<_>` + let _ = Arc::::from("String"); + //~^ ERROR non-primitive cast: `&'static str` as `Arc` +} diff --git a/tests/ui/coercion/non-primitive-cast-135412.rs b/tests/ui/coercion/non-primitive-cast-135412.rs new file mode 100644 index 0000000000000..67a3ef340d2f1 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.rs @@ -0,0 +1,10 @@ +//@ run-rustfix + +use std::sync::Arc; + +fn main() { + let _ = 7u32 as Option<_>; + //~^ ERROR non-primitive cast: `u32` as `Option<_>` + let _ = "String" as Arc; + //~^ ERROR non-primitive cast: `&'static str` as `Arc` +} diff --git a/tests/ui/coercion/non-primitive-cast-135412.stderr b/tests/ui/coercion/non-primitive-cast-135412.stderr new file mode 100644 index 0000000000000..7e5861f83e9c1 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.stderr @@ -0,0 +1,29 @@ +error[E0605]: non-primitive cast: `u32` as `Option<_>` + --> $DIR/non-primitive-cast-135412.rs:6:13 + | +LL | let _ = 7u32 as Option<_>; + | ^^^^^^^^^^^^^^^^^ + | + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = 7u32 as Option<_>; +LL + let _ = Option::<_>::from(7u32); + | + +error[E0605]: non-primitive cast: `&'static str` as `Arc` + --> $DIR/non-primitive-cast-135412.rs:8:13 + | +LL | let _ = "String" as Arc; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = "String" as Arc; +LL + let _ = Arc::::from("String"); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/compiletest-self-test/compile-flags-last.stderr b/tests/ui/compiletest-self-test/compile-flags-last.stderr index 72d92206e2bb4..5a48361cfc0df 100644 --- a/tests/ui/compiletest-self-test/compile-flags-last.stderr +++ b/tests/ui/compiletest-self-test/compile-flags-last.stderr @@ -1,5 +1,5 @@ error: Argument to option 'cap-lints' missing Usage: - --cap-lints LEVEL Set the most restrictive lint level. More restrictive + --cap-lints Set the most restrictive lint level. More restrictive lints are capped at this level diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index 9187de5636202..20f1a9aae70ae 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -2,7 +2,7 @@ //@ ignore-endian-big // ignore-tidy-linelength //@ normalize-stderr: "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼" -> "╾ALLOC_ID$1╼" -#![allow(invalid_value)] +#![allow(invalid_value, unnecessary_transmutes)] #![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] use std::mem; diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.rs b/tests/ui/consts/const-eval/transmute-const-promotion.rs index 1f0240d4b5ac7..840334f43c09c 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.rs +++ b/tests/ui/consts/const-eval/transmute-const-promotion.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] use std::mem; fn main() { diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.stderr b/tests/ui/consts/const-eval/transmute-const-promotion.stderr index 3603db03bb204..eb2fed091c34a 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.stderr +++ b/tests/ui/consts/const-eval/transmute-const-promotion.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/transmute-const-promotion.rs:4:37 + --> $DIR/transmute-const-promotion.rs:5:37 | LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) }; | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use diff --git a/tests/ui/consts/const-eval/transmute-const.rs b/tests/ui/consts/const-eval/transmute-const.rs index 1cfad00ca76df..fb6cb77675fa6 100644 --- a/tests/ui/consts/const-eval/transmute-const.rs +++ b/tests/ui/consts/const-eval/transmute-const.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] use std::mem; static FOO: bool = unsafe { mem::transmute(3u8) }; diff --git a/tests/ui/consts/const-eval/transmute-const.stderr b/tests/ui/consts/const-eval/transmute-const.stderr index d72289487d7bf..35a5cabaa6710 100644 --- a/tests/ui/consts/const-eval/transmute-const.stderr +++ b/tests/ui/consts/const-eval/transmute-const.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/transmute-const.rs:3:1 + --> $DIR/transmute-const.rs:4:1 | LL | static FOO: bool = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 5be444e667a1f..a5255ef95aa5b 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -3,7 +3,7 @@ //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "0x0+" -> "0x0" #![feature(never_type)] -#![allow(invalid_value)] +#![allow(invalid_value, unnecessary_transmutes)] use std::mem; diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.rs b/tests/ui/consts/const-eval/ub-wide-ptr.rs index a071a44272b4d..4e2defc1a0907 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.rs +++ b/tests/ui/consts/const-eval/ub-wide-ptr.rs @@ -1,5 +1,5 @@ // ignore-tidy-linelength -#![allow(unused)] +#![allow(unused, unnecessary_transmutes)] #![feature(ptr_metadata)] use std::{ptr, mem}; diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index 481f2ff88df67..c3bd8301d5ce4 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -2,6 +2,7 @@ //@ [no_flag] check-pass //@ [with_flag] compile-flags: -Zextra-const-ub-checks #![feature(never_type)] +#![allow(unnecessary_transmutes)] use std::mem::transmute; use std::ptr::addr_of; diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index 0100aafb6b7c6..ea3b0e70b8285 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:29:20 + --> $DIR/detect-extra-ub.rs:30:20 | LL | let _x: bool = transmute(3u8); | ^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:35:21 + --> $DIR/detect-extra-ub.rs:36:21 | LL | let _x: usize = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a pointer, but expected an integer @@ -14,7 +14,7 @@ LL | let _x: usize = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:41:28 + --> $DIR/detect-extra-ub.rs:42:28 | LL | let _x: PtrSizedEnum = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value at .: encountered a pointer, but expected an integer @@ -23,7 +23,7 @@ LL | let _x: PtrSizedEnum = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:48:30 + --> $DIR/detect-extra-ub.rs:49:30 | LL | let _x: (usize, usize) = transmute(x); | ^^^^^^^^^^^^ constructing invalid value at .0: encountered a pointer, but expected an integer @@ -32,19 +32,19 @@ LL | let _x: (usize, usize) = transmute(x); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:54:20 + --> $DIR/detect-extra-ub.rs:55:20 | LL | let _x: &u32 = transmute(&[0u8; 4]); | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 4 byte alignment but found 1) error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:62:13 + --> $DIR/detect-extra-ub.rs:63:13 | LL | let v = *addr_of!(data).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:82:16 + --> $DIR/detect-extra-ub.rs:83:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a partial pointer or a mix of pointers @@ -53,7 +53,7 @@ LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:97:16 + --> $DIR/detect-extra-ub.rs:98:16 | LL | let _val = &*slice; | ^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object diff --git a/tests/ui/consts/issue-69532.rs b/tests/ui/consts/issue-69532.rs index 285cfe7213bae..43ab1d6cca743 100644 --- a/tests/ui/consts/issue-69532.rs +++ b/tests/ui/consts/issue-69532.rs @@ -1,8 +1,8 @@ //@ run-pass const fn make_nans() -> (f64, f64, f32, f32) { - let nan1: f64 = unsafe { std::mem::transmute(0x7FF0_0001_0000_0001u64) }; - let nan2: f64 = unsafe { std::mem::transmute(0x7FF0_0000_0000_0001u64) }; + let nan1 = f64::from_bits(0x7FF0_0001_0000_0001); + let nan2 = f64::from_bits(0x7FF0_0000_0000_0001); let nan1_32 = nan1 as f32; let nan2_32 = nan2 as f32; diff --git a/tests/ui/copy-a-resource.rs b/tests/ui/copy-a-resource.rs deleted file mode 100644 index 55f2dd4ee6dde..0000000000000 --- a/tests/ui/copy-a-resource.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[derive(Debug)] -struct Foo { - i: isize, -} - -impl Drop for Foo { - fn drop(&mut self) {} -} - -fn foo(i:isize) -> Foo { - Foo { - i: i - } -} - -fn main() { - let x = foo(10); - let _y = x.clone(); - //~^ ERROR no method named `clone` found - println!("{:?}", x); -} diff --git a/tests/ui/coroutine/clone-rpit.next.stderr b/tests/ui/coroutine/clone-rpit.next.stderr deleted file mode 100644 index 213e9e908f56d..0000000000000 --- a/tests/ui/coroutine/clone-rpit.next.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0391]: cycle detected when type-checking `foo` - --> $DIR/clone-rpit.rs:13:1 - | -LL | pub fn foo<'a, 'b>() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires coroutine witness types for `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires checking if `foo::{closure#0}` contains FFI-unwind calls... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires building MIR for `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires match-checking `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires type-checking `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ - = note: ...which again requires type-checking `foo`, completing the cycle -note: cycle used when match-checking `foo` - --> $DIR/clone-rpit.rs:13:1 - | -LL | pub fn foo<'a, 'b>() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/coroutine/clone-rpit.rs b/tests/ui/coroutine/clone-rpit.rs index 66569b4f42741..3882564639b4e 100644 --- a/tests/ui/coroutine/clone-rpit.rs +++ b/tests/ui/coroutine/clone-rpit.rs @@ -1,8 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[current] check-pass -//@[next] known-bug: trait-system-refactor-initiative#82 +//@ check-pass #![feature(coroutines, coroutine_trait, coroutine_clone)] diff --git a/tests/ui/coroutine/dont-drop-stalled-generators.rs b/tests/ui/coroutine/dont-drop-stalled-generators.rs new file mode 100644 index 0000000000000..8e0c45a9773b3 --- /dev/null +++ b/tests/ui/coroutine/dont-drop-stalled-generators.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +//@ edition: 2024 + +// This test previously used the `is_copy_raw` query during +// HIR typeck, dropping the list of generators from the current +// body. This then caused a query cycle. + +struct W(*const T); + +impl Clone for W { + fn clone(&self) -> Self { W(self.0) } +} + +impl Copy for W {} + +fn main() { + let coro = async {}; + let x = W(&raw const coro); + let c = || { + let x = x; + }; +} diff --git a/tests/ui/deref-non-pointer.rs b/tests/ui/deref-patterns/deref-non-pointer.rs similarity index 100% rename from tests/ui/deref-non-pointer.rs rename to tests/ui/deref-patterns/deref-non-pointer.rs diff --git a/tests/ui/deref-non-pointer.stderr b/tests/ui/deref-patterns/deref-non-pointer.stderr similarity index 100% rename from tests/ui/deref-non-pointer.stderr rename to tests/ui/deref-patterns/deref-non-pointer.stderr diff --git a/tests/ui/deriving/auxiliary/malicious-macro.rs b/tests/ui/deriving/auxiliary/malicious-macro.rs index 6665b40a14f2b..c551b3f927c62 100644 --- a/tests/ui/deriving/auxiliary/malicious-macro.rs +++ b/tests/ui/deriving/auxiliary/malicious-macro.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +//@ edition: 2024 extern crate proc_macro; diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index 6170250992ce2..8178f54b2aab1 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -18,13 +18,16 @@ LL | where LL | T: AsExpression, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` -error[E0271]: type mismatch resolving `Integer == Text` +error[E0277]: the trait bound `&str: AsExpression` is not satisfied --> $DIR/as_expression.rs:56:5 | LL | SelectInt.check("bar"); - | ^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `AsExpression` is not implemented for `&str` + | + = help: the trait `AsExpression` is not implemented for `&str` + but trait `AsExpression` is implemented for it + = help: for that trait implementation, expected `Text`, found `Integer` error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs index 673adb82870d8..86f39e43484e7 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs @@ -55,5 +55,5 @@ impl Foo for T where T: Expression {} fn main() { SelectInt.check("bar"); //~^ ERROR the trait bound `&str: AsExpression` is not satisfied - //[next]~| ERROR type mismatch + //[next]~| ERROR the trait bound `&str: AsExpression` is not satisfied } diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed index 78cf421cfbf34..71158cb806213 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.fixed +++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed @@ -24,7 +24,7 @@ //@ [e2024] edition: 2024 //@ run-pass -#![feature(let_chains)] +#![cfg_attr(e2021, feature(let_chains))] #![cfg_attr(e2021, warn(rust_2024_compatibility))] fn t_bindings() { diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs index 78c75a9449f17..0492b3a4db7f9 100644 --- a/tests/ui/drop/drop-order-comparisons.rs +++ b/tests/ui/drop/drop-order-comparisons.rs @@ -24,7 +24,7 @@ //@ [e2024] edition: 2024 //@ run-pass -#![feature(let_chains)] +#![cfg_attr(e2021, feature(let_chains))] #![cfg_attr(e2021, warn(rust_2024_compatibility))] fn t_bindings() { diff --git a/tests/ui/drop/drop_order.rs b/tests/ui/drop/drop_order.rs index d1a5b9bc5e268..b96e55a2535a6 100644 --- a/tests/ui/drop/drop_order.rs +++ b/tests/ui/drop/drop_order.rs @@ -2,9 +2,10 @@ //@ compile-flags: -Z validate-mir //@ revisions: edition2021 edition2024 //@ [edition2021] edition: 2021 +//@ [edition2024] compile-flags: -Z lint-mir //@ [edition2024] edition: 2024 -#![feature(let_chains)] +#![cfg_attr(edition2021, feature(let_chains))] use std::cell::RefCell; use std::convert::TryInto; diff --git a/tests/ui/drop/drop_order_if_let_rescope.rs b/tests/ui/drop/drop_order_if_let_rescope.rs index 7445e3a6a5f1f..27bced5fa62fd 100644 --- a/tests/ui/drop/drop_order_if_let_rescope.rs +++ b/tests/ui/drop/drop_order_if_let_rescope.rs @@ -1,8 +1,6 @@ //@ run-pass //@ edition:2024 -//@ compile-flags: -Z validate-mir - -#![feature(let_chains)] +//@ compile-flags: -Z validate-mir -Z lint-mir use std::cell::RefCell; use std::convert::TryInto; diff --git a/tests/ui/drop/issue-100276.rs b/tests/ui/drop/issue-100276.rs index b44710e7c3f21..5d212b3a0a954 100644 --- a/tests/ui/drop/issue-100276.rs +++ b/tests/ui/drop/issue-100276.rs @@ -1,6 +1,11 @@ //@ check-pass //@ compile-flags: -Z validate-mir -#![feature(let_chains)] +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] compile-flags: -Z lint-mir +//@ [edition2024] edition: 2024 + +#![cfg_attr(edition2021, feature(let_chains))] fn let_chains(entry: std::io::Result) { if let Ok(entry) = entry diff --git a/tests/ui/expr/if/attrs/let-chains-attr.rs b/tests/ui/expr/if/attrs/let-chains-attr.rs index 2cf1b169f0698..7b74b17784e80 100644 --- a/tests/ui/expr/if/attrs/let-chains-attr.rs +++ b/tests/ui/expr/if/attrs/let-chains-attr.rs @@ -1,6 +1,5 @@ //@ check-pass - -#![feature(let_chains)] +//@ edition:2024 #[cfg(false)] fn foo() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.rs b/tests/ui/feature-gates/feature-gate-concat_idents.rs index 68caf3d71e907..4fc3b69159733 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + const XY_1: i32 = 10; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.stderr b/tests/ui/feature-gates/feature-gate-concat_idents.stderr index d0f4fe62d048f..6399424eecd8b 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents.rs:5:13 + --> $DIR/feature-gate-concat_idents.rs:7:13 | LL | let a = concat_idents!(X, Y_1); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let a = concat_idents!(X, Y_1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents.rs:6:13 + --> $DIR/feature-gate-concat_idents.rs:8:13 | LL | let b = concat_idents!(X, Y_2); | ^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.rs b/tests/ui/feature-gates/feature-gate-concat_idents2.rs index 9660ffeafa518..bc2b4f7cddf99 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents2.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents2.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + fn main() { concat_idents!(a, b); //~ ERROR `concat_idents` is not stable enough //~| ERROR cannot find value `ab` in this scope diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr index b42a1d999e4d9..a770c1a348b5d 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents2.rs:2:5 + --> $DIR/feature-gate-concat_idents2.rs:4:5 | LL | concat_idents!(a, b); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | concat_idents!(a, b); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0425]: cannot find value `ab` in this scope - --> $DIR/feature-gate-concat_idents2.rs:2:5 + --> $DIR/feature-gate-concat_idents2.rs:4:5 | LL | concat_idents!(a, b); | ^^^^^^^^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.rs b/tests/ui/feature-gates/feature-gate-concat_idents3.rs index 81710fd9fb041..d4a0d2e6bb0e4 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents3.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents3.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + const XY_1: i32 = 10; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr index b186601d0ed68..7d929322bc06e 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents3.rs:5:20 + --> $DIR/feature-gate-concat_idents3.rs:7:20 | LL | assert_eq!(10, concat_idents!(X, Y_1)); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | assert_eq!(10, concat_idents!(X, Y_1)); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents3.rs:6:20 + --> $DIR/feature-gate-concat_idents3.rs:8:20 | LL | assert_eq!(20, concat_idents!(X, Y_2)); | ^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.rs b/tests/ui/feature-gates/feature-gate-naked_functions.rs deleted file mode 100644 index d940decd561e9..0000000000000 --- a/tests/ui/feature-gates/feature-gate-naked_functions.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ needs-asm-support - -use std::arch::naked_asm; -//~^ ERROR use of unstable library feature `naked_functions` - -#[naked] //~ ERROR unsafe attribute used without unsafe -//~^ ERROR the `#[naked]` attribute is an experimental feature -extern "C" fn naked() { - naked_asm!("") - //~^ ERROR use of unstable library feature `naked_functions` -} - -#[naked] //~ ERROR unsafe attribute used without unsafe -//~^ ERROR the `#[naked]` attribute is an experimental feature -extern "C" fn naked_2() -> isize { - naked_asm!("") - //~^ ERROR use of unstable library feature `naked_functions` -} - -fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.stderr b/tests/ui/feature-gates/feature-gate-naked_functions.stderr deleted file mode 100644 index ea765db7d946e..0000000000000 --- a/tests/ui/feature-gates/feature-gate-naked_functions.stderr +++ /dev/null @@ -1,75 +0,0 @@ -error[E0658]: use of unstable library feature `naked_functions` - --> $DIR/feature-gate-naked_functions.rs:9:5 - | -LL | naked_asm!("") - | ^^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `naked_functions` - --> $DIR/feature-gate-naked_functions.rs:16:5 - | -LL | naked_asm!("") - | ^^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: unsafe attribute used without unsafe - --> $DIR/feature-gate-naked_functions.rs:6:3 - | -LL | #[naked] - | ^^^^^ usage of unsafe attribute - | -help: wrap the attribute in `unsafe(...)` - | -LL | #[unsafe(naked)] - | +++++++ + - -error: unsafe attribute used without unsafe - --> $DIR/feature-gate-naked_functions.rs:13:3 - | -LL | #[naked] - | ^^^^^ usage of unsafe attribute - | -help: wrap the attribute in `unsafe(...)` - | -LL | #[unsafe(naked)] - | +++++++ + - -error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:6:1 - | -LL | #[naked] - | ^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:13:1 - | -LL | #[naked] - | ^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `naked_functions` - --> $DIR/feature-gate-naked_functions.rs:3:5 - | -LL | use std::arch::naked_asm; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 7 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs b/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs index cc5b4f0e88b42..d16c6ccd4c331 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs +++ b/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs @@ -1,7 +1,7 @@ //@ needs-asm-support //@ only-x86_64 -#![feature(naked_functions, rust_cold_cc)] +#![feature(rust_cold_cc)] use std::arch::naked_asm; diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs index b2e102f1db4b6..1fee3e7dcd1a9 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs +++ b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs @@ -1,8 +1,6 @@ //@ needs-asm-support //@ only-x86_64 -#![feature(naked_functions)] - use std::arch::naked_asm; #[unsafe(naked)] diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr index b0592d08046f5..8e601a14753bb 100644 --- a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr +++ b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions - --> $DIR/feature-gate-naked_functions_target_feature.rs:9:1 + --> $DIR/feature-gate-naked_functions_target_feature.rs:7:1 | LL | #[target_feature(enable = "avx2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/generics/single-colon-path-not-const-generics.stderr b/tests/ui/generics/single-colon-path-not-const-generics.stderr index 9eb62de275614..163ea4bbda6cd 100644 --- a/tests/ui/generics/single-colon-path-not-const-generics.stderr +++ b/tests/ui/generics/single-colon-path-not-const-generics.stderr @@ -1,13 +1,15 @@ error: path separator must be a double colon --> $DIR/single-colon-path-not-const-generics.rs:8:18 | +LL | pub struct Foo { + | --- while parsing this struct LL | a: Vec, | ^ | help: use a double colon instead | LL | a: Vec, - | + + | + error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-2.rs b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-2.rs new file mode 100644 index 0000000000000..e8329e3694d73 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-2.rs @@ -0,0 +1,12 @@ +trait Foo { + fn rpitit() -> impl Sized; +} + +// Ensure that we don't try to probe the name of the RPITIT when looking for +// fixes to suggest for the redundant generic below. + +fn test>() {} +//~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied +//~| ERROR associated type `Assoc` not found for `Foo` + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-2.stderr b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-2.stderr new file mode 100644 index 0000000000000..1058ef01f323f --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-2.stderr @@ -0,0 +1,24 @@ +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/dont-probe-missing-item-name-2.rs:8:12 + | +LL | fn test>() {} + | ^^^------------------ help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: trait defined here, with 0 generic parameters + --> $DIR/dont-probe-missing-item-name-2.rs:1:7 + | +LL | trait Foo { + | ^^^ + +error[E0220]: associated type `Assoc` not found for `Foo` + --> $DIR/dont-probe-missing-item-name-2.rs:8:21 + | +LL | fn test>() {} + | ^^^^^ associated type `Assoc` not found + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0107, E0220. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-3.rs b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-3.rs new file mode 100644 index 0000000000000..db39bb354379a --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-3.rs @@ -0,0 +1,11 @@ +trait Trait { + fn method() -> impl Sized; +} + +// Ensure that we don't try to probe the name of the RPITIT when looking for +// fixes to suggest for the missing associated type's trait path below. + +fn foo() -> i32::Assoc {} +//~^ ERROR ambiguous associated type + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-3.stderr b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-3.stderr new file mode 100644 index 0000000000000..f30d0228a3c86 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-3.stderr @@ -0,0 +1,15 @@ +error[E0223]: ambiguous associated type + --> $DIR/dont-probe-missing-item-name-3.rs:8:13 + | +LL | fn foo() -> i32::Assoc {} + | ^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `Assoc` implemented for `i32`, you could use the fully-qualified path + | +LL - fn foo() -> i32::Assoc {} +LL + fn foo() -> ::Assoc {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.rs b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.rs new file mode 100644 index 0000000000000..1ee3bfd123397 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.rs @@ -0,0 +1,23 @@ +trait ServerFn { + type Output; + fn run_body() -> impl Sized; +} +struct MyServerFn {} + +macro_rules! f { + () => { + impl ServerFn for MyServerFn { + type Output = (); + fn run_body() -> impl Sized {} + } + }; +} + +f! {} + +fn problem>(_: T) {} + +fn main() { + problem(MyServerFn {}); + //~^ ERROR type mismatch resolving `::Output == i64` +} diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.stderr b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.stderr new file mode 100644 index 0000000000000..b4c022d352193 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.stderr @@ -0,0 +1,26 @@ +error[E0271]: type mismatch resolving `::Output == i64` + --> $DIR/dont-probe-missing-item-name-4.rs:21:13 + | +LL | problem(MyServerFn {}); + | ------- ^^^^^^^^^^^^^ type mismatch resolving `::Output == i64` + | | + | required by a bound introduced by this call + | +note: expected this to be `i64` + --> $DIR/dont-probe-missing-item-name-4.rs:10:27 + | +LL | type Output = (); + | ^^ +... +LL | f! {} + | ----- in this macro invocation +note: required by a bound in `problem` + --> $DIR/dont-probe-missing-item-name-4.rs:18:24 + | +LL | fn problem>(_: T) {} + | ^^^^^^^^^^^^ required by this bound in `problem` + = note: this error originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/inference/auto-instantiate.rs b/tests/ui/inference/auto-instantiate.rs new file mode 100644 index 0000000000000..bf43330a0b77c --- /dev/null +++ b/tests/ui/inference/auto-instantiate.rs @@ -0,0 +1,28 @@ +//! Check that type parameters in generic function arg position and in "nested" return type position +//! can be inferred on an invocation of the generic function. +//! +//! See . + +//@ run-pass + +#![allow(dead_code)] +#[derive(Debug)] +struct Pair { + a: T, + b: U, +} + +struct Triple { + x: isize, + y: isize, + z: isize, +} + +fn f(x: T, y: U) -> Pair { + return Pair { a: x, b: y }; +} + +pub fn main() { + println!("{}", f(Triple {x: 3, y: 4, z: 5}, 4).a.x); + println!("{}", f(5, 6).a); +} diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs b/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs new file mode 100644 index 0000000000000..a96eeb0a5ce08 --- /dev/null +++ b/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --emit +//@ error-pattern: Argument to option 'emit' missing diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr b/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr new file mode 100644 index 0000000000000..9e4486a272f9d --- /dev/null +++ b/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr @@ -0,0 +1,16 @@ +error: Argument to option 'emit' missing + Usage: + --emit [=] + Comma separated list of types of output for the + compiler to emit. + Each TYPE has the default FILE name: + * asm - CRATE_NAME.s + * llvm-bc - CRATE_NAME.bc + * dep-info - CRATE_NAME.d + * link - (platform and crate-type dependent) + * llvm-ir - CRATE_NAME.ll + * metadata - libCRATE_NAME.rmeta + * mir - CRATE_NAME.mir + * obj - CRATE_NAME.o + * thin-link-bitcode - CRATE_NAME.indexing.o + diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index fd2a36e761bef..3048a59d0d00c 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -1,6 +1,7 @@ error: Argument to option 'print' missing Usage: - --print INFO[=FILE] Compiler information to print on stdout (or to a file) + --print [=] + Compiler information to print on stdout (or to a file) INFO may be one of - (all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models). + . diff --git a/tests/ui/issues/issue-25746-bool-transmute.rs b/tests/ui/issues/issue-25746-bool-transmute.rs index f8cdc980daa48..046dcf83f62d6 100644 --- a/tests/ui/issues/issue-25746-bool-transmute.rs +++ b/tests/ui/issues/issue-25746-bool-transmute.rs @@ -1,4 +1,5 @@ //@ run-pass +#![allow(unnecessary_transmutes)] use std::mem::transmute; fn main() { diff --git a/tests/ui/issues/issue-32950.rs b/tests/ui/issues/issue-32950.rs index 27d68a11c1f16..b51ac2967768a 100644 --- a/tests/ui/issues/issue-32950.rs +++ b/tests/ui/issues/issue-32950.rs @@ -1,4 +1,5 @@ #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated #[derive(Debug)] struct Baz( diff --git a/tests/ui/issues/issue-32950.stderr b/tests/ui/issues/issue-32950.stderr index 3cdf35af1d866..38a82542f8964 100644 --- a/tests/ui/issues/issue-32950.stderr +++ b/tests/ui/issues/issue-32950.stderr @@ -1,11 +1,11 @@ error: `derive` cannot be used on items with type macros - --> $DIR/issue-32950.rs:5:5 + --> $DIR/issue-32950.rs:6:5 | LL | concat_idents!(Foo, Bar) | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0412]: cannot find type `FooBar` in this scope - --> $DIR/issue-32950.rs:5:5 + --> $DIR/issue-32950.rs:6:5 | LL | concat_idents!(Foo, Bar) | ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/issues/issue-50403.rs b/tests/ui/issues/issue-50403.rs index ab22aff26d99c..f14958afc34dc 100644 --- a/tests/ui/issues/issue-50403.rs +++ b/tests/ui/issues/issue-50403.rs @@ -1,4 +1,5 @@ #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated fn main() { let x = concat_idents!(); //~ ERROR `concat_idents!()` takes 1 or more arguments diff --git a/tests/ui/issues/issue-50403.stderr b/tests/ui/issues/issue-50403.stderr index 193d815d5195f..e7dd05bb0183d 100644 --- a/tests/ui/issues/issue-50403.stderr +++ b/tests/ui/issues/issue-50403.stderr @@ -1,5 +1,5 @@ error: `concat_idents!()` takes 1 or more arguments - --> $DIR/issue-50403.rs:4:13 + --> $DIR/issue-50403.rs:5:13 | LL | let x = concat_idents!(); | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lazy-and-or.rs b/tests/ui/lazy-and-or.rs deleted file mode 100644 index f9dbeb68959a3..0000000000000 --- a/tests/ui/lazy-and-or.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass - -fn incr(x: &mut isize) -> bool { *x += 1; assert!((false)); return false; } - -pub fn main() { - let x = 1 == 2 || 3 == 3; - assert!((x)); - let mut y: isize = 10; - println!("{}", x || incr(&mut y)); - assert_eq!(y, 10); - if true && x { assert!((true)); } else { assert!((false)); } -} diff --git a/tests/ui/linking/cdylib-no-mangle.rs b/tests/ui/linking/cdylib-no-mangle.rs new file mode 100644 index 0000000000000..f442c3f584d6e --- /dev/null +++ b/tests/ui/linking/cdylib-no-mangle.rs @@ -0,0 +1,20 @@ +//@ only-apple +//@ build-fail +//@ dont-check-compiler-stderr +//@ dont-check-compiler-stdout + +// Regression test for . +// Functions in the dynamic library marked with no_mangle should not be GC-ed. + +#![crate_type = "cdylib"] + +unsafe extern "C" { + unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize; +} + +#[unsafe(no_mangle)] +pub unsafe fn function_marked_with_no_mangle() { + println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +} + +//~? ERROR linking diff --git a/tests/ui/linking/executable-no-mangle-strip.rs b/tests/ui/linking/executable-no-mangle-strip.rs new file mode 100644 index 0000000000000..cc283dc53ee3a --- /dev/null +++ b/tests/ui/linking/executable-no-mangle-strip.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ ignore-windows-gnu: only statics marked with used can be GC-ed on windows-gnu + +// Regression test for . +// Functions in the binary marked with no_mangle should be GC-ed if they +// are not indirectly referenced by main. + +#![feature(used_with_arg)] + +#[cfg_attr(windows, link(name = "this_lib_does_not_exist", kind = "raw-dylib"))] +unsafe extern "C" { + unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize; +} + +#[unsafe(no_mangle)] +pub unsafe fn function_marked_with_no_mangle() { + println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +} + +#[used(compiler)] +pub static FUNCTION_MARKED_WITH_USED: unsafe fn() = || { + println!("FUNCTION_MARKED_WITH_USED = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +}; + +fn main() { + println!("MAIN"); +} diff --git a/tests/ui/lint/inline-exported.rs b/tests/ui/lint/inline-exported.rs index 69e322ef513ae..6a23cd58236ff 100644 --- a/tests/ui/lint/inline-exported.rs +++ b/tests/ui/lint/inline-exported.rs @@ -2,9 +2,7 @@ //! because `#[inline]` is ignored for such functions. #![crate_type = "lib"] - #![feature(linkage)] -#![feature(naked_functions)] #![deny(unused_attributes)] #[inline] diff --git a/tests/ui/lint/inline-exported.stderr b/tests/ui/lint/inline-exported.stderr index dcf63cc4090e4..05a2bda24067e 100644 --- a/tests/ui/lint/inline-exported.stderr +++ b/tests/ui/lint/inline-exported.stderr @@ -1,18 +1,18 @@ error: `#[inline]` is ignored on externally exported functions - --> $DIR/inline-exported.rs:10:1 + --> $DIR/inline-exported.rs:8:1 | LL | #[inline] | ^^^^^^^^^ | = help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]` note: the lint level is defined here - --> $DIR/inline-exported.rs:8:9 + --> $DIR/inline-exported.rs:6:9 | LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: `#[inline]` is ignored on externally exported functions - --> $DIR/inline-exported.rs:15:1 + --> $DIR/inline-exported.rs:13:1 | LL | #[inline] | ^^^^^^^^^ @@ -20,7 +20,7 @@ LL | #[inline] = help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]` error: `#[inline]` is ignored on externally exported functions - --> $DIR/inline-exported.rs:20:1 + --> $DIR/inline-exported.rs:18:1 | LL | #[inline] | ^^^^^^^^^ diff --git a/tests/ui/lint/invalid_null_args.rs b/tests/ui/lint/invalid_null_args.rs index 7948f0d86d09f..f40f06a0d3624 100644 --- a/tests/ui/lint/invalid_null_args.rs +++ b/tests/ui/lint/invalid_null_args.rs @@ -1,19 +1,19 @@ // check-fail // run-rustfix +#![allow(unnecessary_transmutes)] -use std::ptr; -use std::mem; +use std::{mem, ptr}; unsafe fn null_ptr() { ptr::write( - //~^ ERROR calling this function with a null pointer is undefined behavior + //~^ ERROR calling this function with a null pointer is undefined behavior ptr::null_mut() as *mut u32, mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), ); let null_ptr = ptr::null_mut(); ptr::write( - //~^ ERROR calling this function with a null pointer is undefined behavior + //~^ ERROR calling this function with a null pointer is undefined behavior null_ptr as *mut u32, mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), ); @@ -38,10 +38,10 @@ unsafe fn null_ptr() { ptr::copy_nonoverlapping::(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0); //~^ ERROR calling this function with a null pointer is undefined behavior ptr::copy_nonoverlapping::( - //~^ ERROR calling this function with a null pointer is undefined behavior + //~^ ERROR calling this function with a null pointer is undefined behavior ptr::NonNull::dangling().as_ptr(), ptr::null_mut(), - 0 + 0, ); #[derive(Copy, Clone)] diff --git a/tests/ui/lint/invalid_null_args.stderr b/tests/ui/lint/invalid_null_args.stderr index f95bc2afa829e..11c6270cfb78e 100644 --- a/tests/ui/lint/invalid_null_args.stderr +++ b/tests/ui/lint/invalid_null_args.stderr @@ -117,7 +117,7 @@ LL | | LL | | ptr::NonNull::dangling().as_ptr(), LL | | ptr::null_mut(), | | --------------- null pointer originates from here -LL | | 0 +LL | | 0, LL | | ); | |_____^ | diff --git a/tests/ui/lint/issue-121070-let-range.rs b/tests/ui/lint/issue-121070-let-range.rs index 1f575cfaca556..82878587f8d7b 100644 --- a/tests/ui/lint/issue-121070-let-range.rs +++ b/tests/ui/lint/issue-121070-let-range.rs @@ -1,6 +1,6 @@ //@ check-pass +//@ edition:2024 -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] fn main() { let _a = 0..1; diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 0d19d5b534713..b2ef27b833bdb 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -2,6 +2,8 @@ #![deny(improper_ctypes)] #![feature(ptr_internals)] #![feature(transparent_unions)] +#![feature(repr128)] +#![allow(incomplete_features)] use std::num; @@ -40,6 +42,20 @@ enum Isize { C, } +#[repr(u128)] +enum U128 { + A, + B, + C, +} + +#[repr(i128)] +enum I128 { + A, + B, + C, +} + #[repr(transparent)] struct TransparentStruct(T, std::marker::PhantomData); @@ -71,6 +87,8 @@ extern "C" { fn repr_c(x: ReprC); fn repr_u8(x: U8); fn repr_isize(x: Isize); + fn repr_u128(x: U128); //~ ERROR `extern` block uses type `U128` + fn repr_i128(x: I128); //~ ERROR `extern` block uses type `I128` fn option_ref(x: Option<&'static u8>); fn option_fn(x: Option); fn option_nonnull(x: Option>); diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index a491bd1960563..d5fc844f75607 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:68:14 + --> $DIR/lint-ctypes-enum.rs:84:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:9:1 + --> $DIR/lint-ctypes-enum.rs:11:1 | LL | enum U { | ^^^^^^ @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:69:14 + --> $DIR/lint-ctypes-enum.rs:85:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:12:1 + --> $DIR/lint-ctypes-enum.rs:14:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:70:14 + --> $DIR/lint-ctypes-enum.rs:86:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,39 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:16:1 + --> $DIR/lint-ctypes-enum.rs:18:1 | LL | enum T { | ^^^^^^ +error: `extern` block uses type `U128`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:90:21 + | +LL | fn repr_u128(x: U128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI +note: the type is defined here + --> $DIR/lint-ctypes-enum.rs:46:1 + | +LL | enum U128 { + | ^^^^^^^^^ + +error: `extern` block uses type `I128`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:91:21 + | +LL | fn repr_i128(x: I128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI +note: the type is defined here + --> $DIR/lint-ctypes-enum.rs:53:1 + | +LL | enum I128 { + | ^^^^^^^^^ + error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:82:31 + --> $DIR/lint-ctypes-enum.rs:100:31 | LL | fn option_nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -54,7 +80,7 @@ LL | fn option_nonzero_u128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:89:31 + --> $DIR/lint-ctypes-enum.rs:107:31 | LL | fn option_nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -62,7 +88,7 @@ LL | fn option_nonzero_i128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:94:36 + --> $DIR/lint-ctypes-enum.rs:112:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -71,7 +97,7 @@ LL | fn option_transparent_union(x: Option = note: enum has no representation hint error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:96:28 + --> $DIR/lint-ctypes-enum.rs:114:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +106,7 @@ LL | fn option_repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:97:21 + --> $DIR/lint-ctypes-enum.rs:115:21 | LL | fn option_u8(x: Option); | ^^^^^^^^^^ not FFI-safe @@ -89,7 +115,7 @@ LL | fn option_u8(x: Option); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:107:33 + --> $DIR/lint-ctypes-enum.rs:125:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -97,7 +123,7 @@ LL | fn result_nonzero_u128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:114:33 + --> $DIR/lint-ctypes-enum.rs:132:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -105,7 +131,7 @@ LL | fn result_nonzero_i128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:119:38 + --> $DIR/lint-ctypes-enum.rs:137:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -114,7 +140,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:121:30 + --> $DIR/lint-ctypes-enum.rs:139:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +149,7 @@ LL | fn result_repr_rust_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:125:51 + --> $DIR/lint-ctypes-enum.rs:143:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +158,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:127:53 + --> $DIR/lint-ctypes-enum.rs:145:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -141,7 +167,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result = note: enum has no representation hint error: `extern` block uses type `Result, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:129:51 + --> $DIR/lint-ctypes-enum.rs:147:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -150,7 +176,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:132:49 + --> $DIR/lint-ctypes-enum.rs:150:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +185,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result, Fi = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:134:30 + --> $DIR/lint-ctypes-enum.rs:152:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -168,7 +194,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:145:33 + --> $DIR/lint-ctypes-enum.rs:163:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +202,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:152:33 + --> $DIR/lint-ctypes-enum.rs:170:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -184,7 +210,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:157:38 + --> $DIR/lint-ctypes-enum.rs:175:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,7 +219,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:159:30 + --> $DIR/lint-ctypes-enum.rs:177:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +228,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust>>); = note: enum has no representation hint error: `extern` block uses type `Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:163:51 + --> $DIR/lint-ctypes-enum.rs:181:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -211,7 +237,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:165:53 + --> $DIR/lint-ctypes-enum.rs:183:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -220,7 +246,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:167:51 + --> $DIR/lint-ctypes-enum.rs:185:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -229,7 +255,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:170:49 + --> $DIR/lint-ctypes-enum.rs:188:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,7 +264,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:172:30 + --> $DIR/lint-ctypes-enum.rs:190:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -247,7 +273,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:174:27 + --> $DIR/lint-ctypes-enum.rs:192:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe @@ -255,5 +281,5 @@ LL | fn result_unit_t_e(x: Result<(), ()>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/lint/wasm_c_abi_transition.rs b/tests/ui/lint/wasm_c_abi_transition.rs index 6a933a0de036f..411772ae890b7 100644 --- a/tests/ui/lint/wasm_c_abi_transition.rs +++ b/tests/ui/lint/wasm_c_abi_transition.rs @@ -3,7 +3,7 @@ //@ add-core-stubs //@ build-fail -#![feature(no_core)] +#![feature(no_core, repr_simd)] #![no_core] #![crate_type = "lib"] #![deny(wasm_c_abi)] @@ -45,3 +45,13 @@ pub fn call_other_fun(x: MyType) { pub struct MyZstType; #[allow(improper_ctypes_definitions)] pub extern "C" fn zst_safe(_x: (), _y: MyZstType) {} + +// The old and new wasm ABI treats simd types like `v128` the same way, so no +// wasm_c_abi warning should be emitted. +#[repr(simd)] +#[allow(non_camel_case_types)] +pub struct v128([i32; 4]); +#[target_feature(enable = "simd128")] +pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } +//~^ WARN `extern` fn uses type `v128`, which is not FFI-safe +//~| WARN `extern` fn uses type `v128`, which is not FFI-safe diff --git a/tests/ui/lint/wasm_c_abi_transition.stderr b/tests/ui/lint/wasm_c_abi_transition.stderr index 389710d5cb3a2..b4526bf8d6873 100644 --- a/tests/ui/lint/wasm_c_abi_transition.stderr +++ b/tests/ui/lint/wasm_c_abi_transition.stderr @@ -1,3 +1,32 @@ +warning: `extern` fn uses type `v128`, which is not FFI-safe + --> $DIR/wasm_c_abi_transition.rs:55:35 + | +LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } + | ^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/wasm_c_abi_transition.rs:53:1 + | +LL | pub struct v128([i32; 4]); + | ^^^^^^^^^^^^^^^ + = note: `#[warn(improper_ctypes_definitions)]` on by default + +warning: `extern` fn uses type `v128`, which is not FFI-safe + --> $DIR/wasm_c_abi_transition.rs:55:44 + | +LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } + | ^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/wasm_c_abi_transition.rs:53:1 + | +LL | pub struct v128([i32; 4]); + | ^^^^^^^^^^^^^^^ + error: this function definition involves an argument of type `MyType` which is affected by the wasm ABI transition --> $DIR/wasm_c_abi_transition.rs:18:1 | @@ -33,7 +62,7 @@ LL | unsafe { other_fun(x) } = note: for more information, see issue #138762 = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 2 warnings emitted Future incompatibility report: Future breakage diagnostic: error: this function definition involves an argument of type `MyType` which is affected by the wasm ABI transition diff --git a/tests/ui/list.rs b/tests/ui/list.rs deleted file mode 100644 index 443c4c9f28f88..0000000000000 --- a/tests/ui/list.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] - -enum list { #[allow(dead_code)] cons(isize, Box), nil, } - -pub fn main() { - list::cons(10, Box::new(list::cons(11, Box::new(list::cons(12, Box::new(list::nil)))))); -} diff --git a/tests/ui/macros/macros-nonfatal-errors.rs b/tests/ui/macros/macros-nonfatal-errors.rs index 79beffbe986e3..091d64ea5d9eb 100644 --- a/tests/ui/macros/macros-nonfatal-errors.rs +++ b/tests/ui/macros/macros-nonfatal-errors.rs @@ -5,6 +5,7 @@ #![feature(trace_macros, concat_idents)] #![feature(stmt_expr_attributes)] +#![expect(deprecated)] // concat_idents is deprecated use std::arch::asm; diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index 44194b506a43a..2f990cb24e2bf 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -1,5 +1,5 @@ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:13:5 + --> $DIR/macros-nonfatal-errors.rs:14:5 | LL | #[default] | ^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:18:36 + --> $DIR/macros-nonfatal-errors.rs:19:36 | LL | struct DefaultInnerAttrTupleStruct(#[default] ()); | ^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | struct DefaultInnerAttrTupleStruct(#[default] ()); = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:22:1 + --> $DIR/macros-nonfatal-errors.rs:23:1 | LL | #[default] | ^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:26:1 + --> $DIR/macros-nonfatal-errors.rs:27:1 | LL | #[default] | ^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:36:11 + --> $DIR/macros-nonfatal-errors.rs:37:11 | LL | Foo = #[default] 0, | ^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Foo = #[default] 0, = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:37:14 + --> $DIR/macros-nonfatal-errors.rs:38:14 | LL | Bar([u8; #[default] 1]), | ^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | Bar([u8; #[default] 1]), = help: consider a manual implementation of `Default` error[E0665]: `#[derive(Default)]` on enum with no `#[default]` - --> $DIR/macros-nonfatal-errors.rs:42:10 + --> $DIR/macros-nonfatal-errors.rs:43:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -67,7 +67,7 @@ LL | #[default] Bar, | ++++++++++ error[E0665]: `#[derive(Default)]` on enum with no `#[default]` - --> $DIR/macros-nonfatal-errors.rs:48:10 + --> $DIR/macros-nonfatal-errors.rs:49:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -78,7 +78,7 @@ LL | | } | |_- this enum needs a unit variant marked with `#[default]` error: multiple declared defaults - --> $DIR/macros-nonfatal-errors.rs:54:10 + --> $DIR/macros-nonfatal-errors.rs:55:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -95,7 +95,7 @@ LL | Baz, = note: only one variant can be default error: `#[default]` attribute does not accept a value - --> $DIR/macros-nonfatal-errors.rs:66:5 + --> $DIR/macros-nonfatal-errors.rs:67:5 | LL | #[default = 1] | ^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL | #[default = 1] = help: try using `#[default]` error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:74:5 + --> $DIR/macros-nonfatal-errors.rs:75:5 | LL | #[default] | ---------- `#[default]` used here @@ -114,13 +114,13 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing this - --> $DIR/macros-nonfatal-errors.rs:73:5 + --> $DIR/macros-nonfatal-errors.rs:74:5 | LL | #[default] | ^^^^^^^^^^ error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:84:5 + --> $DIR/macros-nonfatal-errors.rs:85:5 | LL | #[default] | ---------- `#[default]` used here @@ -132,7 +132,7 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing these - --> $DIR/macros-nonfatal-errors.rs:81:5 + --> $DIR/macros-nonfatal-errors.rs:82:5 | LL | #[default] | ^^^^^^^^^^ @@ -142,7 +142,7 @@ LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:91:5 + --> $DIR/macros-nonfatal-errors.rs:92:5 | LL | Foo {}, | ^^^ @@ -150,7 +150,7 @@ LL | Foo {}, = help: consider a manual implementation of `Default` error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:99:5 + --> $DIR/macros-nonfatal-errors.rs:100:5 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -160,37 +160,37 @@ LL | Foo, = help: consider a manual implementation of `Default` error: asm template must be a string literal - --> $DIR/macros-nonfatal-errors.rs:104:10 + --> $DIR/macros-nonfatal-errors.rs:105:10 | LL | asm!(invalid); | ^^^^^^^ error: `concat_idents!()` requires ident args - --> $DIR/macros-nonfatal-errors.rs:107:5 + --> $DIR/macros-nonfatal-errors.rs:108:5 | LL | concat_idents!("not", "idents"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:109:17 + --> $DIR/macros-nonfatal-errors.rs:110:17 | LL | option_env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:110:10 + --> $DIR/macros-nonfatal-errors.rs:111:10 | LL | env!(invalid); | ^^^^^^^ error: `env!()` takes 1 or 2 arguments - --> $DIR/macros-nonfatal-errors.rs:111:5 + --> $DIR/macros-nonfatal-errors.rs:112:5 | LL | env!(foo, abr, baz); | ^^^^^^^^^^^^^^^^^^^ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time - --> $DIR/macros-nonfatal-errors.rs:112:5 + --> $DIR/macros-nonfatal-errors.rs:113:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +198,7 @@ LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); = help: use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time error: format argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:114:13 + --> $DIR/macros-nonfatal-errors.rs:115:13 | LL | format!(invalid); | ^^^^^^^ @@ -209,43 +209,43 @@ LL | format!("{}", invalid); | +++++ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:116:14 + --> $DIR/macros-nonfatal-errors.rs:117:14 | LL | include!(invalid); | ^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:118:18 + --> $DIR/macros-nonfatal-errors.rs:119:18 | LL | include_str!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:119:5 + --> $DIR/macros-nonfatal-errors.rs:120:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:120:20 + --> $DIR/macros-nonfatal-errors.rs:121:20 | LL | include_bytes!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:121:5 + --> $DIR/macros-nonfatal-errors.rs:122:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trace_macros! accepts only `true` or `false` - --> $DIR/macros-nonfatal-errors.rs:123:5 + --> $DIR/macros-nonfatal-errors.rs:124:5 | LL | trace_macros!(invalid); | ^^^^^^^^^^^^^^^^^^^^^^ error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:133:9 + --> $DIR/macros-nonfatal-errors.rs:134:9 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -255,7 +255,7 @@ LL | Foo, = help: consider a manual implementation of `Default` error: cannot find macro `llvm_asm` in this scope - --> $DIR/macros-nonfatal-errors.rs:105:5 + --> $DIR/macros-nonfatal-errors.rs:106:5 | LL | llvm_asm!(invalid); | ^^^^^^^^ diff --git a/tests/ui/fail-simple.rs b/tests/ui/macros/no-matching-rule.rs similarity index 100% rename from tests/ui/fail-simple.rs rename to tests/ui/macros/no-matching-rule.rs diff --git a/tests/ui/fail-simple.stderr b/tests/ui/macros/no-matching-rule.stderr similarity index 85% rename from tests/ui/fail-simple.stderr rename to tests/ui/macros/no-matching-rule.stderr index 50c350b3ef55e..a6312a843f398 100644 --- a/tests/ui/fail-simple.stderr +++ b/tests/ui/macros/no-matching-rule.stderr @@ -1,5 +1,5 @@ error: no rules expected `@` - --> $DIR/fail-simple.rs:2:12 + --> $DIR/no-matching-rule.rs:2:12 | LL | panic!(@); | ^ no rules expected this token in macro call diff --git a/tests/ui/macros/reparse-expr-issue-139495.rs b/tests/ui/macros/reparse-expr-issue-139495.rs index 38d24573a5338..89734cae0a61d 100644 --- a/tests/ui/macros/reparse-expr-issue-139495.rs +++ b/tests/ui/macros/reparse-expr-issue-139495.rs @@ -1,7 +1,15 @@ -macro_rules! m { - ($abi : expr) => { extern $abi } //~ ERROR expected expression, found keyword `extern` +macro_rules! m1 { + ($abi: literal) => { extern $abi } //~ ERROR expected expression, found keyword `extern` +} + +macro_rules! m2 { + ($abi: expr) => { extern $abi } //~ ERROR expected expression, found keyword `extern` } fn main() { - m!(-2) + m1!(-2) +} + +fn f() { + m2!(-2) } diff --git a/tests/ui/macros/reparse-expr-issue-139495.stderr b/tests/ui/macros/reparse-expr-issue-139495.stderr index 73a8ed87ba058..e2e05d67ecc22 100644 --- a/tests/ui/macros/reparse-expr-issue-139495.stderr +++ b/tests/ui/macros/reparse-expr-issue-139495.stderr @@ -1,13 +1,24 @@ error: expected expression, found keyword `extern` - --> $DIR/reparse-expr-issue-139495.rs:2:22 + --> $DIR/reparse-expr-issue-139495.rs:2:24 | -LL | ($abi : expr) => { extern $abi } - | ^^^^^^ expected expression +LL | ($abi: literal) => { extern $abi } + | ^^^^^^ expected expression ... -LL | m!(-2) - | ------ in this macro invocation +LL | m1!(-2) + | ------- in this macro invocation | - = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `m1` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error: expected expression, found keyword `extern` + --> $DIR/reparse-expr-issue-139495.rs:6:21 + | +LL | ($abi: expr) => { extern $abi } + | ^^^^^^ expected expression +... +LL | m2!(-2) + | ------- in this macro invocation + | + = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors diff --git a/tests/ui/methods/clone-missing.rs b/tests/ui/methods/clone-missing.rs new file mode 100644 index 0000000000000..f2e4ad268c63a --- /dev/null +++ b/tests/ui/methods/clone-missing.rs @@ -0,0 +1,19 @@ +// This test checks that calling `.clone()` on a type that does not implement the `Clone` trait +// results in a compilation error. The `Foo` struct does not derive or implement `Clone`, +// so attempting to clone it should fail. + +struct Foo { + i: isize, +} + +fn foo(i:isize) -> Foo { + Foo { + i: i + } +} + +fn main() { + let x = foo(10); + let _y = x.clone(); + //~^ ERROR no method named `clone` found +} diff --git a/tests/ui/copy-a-resource.stderr b/tests/ui/methods/clone-missing.stderr similarity index 94% rename from tests/ui/copy-a-resource.stderr rename to tests/ui/methods/clone-missing.stderr index ff1e28bf9611d..4ab1aae4934bf 100644 --- a/tests/ui/copy-a-resource.stderr +++ b/tests/ui/methods/clone-missing.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `Foo` in the current scope - --> $DIR/copy-a-resource.rs:18:16 + --> $DIR/clone-missing.rs:17:16 | LL | struct Foo { | ---------- method `clone` not found for this struct diff --git a/tests/ui/minus-string.rs b/tests/ui/minus-string.rs deleted file mode 100644 index b83347b937edf..0000000000000 --- a/tests/ui/minus-string.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() { -"foo".to_string(); } //~ ERROR cannot apply unary operator `-` to type `String` diff --git a/tests/ui/mir/issue-99852.rs b/tests/ui/mir/issue-99852.rs index 59459c672281e..af754cf854699 100644 --- a/tests/ui/mir/issue-99852.rs +++ b/tests/ui/mir/issue-99852.rs @@ -1,6 +1,6 @@ //@ check-pass //@ compile-flags: -Z validate-mir -#![feature(let_chains)] +//@ edition: 2024 fn lambda() -> U where diff --git a/tests/ui/mir/mir_let_chains_drop_order.rs b/tests/ui/mir/mir_let_chains_drop_order.rs index 8991c6db7b98f..4794f3427ddac 100644 --- a/tests/ui/mir/mir_let_chains_drop_order.rs +++ b/tests/ui/mir/mir_let_chains_drop_order.rs @@ -6,7 +6,7 @@ // See `mir_drop_order.rs` for more information -#![feature(let_chains)] +#![cfg_attr(edition2021, feature(let_chains))] #![allow(irrefutable_let_patterns)] use std::cell::RefCell; diff --git a/tests/ui/modules/mod-pub-access.rs b/tests/ui/modules/mod-pub-access.rs new file mode 100644 index 0000000000000..c07e7a2ff3062 --- /dev/null +++ b/tests/ui/modules/mod-pub-access.rs @@ -0,0 +1,11 @@ +//@ run-pass +// This is a name resolution smoke test that ensures paths with more than one +// segment (e.g., `foo::bar`) resolve correctly. +// It also serves as a basic visibility test — confirming that a `pub` item +// inside a private module can still be accessed from outside that module. + +mod foo { + pub fn bar(_offset: usize) {} +} + +fn main() { foo::bar(0); } diff --git a/tests/ui/nll/relate_tys/placeholder-outlives-existential.rs b/tests/ui/nll/relate_tys/placeholder-outlives-existential.rs new file mode 100644 index 0000000000000..3f22e9a313af6 --- /dev/null +++ b/tests/ui/nll/relate_tys/placeholder-outlives-existential.rs @@ -0,0 +1,31 @@ +// Test that we correctly handle some cases of placeholder leaks. +// +//@ compile-flags:-Zno-leak-check + + +struct Co<'a>(&'a ()); +struct Inv<'a>(*mut &'a ()); +struct Contra<'a>(fn(&'a ())); + +// `exists<'e> forall<'p> 'p: 'e` -> ERROR +fn p_outlives_e( + x: for<'e> fn(for<'p> fn(fn(fn(Contra<'e>, Co<'p>)))), +) -> fn(fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>)))) { + x //~ ERROR mismatched types [E0308] +} + +// `exists<'e> forall<'p> 'e: 'p` -> Ok, 'e: 'static +fn e_outlives_p_static( + x: for<'e> fn(Inv<'e>, for<'p> fn(fn(fn(Contra<'p>, Co<'e>)))), +) -> fn(Inv<'static>, fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>)))) { + x +} + +// `exists<'e> forall<'p> 'e: 'p` -> Ok, 'e: 'static -> ERROR +fn e_outlives_p_static_err<'not_static>( + x: for<'e> fn(Inv<'e>, for<'p> fn(fn(fn(Contra<'p>, Co<'e>)))), +) -> fn(Inv<'not_static>, fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>)))) { + x //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/nll/relate_tys/placeholder-outlives-existential.stderr b/tests/ui/nll/relate_tys/placeholder-outlives-existential.stderr new file mode 100644 index 0000000000000..80ab5c8d6e9d8 --- /dev/null +++ b/tests/ui/nll/relate_tys/placeholder-outlives-existential.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/placeholder-outlives-existential.rs:14:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected fn pointer `fn(fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>))))` + found fn pointer `for<'e> fn(for<'e, 'p> fn(for<'e, 'p> fn(for<'e, 'p> fn(Contra<'e>, Co<'p>))))` + +error: lifetime may not live long enough + --> $DIR/placeholder-outlives-existential.rs:28:5 + | +LL | fn e_outlives_p_static_err<'not_static>( + | ----------- lifetime `'not_static` defined here +... +LL | x + | ^ returning this value requires that `'not_static` must outlive `'static` + | + = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Inv<'a>` is invariant over the parameter `'a` + = help: see for more information about variance + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/or-patterns/lazy-and-or.rs b/tests/ui/or-patterns/lazy-and-or.rs new file mode 100644 index 0000000000000..3d69553132b98 --- /dev/null +++ b/tests/ui/or-patterns/lazy-and-or.rs @@ -0,0 +1,25 @@ +//@ run-pass +// This test verifies the short-circuiting behavior of logical operators `||` and `&&`. +// It ensures that the right-hand expression is not evaluated when the left-hand +// expression is sufficient to determine the result. + +fn would_panic_if_called(x: &mut isize) -> bool { + *x += 1; + assert!(false, "This function should never be called due to short-circuiting"); + false +} + +fn main() { + let x = 1 == 2 || 3 == 3; + assert!(x); + + let mut y: isize = 10; + println!("Result of short-circuit: {}", x || would_panic_if_called(&mut y)); + assert_eq!(y, 10, "y should remain 10 if short-circuiting works correctly"); + + if true && x { + assert!(true); + } else { + assert!(false, "This branch should not be reached"); + } +} diff --git a/tests/ui/parser/brace-in-let-chain.rs b/tests/ui/parser/brace-in-let-chain.rs index 2009bc88d9e8d..25586441c1886 100644 --- a/tests/ui/parser/brace-in-let-chain.rs +++ b/tests/ui/parser/brace-in-let-chain.rs @@ -1,6 +1,6 @@ // issue #117766 +//@ edition: 2024 -#![feature(let_chains)] fn main() { if let () = () && let () = () { diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs index 224ee6c09e0c2..b7e7df1f860bb 100644 --- a/tests/ui/parser/deli-ident-issue-1.rs +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +//@ edition: 2024 trait Demo {} impl dyn Demo { diff --git a/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.rs b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.rs new file mode 100644 index 0000000000000..0ce5e233930e0 --- /dev/null +++ b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.rs @@ -0,0 +1,22 @@ +#![allow( + dead_code, + unused_must_use +)] + +struct Named { + foo: usize, +} + +struct Unnamed(usize); + +unsafe fn named_struct_field_access(named: *mut Named) { + named->foo += 1; //~ ERROR `->` is not valid syntax for field accesses and method calls + //~^ ERROR no field `foo` on type `*mut Named` +} + +unsafe fn unnamed_struct_field_access(unnamed: *mut Unnamed) { + unnamed->0 += 1; //~ ERROR `->` is not valid syntax for field accesses and method calls + //~^ ERROR no field `0` on type `*mut Unnamed` +} + +fn main() {} diff --git a/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.stderr b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.stderr new file mode 100644 index 0000000000000..45f3c5549f7ef --- /dev/null +++ b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.stderr @@ -0,0 +1,53 @@ +error: `->` is not valid syntax for field accesses and method calls + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:13:10 + | +LL | named->foo += 1; + | ^^ + | + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer +help: try using `.` instead + | +LL - named->foo += 1; +LL + named.foo += 1; + | + +error: `->` is not valid syntax for field accesses and method calls + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:18:12 + | +LL | unnamed->0 += 1; + | ^^ + | + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer +help: try using `.` instead + | +LL - unnamed->0 += 1; +LL + unnamed.0 += 1; + | + +error[E0609]: no field `foo` on type `*mut Named` + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:13:12 + | +LL | named->foo += 1; + | ^^^ unknown field + | +help: `named` is a raw pointer; try dereferencing it + | +LL - named->foo += 1; +LL + (*named).foo += 1; + | + +error[E0609]: no field `0` on type `*mut Unnamed` + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:18:14 + | +LL | unnamed->0 += 1; + | ^ unknown field + | +help: `unnamed` is a raw pointer; try dereferencing it + | +LL - unnamed->0 += 1; +LL + (*unnamed).0 += 1; + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/parser/expr-rarrow-call.fixed b/tests/ui/parser/expr-rarrow-call.fixed index 9a05e20092dd8..c97284c4b01e7 100644 --- a/tests/ui/parser/expr-rarrow-call.fixed +++ b/tests/ui/parser/expr-rarrow-call.fixed @@ -11,23 +11,23 @@ struct Named { struct Unnamed(usize); fn named_struct_field_access(named: &Named) { - named.foo; //~ ERROR `->` used for field access or method call + named.foo; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn unnamed_struct_field_access(unnamed: &Unnamed) { - unnamed.0; //~ ERROR `->` used for field access or method call + unnamed.0; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn tuple_field_access(t: &(u8, u8)) { - t.0; //~ ERROR `->` used for field access or method call - t.1; //~ ERROR `->` used for field access or method call + t.0; //~ ERROR `->` is not valid syntax for field accesses and method calls + t.1; //~ ERROR `->` is not valid syntax for field accesses and method calls } #[derive(Clone)] struct Foo; fn method_call(foo: &Foo) { - foo.clone(); //~ ERROR `->` used for field access or method call + foo.clone(); //~ ERROR `->` is not valid syntax for field accesses and method calls } fn main() {} diff --git a/tests/ui/parser/expr-rarrow-call.rs b/tests/ui/parser/expr-rarrow-call.rs index 760b0f6f345b9..78cd72b12ec85 100644 --- a/tests/ui/parser/expr-rarrow-call.rs +++ b/tests/ui/parser/expr-rarrow-call.rs @@ -11,23 +11,23 @@ struct Named { struct Unnamed(usize); fn named_struct_field_access(named: &Named) { - named->foo; //~ ERROR `->` used for field access or method call + named->foo; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn unnamed_struct_field_access(unnamed: &Unnamed) { - unnamed->0; //~ ERROR `->` used for field access or method call + unnamed->0; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn tuple_field_access(t: &(u8, u8)) { - t->0; //~ ERROR `->` used for field access or method call - t->1; //~ ERROR `->` used for field access or method call + t->0; //~ ERROR `->` is not valid syntax for field accesses and method calls + t->1; //~ ERROR `->` is not valid syntax for field accesses and method calls } #[derive(Clone)] struct Foo; fn method_call(foo: &Foo) { - foo->clone(); //~ ERROR `->` used for field access or method call + foo->clone(); //~ ERROR `->` is not valid syntax for field accesses and method calls } fn main() {} diff --git a/tests/ui/parser/expr-rarrow-call.stderr b/tests/ui/parser/expr-rarrow-call.stderr index 2e168ca26fecc..0b105273861f8 100644 --- a/tests/ui/parser/expr-rarrow-call.stderr +++ b/tests/ui/parser/expr-rarrow-call.stderr @@ -1,62 +1,62 @@ -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:14:10 | LL | named->foo; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - named->foo; LL + named.foo; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:18:12 | LL | unnamed->0; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - unnamed->0; LL + unnamed.0; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:22:6 | LL | t->0; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - t->0; LL + t.0; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:23:6 | LL | t->1; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - t->1; LL + t.1; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:30:8 | LL | foo->clone(); | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - foo->clone(); diff --git a/tests/ui/parser/issues/issue-103381.fixed b/tests/ui/parser/issues/issue-103381.fixed index 87c308789a18e..955b246b86368 100644 --- a/tests/ui/parser/issues/issue-103381.fixed +++ b/tests/ui/parser/issues/issue-103381.fixed @@ -1,6 +1,6 @@ +//@ edition: 2024 //@ run-rustfix -#![feature(let_chains)] #![allow(unused_variables)] #![allow(dead_code)] #![allow(irrefutable_let_patterns)] diff --git a/tests/ui/parser/issues/issue-103381.rs b/tests/ui/parser/issues/issue-103381.rs index ccbc40e5d0216..d4b06b9c77016 100644 --- a/tests/ui/parser/issues/issue-103381.rs +++ b/tests/ui/parser/issues/issue-103381.rs @@ -1,6 +1,6 @@ +//@ edition: 2024 //@ run-rustfix -#![feature(let_chains)] #![allow(unused_variables)] #![allow(dead_code)] #![allow(irrefutable_let_patterns)] diff --git a/tests/ui/parser/issues/issue-118530-ice.rs b/tests/ui/parser/issues/issue-118530-ice.rs index cf14eebec2b76..8930eb86c6b24 100644 --- a/tests/ui/parser/issues/issue-118530-ice.rs +++ b/tests/ui/parser/issues/issue-118530-ice.rs @@ -5,7 +5,7 @@ fn bar() -> String { attr::fn bar() -> String { //~ ERROR expected identifier, found keyword `fn` //~^ ERROR expected one of `(`, `.`, `::`, `;`, `?`, `}`, or an operator, found `{` //~| ERROR expected `;`, found `bar` - //~| ERROR `->` used for field access or method call + //~| ERROR `->` is not valid syntax for field accesses and method calls #[attr] [1, 2, 3].iter().map().collect::() #[attr] diff --git a/tests/ui/parser/issues/issue-118530-ice.stderr b/tests/ui/parser/issues/issue-118530-ice.stderr index 72c0397e9c95b..ef891d1dc2906 100644 --- a/tests/ui/parser/issues/issue-118530-ice.stderr +++ b/tests/ui/parser/issues/issue-118530-ice.stderr @@ -33,13 +33,13 @@ LL | attr::fn bar() -> String { | | | help: add `;` here -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/issue-118530-ice.rs:5:20 | LL | attr::fn bar() -> String { | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - attr::fn bar() -> String { diff --git a/tests/ui/parser/or-in-let-chain.edition2021.stderr b/tests/ui/parser/or-in-let-chain.edition2021.stderr new file mode 100644 index 0000000000000..a97095cc3b82e --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2021.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.edition2024.stderr b/tests/ui/parser/or-in-let-chain.edition2024.stderr new file mode 100644 index 0000000000000..a97095cc3b82e --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2024.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.rs b/tests/ui/parser/or-in-let-chain.rs new file mode 100644 index 0000000000000..4c4372bb00f0f --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 + +fn main() { + if let true = true || false {} + //~^ ERROR `||` operators are not supported in let chain conditions + // With parentheses + if (let true = true) || false {} + //~^ ERROR expected expression, found `let` statement + // Multiple || operators + if let true = true || false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions + // Mixed operators (should still show error for ||) + if let true = true && false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions +} diff --git a/tests/ui/parser/semi-in-let-chain.rs b/tests/ui/parser/semi-in-let-chain.rs index 9c21af0372d25..522b90ea250b7 100644 --- a/tests/ui/parser/semi-in-let-chain.rs +++ b/tests/ui/parser/semi-in-let-chain.rs @@ -1,6 +1,5 @@ // Issue #117720 - -#![feature(let_chains)] +//@ edition: 2024 fn main() { if let () = () diff --git a/tests/ui/parser/semi-in-let-chain.stderr b/tests/ui/parser/semi-in-let-chain.stderr index c1a8f92965eb8..f36d5e041e5d5 100644 --- a/tests/ui/parser/semi-in-let-chain.stderr +++ b/tests/ui/parser/semi-in-let-chain.stderr @@ -1,11 +1,11 @@ error: expected `{`, found `;` - --> $DIR/semi-in-let-chain.rs:7:23 + --> $DIR/semi-in-let-chain.rs:6:23 | LL | && let () = (); | ^ expected `{` | note: you likely meant to continue parsing the let-chain starting here - --> $DIR/semi-in-let-chain.rs:8:9 + --> $DIR/semi-in-let-chain.rs:7:9 | LL | && let () = () | ^^^^^^ @@ -16,13 +16,13 @@ LL + && let () = () | error: expected `{`, found `;` - --> $DIR/semi-in-let-chain.rs:15:20 + --> $DIR/semi-in-let-chain.rs:14:20 | LL | && () == (); | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/semi-in-let-chain.rs:14:8 + --> $DIR/semi-in-let-chain.rs:13:8 | LL | if let () = () | ________^ @@ -30,13 +30,13 @@ LL | | && () == (); | |___________________^ error: expected `{`, found `;` - --> $DIR/semi-in-let-chain.rs:23:20 + --> $DIR/semi-in-let-chain.rs:22:20 | LL | && () == (); | ^ expected `{` | note: you likely meant to continue parsing the let-chain starting here - --> $DIR/semi-in-let-chain.rs:24:9 + --> $DIR/semi-in-let-chain.rs:23:9 | LL | && let () = () | ^^^^^^ diff --git a/tests/ui/parser/ty-path-followed-by-single-colon.rs b/tests/ui/parser/ty-path-followed-by-single-colon.rs new file mode 100644 index 0000000000000..a9082ea317a78 --- /dev/null +++ b/tests/ui/parser/ty-path-followed-by-single-colon.rs @@ -0,0 +1,22 @@ +// Paths in type contexts may be followed by single colons. +// This means we can't generally assume that the user typo'ed a double colon. +// issue: +//@ check-pass +#![crate_type = "lib"] +#![expect(non_camel_case_types)] + +#[rustfmt::skip] +mod garden { + + fn f() where path:to::somewhere {} // OK! + + fn g(_: impl Take) {} // OK! + + #[cfg(any())] fn h() where a::path:to::nowhere {} // OK! + + fn i(_: impl Take:to::somewhere>) {} // OK! + + mod to { pub(super) trait somewhere {} } + trait Take { type path; } + +} diff --git a/tests/ui/path.rs b/tests/ui/path.rs deleted file mode 100644 index bd7b99ac01ad5..0000000000000 --- a/tests/ui/path.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass - -mod foo { - pub fn bar(_offset: usize) { } -} - -pub fn main() { foo::bar(0); } diff --git a/tests/ui/pattern/byte-string-mutability-mismatch.rs b/tests/ui/pattern/byte-string-mutability-mismatch.rs new file mode 100644 index 0000000000000..4ffdb5f9b99e8 --- /dev/null +++ b/tests/ui/pattern/byte-string-mutability-mismatch.rs @@ -0,0 +1,19 @@ +//! Byte string literal patterns use the mutability of the literal, rather than the mutability of +//! the pattern's scrutinee. Since byte string literals are always shared references, it's a +//! mismatch to use a byte string literal pattern to match on a mutable array or slice reference. + +fn main() { + let mut val = [97u8, 10u8]; + match &mut val { + b"a\n" => {}, + //~^ ERROR mismatched types + //~| types differ in mutability + _ => {}, + } + match &mut val[..] { + b"a\n" => {}, + //~^ ERROR mismatched types + //~| types differ in mutability + _ => {}, + } +} diff --git a/tests/ui/pattern/byte-string-mutability-mismatch.stderr b/tests/ui/pattern/byte-string-mutability-mismatch.stderr new file mode 100644 index 0000000000000..ee796278e69d5 --- /dev/null +++ b/tests/ui/pattern/byte-string-mutability-mismatch.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/byte-string-mutability-mismatch.rs:8:9 + | +LL | match &mut val { + | -------- this expression has type `&mut [u8; 2]` +LL | b"a\n" => {}, + | ^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/byte-string-mutability-mismatch.rs:14:10 + | +LL | match &mut val[..] { + | ------------ this expression has type `&mut [u8]` +LL | b"a\n" => {}, + | ^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs new file mode 100644 index 0000000000000..29a33e3e2c3b6 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs @@ -0,0 +1,34 @@ +//! Test type errors for byte string literal patterns. `deref_patterns` allows byte string literal +//! patterns to have type `[u8]` or `[u8; N]` when matching on a slice or array; this can affect the +//! "found" type reported in error messages when matching on a slice or array of the wrong type. + +#![feature(deref_patterns)] +#![expect(incomplete_features)] + +fn main() { + // Baseline 1: under normal circumstances, byte string literal patterns have type `&[u8; N]`, + // the same as byte string literals. + if let b"test" = () {} + //~^ ERROR mismatched types + //~| expected `()`, found `&[u8; 4]` + + // Baseline 2: there's a special case for byte string patterns in stable rust, allowing them to + // match on slice references. This affects the error when matching on a non-`&[u8]` slice ref, + // reporting the "found" type as `&[u8]`. + if let b"test" = &[] as &[i8] {} + //~^ ERROR mismatched types + //~| expected `&[i8]`, found `&[u8]` + + // Test matching on a non-`[u8]` slice: the pattern has type `[u8]` if a slice is expected. + if let b"test" = *(&[] as &[i8]) {} + //~^ ERROR mismatched types + //~| expected `[i8]`, found `[u8]` + + // Test matching on a non-`[u8;4]` array: the pattern has type `[u8;4]` if an array is expected. + if let b"test" = [()] {} + //~^ ERROR mismatched types + //~| expected `[(); 1]`, found `[u8; 4]` + if let b"test" = *b"this array is too long" {} + //~^ ERROR mismatched types + //~| expected an array with a size of 22, found one with a size of 4 +} diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr new file mode 100644 index 0000000000000..d29a5b59252c9 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr @@ -0,0 +1,52 @@ +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:11:12 + | +LL | if let b"test" = () {} + | ^^^^^^^ -- this expression has type `()` + | | + | expected `()`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:18:12 + | +LL | if let b"test" = &[] as &[i8] {} + | ^^^^^^^ ------------ this expression has type `&[i8]` + | | + | expected `&[i8]`, found `&[u8]` + | + = note: expected reference `&[i8]` + found reference `&'static [u8]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:23:12 + | +LL | if let b"test" = *(&[] as &[i8]) {} + | ^^^^^^^ --------------- this expression has type `[i8]` + | | + | expected `[i8]`, found `[u8]` + | + = note: expected slice `[i8]` + found slice `[u8]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:28:12 + | +LL | if let b"test" = [()] {} + | ^^^^^^^ ---- this expression has type `[(); 1]` + | | + | expected `[(); 1]`, found `[u8; 4]` + | + = note: expected array `[(); 1]` + found array `[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:31:12 + | +LL | if let b"test" = *b"this array is too long" {} + | ^^^^^^^ -------------------------- this expression has type `[u8; 22]` + | | + | expected an array with a size of 22, found one with a size of 4 + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/needs-gate.rs b/tests/ui/pattern/deref-patterns/needs-gate.rs index 2d5ec45217fbb..7944744ee839c 100644 --- a/tests/ui/pattern/deref-patterns/needs-gate.rs +++ b/tests/ui/pattern/deref-patterns/needs-gate.rs @@ -12,4 +12,21 @@ fn main() { //~^ ERROR: mismatched types _ => {} } + + // `deref_patterns` allows string and byte string literals to have non-ref types. + match *"test" { + "test" => {} + //~^ ERROR: mismatched types + _ => {} + } + match *b"test" { + b"test" => {} + //~^ ERROR: mismatched types + _ => {} + } + match *(b"test" as &[u8]) { + b"test" => {} + //~^ ERROR: mismatched types + _ => {} + } } diff --git a/tests/ui/pattern/deref-patterns/needs-gate.stderr b/tests/ui/pattern/deref-patterns/needs-gate.stderr index 8687b5dc977db..e886ca9805581 100644 --- a/tests/ui/pattern/deref-patterns/needs-gate.stderr +++ b/tests/ui/pattern/deref-patterns/needs-gate.stderr @@ -23,7 +23,31 @@ help: consider dereferencing to access the inner value using the Deref trait LL | match *Box::new(0) { | + -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:18:9 + | +LL | match *"test" { + | ------- this expression has type `str` +LL | "test" => {} + | ^^^^^^ expected `str`, found `&str` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:23:9 + | +LL | match *b"test" { + | -------- this expression has type `[u8; 4]` +LL | b"test" => {} + | ^^^^^^^ expected `[u8; 4]`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:28:9 + | +LL | match *(b"test" as &[u8]) { + | ------------------- this expression has type `[u8]` +LL | b"test" => {} + | ^^^^^^^ expected `[u8]`, found `&[u8; 4]` + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/strings.rs b/tests/ui/pattern/deref-patterns/strings.rs new file mode 100644 index 0000000000000..536e943b3f672 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/strings.rs @@ -0,0 +1,66 @@ +//@ run-pass +//! Test deref patterns using string and bytestring literals. + +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +fn main() { + for (test_in, test_expect) in [("zero", 0), ("one", 1), ("two", 2)] { + // Test string literal patterns having type `str`. + let test_actual = match *test_in { + "zero" => 0, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test string literals in explicit `deref!(_)` patterns. + let test_actual = match test_in.to_string() { + deref!("zero") => 0, + deref!("one") => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + } + + // Test that we can still mutate in the match arm after using a literal to test equality: + let mut test = "test".to_string(); + if let deref!(s @ "test") = &mut test { + s.make_ascii_uppercase(); + } + assert_eq!(test, "TEST"); + + for (test_in, test_expect) in [(b"0", 0), (b"1", 1), (b"2", 2)] { + // Test byte string literal patterns having type `[u8; N]` + let test_actual = match *test_in { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literal patterns having type `[u8]` + let test_actual = match *(test_in as &[u8]) { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literals used as arrays in explicit `deref!(_)` patterns. + let test_actual = match Box::new(*test_in) { + deref!(b"0") => 0, + deref!(b"1") => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literals used as slices in explicit `deref!(_)` patterns. + let test_actual = match test_in.to_vec() { + deref!(b"0") => 0, + deref!(b"1") => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + } +} diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.rs b/tests/ui/pattern/deref-patterns/typeck_fail.rs index 4b9ad7d25f090..52d84f7a34de3 100644 --- a/tests/ui/pattern/deref-patterns/typeck_fail.rs +++ b/tests/ui/pattern/deref-patterns/typeck_fail.rs @@ -2,18 +2,14 @@ #![allow(incomplete_features)] fn main() { - // FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a - // place of type `str`. + // FIXME(deref_patterns): fails to typecheck because string literal patterns don't peel + // references from the scrutinee. match "foo".to_string() { - deref!("foo") => {} - //~^ ERROR: mismatched types "foo" => {} //~^ ERROR: mismatched types _ => {} } match &"foo".to_string() { - deref!("foo") => {} - //~^ ERROR: mismatched types "foo" => {} //~^ ERROR: mismatched types _ => {} diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.stderr b/tests/ui/pattern/deref-patterns/typeck_fail.stderr index 3e2f356188220..e87528c1c51a9 100644 --- a/tests/ui/pattern/deref-patterns/typeck_fail.stderr +++ b/tests/ui/pattern/deref-patterns/typeck_fail.stderr @@ -1,34 +1,16 @@ error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:8:16 + --> $DIR/typeck_fail.rs:8:9 | LL | match "foo".to_string() { | ----------------- this expression has type `String` -LL | deref!("foo") => {} - | ^^^^^ expected `str`, found `&str` - -error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:10:9 - | -LL | match "foo".to_string() { - | ----------------- this expression has type `String` -... LL | "foo" => {} | ^^^^^ expected `String`, found `&str` error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:15:16 - | -LL | match &"foo".to_string() { - | ------------------ this expression has type `&String` -LL | deref!("foo") => {} - | ^^^^^ expected `str`, found `&str` - -error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:17:9 + --> $DIR/typeck_fail.rs:13:9 | LL | match &"foo".to_string() { | ------------------ this expression has type `&String` -... LL | "foo" => {} | ^^^^^ expected `&String`, found `&str` | @@ -36,7 +18,7 @@ LL | "foo" => {} found reference `&'static str` error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:24:9 + --> $DIR/typeck_fail.rs:20:9 | LL | match Some(0) { | ------- this expression has type `Option<{integer}>` @@ -46,6 +28,6 @@ LL | Ok(0) => {} = note: expected enum `Option<{integer}>` found enum `Result<_, _>` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/recursion/recursive-enum-box.rs b/tests/ui/recursion/recursive-enum-box.rs new file mode 100644 index 0000000000000..540b0c553603c --- /dev/null +++ b/tests/ui/recursion/recursive-enum-box.rs @@ -0,0 +1,21 @@ +//@ run-pass +// A smoke test for recursive enum structures using Box. +// This test constructs a linked list-like structure to exercise memory allocation and ownership. +// Originally introduced in 2010, this is one of Rust’s earliest test cases. + +#![allow(dead_code)] + +enum List { + Cons(isize, Box), + Nil, +} + +fn main() { + List::Cons( + 10, + Box::new(List::Cons( + 11, + Box::new(List::Cons(12, Box::new(List::Nil))), + )), + ); +} diff --git a/tests/ui/capture1.rs b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs similarity index 100% rename from tests/ui/capture1.rs rename to tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs diff --git a/tests/ui/capture1.stderr b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr similarity index 85% rename from tests/ui/capture1.stderr rename to tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr index 8027430de522b..6b3e879201158 100644 --- a/tests/ui/capture1.stderr +++ b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr @@ -1,5 +1,5 @@ error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/capture1.rs:3:32 + --> $DIR/fn-item-cant-capture-dynamic-env.rs:3:32 | LL | fn foo() -> isize { return bar; } | ^^^ diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs b/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs index 6b85ada3aadee..60e5452edb38c 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs @@ -1,5 +1,5 @@ +//@ edition: 2024 #![feature(never_patterns)] -#![feature(let_chains)] #![allow(incomplete_features)] #![deny(unreachable_patterns)] diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs index ce6d10bf33cbd..a4baf1fe4b97d 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs @@ -1,5 +1,4 @@ //@ needs-asm-support -#![feature(naked_functions)] use std::arch::naked_asm; diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr index f89d94b67d806..d3cafbc635086 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr @@ -1,5 +1,5 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/error-with-naked.rs:6:1 + --> $DIR/error-with-naked.rs:5:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` @@ -8,7 +8,7 @@ LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here error[E0736]: attribute incompatible with `#[unsafe(naked)]` - --> $DIR/error-with-naked.rs:18:5 + --> $DIR/error-with-naked.rs:17:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` @@ -17,13 +17,13 @@ LL | #[unsafe(naked)] | ---------------- function marked with `#[unsafe(naked)]` here error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/error-with-naked.rs:6:1 + --> $DIR/error-with-naked.rs:5:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/error-with-naked.rs:18:5 + --> $DIR/error-with-naked.rs:17:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs index 983fe87451f24..b1e305834cb2e 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs @@ -15,11 +15,9 @@ fn _if_let_guard() { () if true && let 0 = 1 => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable () if let 0 = 1 && true => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable () if (let 0 = 1) && true => {} //~^ ERROR expected expression, found `let` statement @@ -33,8 +31,6 @@ fn _if_let_guard() { () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable - //~| ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement @@ -42,7 +38,6 @@ fn _if_let_guard() { () if let Range { start: _, end: _ } = (true..true) && false => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable _ => {} } diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr index 0997f0c81a019..19d1f4b0a573b 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr @@ -25,98 +25,98 @@ LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:24:16 + --> $DIR/feature-gate.rs:22:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:24:16 + --> $DIR/feature-gate.rs:22:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:27:24 + --> $DIR/feature-gate.rs:25:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:27:24 + --> $DIR/feature-gate.rs:25:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:30:16 + --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:30:16 + --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:30:31 + --> $DIR/feature-gate.rs:28:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:30:31 + --> $DIR/feature-gate.rs:28:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:55 + --> $DIR/feature-gate.rs:32:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:68 + --> $DIR/feature-gate.rs:32:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:60:16 + --> $DIR/feature-gate.rs:55:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^ @@ -124,7 +124,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:60:16 + --> $DIR/feature-gate.rs:55:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^ @@ -133,7 +133,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:63:16 + --> $DIR/feature-gate.rs:58:16 | LL | use_expr!((let 0 = 1)); | ^^^ @@ -141,7 +141,7 @@ LL | use_expr!((let 0 = 1)); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:63:16 + --> $DIR/feature-gate.rs:58:16 | LL | use_expr!((let 0 = 1)); | ^^^ @@ -150,7 +150,7 @@ LL | use_expr!((let 0 = 1)); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: no rules expected keyword `let` - --> $DIR/feature-gate.rs:72:15 + --> $DIR/feature-gate.rs:67:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -159,7 +159,7 @@ LL | use_expr!(let 0 = 1); | ^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr` - --> $DIR/feature-gate.rs:53:10 + --> $DIR/feature-gate.rs:48:10 | LL | ($e:expr) => { | ^^^^^^^ @@ -187,7 +187,7 @@ LL | () if true && let 0 = 1 => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:20:12 + --> $DIR/feature-gate.rs:19:12 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +198,7 @@ LL | () if let 0 = 1 && true => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:34:12 + --> $DIR/feature-gate.rs:32:12 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:43:12 + --> $DIR/feature-gate.rs:39:12 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +220,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:68:12 + --> $DIR/feature-gate.rs:63:12 | LL | () if let 0 = 1 => {} | ^^^^^^^^^^^^ @@ -230,56 +230,6 @@ LL | () if let 0 = 1 => {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: you can write `if matches!(, )` instead of `if let = ` -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:16:23 - | -LL | () if true && let 0 = 1 => {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:20:15 - | -LL | () if let 0 = 1 && true => {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:15 - | -LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:28 - | -LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:43:15 - | -LL | () if let Range { start: _, end: _ } = (true..true) && false => {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 25 previous errors +error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs index 423a2cd53fc5c..e1138835006b9 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs @@ -1,7 +1,6 @@ // Expression macros can't expand to a let match guard. #![feature(if_let_guard)] -#![feature(let_chains)] macro_rules! m { ($e:expr) => { let Some(x) = $e } diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr index b8065b9f55607..921cd083fffd7 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr @@ -1,5 +1,5 @@ error: expected expression, found `let` statement - --> $DIR/macro-expanded.rs:7:20 + --> $DIR/macro-expanded.rs:6:20 | LL | ($e:expr) => { let Some(x) = $e } | ^^^ diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs index 47653efffb7b8..29f529a30c2ff 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs @@ -1,5 +1,5 @@ +//@ edition: 2024 #![feature(if_let_guard)] -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] fn same_pattern(c: bool) { diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs index 0e71a9d24c970..a4cb73c650835 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs @@ -1,7 +1,7 @@ +//@ edition: 2024 // Parenthesised let "expressions" are not allowed in guards #![feature(if_let_guard)] -#![feature(let_chains)] #[cfg(false)] fn un_cfged() { diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs index 32a223c915951..e7946a2a23b11 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs @@ -1,8 +1,8 @@ // Check shadowing in if let guards works as expected. //@ check-pass +//@ edition: 2024 #![feature(if_let_guard)] -#![feature(let_chains)] fn main() { let x: Option> = Some(Some(6)); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs index e1edde6de19c9..dc052c38fe7ad 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs @@ -1,6 +1,6 @@ //@ run-pass +//@ edition: 2024 -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] fn main() { diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs index cb3be59bee019..ed461af66aaf9 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs @@ -1,9 +1,9 @@ -#![feature(let_chains)] +//@ edition: 2024 fn let_or_guard(x: Result, ()>) { match x { Ok(opt) if let Some(4) = opt || false => {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions _ => {} } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr index 4b85fdd505057..0566d96fddc31 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr @@ -1,11 +1,4 @@ -error: expected expression, found `let` statement - --> $DIR/ast-validate-guards.rs:5:20 - | -LL | Ok(opt) if let Some(4) = opt || false => {} - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/ast-validate-guards.rs:5:38 | LL | Ok(opt) if let Some(4) = opt || false => {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2021.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2021.rs new file mode 100644 index 0000000000000..d2d5ae1110415 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2021.rs @@ -0,0 +1,26 @@ +//@ edition: 2021 + +#[macro_export] +macro_rules! make_if { + (($($tt:tt)*) { $body:expr } { $else:expr }) => {{ + if $($tt)* { + $body + } else { + $else + } + }}; + (let ($expr:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr { + $body + } else { + $else + } + }}; + (let ($expr:expr) let ($expr2:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr && let None = $expr2 { + $body + } else { + $else + } + }}; +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2024.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2024.rs new file mode 100644 index 0000000000000..f5b6f35abf8fd --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2024.rs @@ -0,0 +1,26 @@ +//@ edition: 2024 + +#[macro_export] +macro_rules! make_if { + (($($tt:tt)*) { $body:expr } { $else:expr }) => {{ + if $($tt)* { + $body + } else { + $else + } + }}; + (let ($expr:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr { + $body + } else { + $else + } + }}; + (let ($expr:expr) let ($expr2:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr && let None = $expr2 { + $body + } else { + $else + } + }}; +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr index 817e226bc45d5..141a6d255d086 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr index bab50c22c0308..dda09de4c5344 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr index 943956feb4e07..5b53691cbf546 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs index 0b0abe6ec1754..65beccf2214fd 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs @@ -119,7 +119,7 @@ fn nested_within_if_expr() { //~^ ERROR expected expression, found `let` statement if true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions if (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement if true && (true || let 0 = 0) {} @@ -210,7 +210,7 @@ fn nested_within_while_expr() { //~^ ERROR expected expression, found `let` statement while true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions while (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement while true && (true || let 0 = 0) {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2021.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2021.stderr new file mode 100644 index 0000000000000..23700f89f1080 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2021.stderr @@ -0,0 +1,65 @@ +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:30 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:52 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:26:30 + | +LL | macro_in_2024::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:26:52 + | +LL | macro_in_2024::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2024.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2024.stderr new file mode 100644 index 0000000000000..3af844f4f9665 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2024.stderr @@ -0,0 +1,45 @@ +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:30 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:52 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.rs new file mode 100644 index 0000000000000..89b555d2c50b7 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.rs @@ -0,0 +1,30 @@ +//@ revisions: edition2021 edition2024 +//@ compile-flags: -Z lint-mir -Z validate-mir +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ aux-build:macro-in-2021.rs +//@ aux-build:macro-in-2024.rs + +use std::unreachable as never; + +// Compiletest doesn't specify the needed --extern flags to make `extern crate` unneccessary +extern crate macro_in_2021; +extern crate macro_in_2024; + +fn main() { + // Gated on both 2021 and 2024 if the `if` comes from a 2021 macro + // Gated only on 2021 if the `if` comes from a 2024 macro + // No gating if both the `if` and the chain are from a 2024 macro + + macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + //~^ ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + //~^ ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + + macro_in_2024::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + //[edition2021]~^ ERROR `let` expressions in this position are unstable + //[edition2021]~| ERROR `let` expressions in this position are unstable + macro_in_2024::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs new file mode 100644 index 0000000000000..5dd928dbce5b0 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs @@ -0,0 +1,76 @@ +//@ revisions: edition2021 edition2024 +//@ compile-flags: -Z lint-mir -Z validate-mir +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ aux-build:macro-in-2021.rs +//@ aux-build:macro-in-2024.rs +//@ run-pass + +#![allow(dead_code)] + +use std::unreachable as never; +use std::cell::RefCell; +use std::convert::TryInto; + +// Compiletest doesn't specify the needed --extern flags to make `extern crate` unneccessary +extern crate macro_in_2021; +extern crate macro_in_2024; + +#[derive(Default)] +struct DropOrderCollector(RefCell>); + +struct LoudDrop<'a>(&'a DropOrderCollector, u32); + +impl Drop for LoudDrop<'_> { + fn drop(&mut self) { + println!("{}", self.1); + self.0.0.borrow_mut().push(self.1); + } +} + +impl DropOrderCollector { + fn print(&self, n: u32) { + println!("{n}"); + self.0.borrow_mut().push(n) + } + fn some_loud(&self, n: u32) -> Option { + Some(LoudDrop(self, n)) + } + + #[track_caller] + fn validate(self) { + assert!( + self.0 + .into_inner() + .into_iter() + .enumerate() + .all(|(idx, item)| idx + 1 == item.try_into().unwrap()) + ); + } + fn with_macro_2021(self) { + // Edition 2021 drop behaviour + macro_in_2021::make_if!((let None = self.some_loud(2)) { never!() } {self.print(1) }); + macro_in_2021::make_if!(let (self.some_loud(4)) { never!() } { self.print(3) }); + self.validate(); + } + fn with_macro_2024(self) { + // Edition 2024 drop behaviour + macro_in_2024::make_if!((let None = self.some_loud(1)) { never!() } { self.print(2) }); + macro_in_2024::make_if!(let (self.some_loud(3)) { never!() } { self.print(4) }); + self.validate(); + } +} + +fn main() { + // 2021 drop order if it's a 2021 macro creating the `if` + // 2024 drop order if it's a 2024 macro creating the `if` + + // Compare this with edition-gate-macro-error.rs: We want to avoid exposing 2021 drop order, + // because it can create bad MIR (issue #104843) + // This test doesn't contain any let chains at all: it should be understood + // in combination with `edition-gate-macro-error.rs` + + DropOrderCollector::default().with_macro_2021(); + DropOrderCollector::default().with_macro_2024(); + +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs index 3c572054e3fad..8e7db9a430cc5 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +//@ edition: 2024 fn main() { let opt = Some(1i32); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs index 59c81d053bc76..0113a87cecc25 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs @@ -1,6 +1,5 @@ //@ check-pass - -#![feature(let_chains)] +//@ edition: 2024 fn main() { let x = Some(vec!["test"]); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs index ea4b34eacba5d..f5d87d6999a23 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs @@ -1,6 +1,5 @@ //@ check-pass - -#![feature(let_chains)] +//@ edition:2024 fn main() { let opt = Some("foo bar"); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs index f90b9ab0d40f0..f6de37867d855 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs @@ -2,7 +2,6 @@ fn main() { match true { _ if let true = true && true => {} //~^ ERROR `if let` guards are - //~| ERROR `let` expressions in this _ => {} } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr index 637ae4915ed14..17fa37d2df322 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr @@ -9,16 +9,6 @@ LL | _ if let true = true && true => {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: you can write `if matches!(, )` instead of `if let = ` -error[E0658]: `let` expressions in this position are unstable - --> $DIR/issue-93150.rs:3:14 - | -LL | _ if let true = true && true => {} - | ^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs index 77756d0bee06b..7589b33a73c19 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zvalidate-mir -C opt-level=3 //@ build-pass -#![feature(let_chains)] +//@ edition: 2024 + struct TupleIter> { inner: I, } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs index 2c7fe2eea3308..7d8dfe37135c7 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs @@ -3,7 +3,6 @@ //@ edition: 2024 //@ check-pass -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] struct Pd; diff --git a/tests/ui/runtime/rt-explody-panic-payloads.rs b/tests/ui/runtime/rt-explody-panic-payloads.rs index c177fd260ed4f..d564a26ca7374 100644 --- a/tests/ui/runtime/rt-explody-panic-payloads.rs +++ b/tests/ui/runtime/rt-explody-panic-payloads.rs @@ -27,6 +27,6 @@ fn main() { // by QEMU in the stderr whenever a core dump happens. Remove it before the check. v.strip_suffix("qemu: uncaught target signal 6 (Aborted) - core dumped\n").unwrap_or(v) }) - .map(|v| { v.ends_with("fatal runtime error: drop of the panic payload panicked\n") }) + .map(|v| v.ends_with("fatal runtime error: drop of the panic payload panicked, aborting\n")) .unwrap_or(false)); } diff --git a/tests/ui/simd/intrinsic/generic-comparison-pass.rs b/tests/ui/simd/intrinsic/generic-comparison-pass.rs index 2ee164cdfd800..50a05eecb03b3 100644 --- a/tests/ui/simd/intrinsic/generic-comparison-pass.rs +++ b/tests/ui/simd/intrinsic/generic-comparison-pass.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(repr_simd, core_intrinsics, concat_idents)] +#![feature(repr_simd, core_intrinsics, macro_metavar_expr_concat)] #![allow(non_camel_case_types)] use std::intrinsics::simd::{simd_eq, simd_ge, simd_gt, simd_le, simd_lt, simd_ne}; @@ -19,7 +19,7 @@ macro_rules! cmp { ($method: ident($lhs: expr, $rhs: expr)) => {{ let lhs = $lhs; let rhs = $rhs; - let e: u32x4 = concat_idents!(simd_, $method)($lhs, $rhs); + let e: u32x4 = ${concat(simd_, $method)}($lhs, $rhs); // assume the scalar version is correct/the behaviour we want. assert!((e.0[0] != 0) == lhs.0[0].$method(&rhs.0[0])); assert!((e.0[1] != 0) == lhs.0[1].$method(&rhs.0[1])); diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr new file mode 100644 index 0000000000000..7e3df0c83f9f3 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr new file mode 100644 index 0000000000000..7e3df0c83f9f3 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.rs b/tests/ui/specialization/prefer-specializing-impl-over-default.rs new file mode 100644 index 0000000000000..af6837b30cafa --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +trait WithAssoc: 'static { + type Assoc; +} +impl WithAssoc for (T,) { + type Assoc = (); +} + +struct GenericArray(U::Assoc); + +trait AbiExample { + fn example(); +} +impl AbiExample for GenericArray { + fn example() {} +} +impl AbiExample for T { + default fn example() {} +} + +fn main() { + let _ = GenericArray::<((),)>::example(); +} diff --git a/tests/ui/specialization/specialization-default-projection.stderr b/tests/ui/specialization/specialization-default-projection.current.stderr similarity index 91% rename from tests/ui/specialization/specialization-default-projection.stderr rename to tests/ui/specialization/specialization-default-projection.current.stderr index b8b81876d8131..038c379c43e13 100644 --- a/tests/ui/specialization/specialization-default-projection.stderr +++ b/tests/ui/specialization/specialization-default-projection.current.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-projection.rs:1:12 + --> $DIR/specialization-default-projection.rs:5:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:21:5 + --> $DIR/specialization-default-projection.rs:25:5 | LL | fn generic() -> ::Assoc { | ----------------- expected `::Assoc` because of return type @@ -23,7 +23,7 @@ LL | () = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:28:5 + --> $DIR/specialization-default-projection.rs:32:5 | LL | fn monomorphic() -> () { | -- expected `()` because of return type diff --git a/tests/ui/specialization/specialization-default-projection.next.stderr b/tests/ui/specialization/specialization-default-projection.next.stderr new file mode 100644 index 0000000000000..9111f173a9c87 --- /dev/null +++ b/tests/ui/specialization/specialization-default-projection.next.stderr @@ -0,0 +1,43 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-projection.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:25:5 + | +LL | fn generic() -> ::Assoc { + | ----------------- expected `::Assoc` because of return type +... +LL | () + | ^^ types differ + | + = note: expected associated type `::Assoc` + found unit type `()` + = help: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:32:5 + | +LL | fn monomorphic() -> () { + | -- expected `()` because of return type +... +LL | generic::<()>() + | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | types differ + | + = note: expected unit type `()` + found associated type `<() as Foo>::Assoc` + = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-projection.rs b/tests/ui/specialization/specialization-default-projection.rs index 7f3ae951287ca..4f69ccb59746b 100644 --- a/tests/ui/specialization/specialization-default-projection.rs +++ b/tests/ui/specialization/specialization-default-projection.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we can't project defaulted associated types diff --git a/tests/ui/specialization/specialization-default-types.stderr b/tests/ui/specialization/specialization-default-types.current.stderr similarity index 91% rename from tests/ui/specialization/specialization-default-types.stderr rename to tests/ui/specialization/specialization-default-types.current.stderr index 774ac9536175d..67477f9a6d537 100644 --- a/tests/ui/specialization/specialization-default-types.stderr +++ b/tests/ui/specialization/specialization-default-types.current.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-types.rs:5:12 + --> $DIR/specialization-default-types.rs:9:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:15:9 + --> $DIR/specialization-default-types.rs:19:9 | LL | default type Output = Box; | ----------------------------- associated type is `default` and may be overridden @@ -22,7 +22,7 @@ LL | Box::new(self) found struct `Box` error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:25:5 + --> $DIR/specialization-default-types.rs:29:5 | LL | fn trouble(t: T) -> Box { | ------ expected `Box` because of return type diff --git a/tests/ui/specialization/specialization-default-types.next.stderr b/tests/ui/specialization/specialization-default-types.next.stderr new file mode 100644 index 0000000000000..4f7c47654460e --- /dev/null +++ b/tests/ui/specialization/specialization-default-types.next.stderr @@ -0,0 +1,39 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-types.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:19:9 + | +LL | default type Output = Box; + | ----------------------------- associated type is `default` and may be overridden +LL | default fn generate(self) -> Self::Output { + | ------------ expected `::Output` because of return type +LL | Box::new(self) + | ^^^^^^^^^^^^^^ types differ + | + = note: expected associated type `::Output` + found struct `Box` + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:29:5 + | +LL | fn trouble(t: T) -> Box { + | ------ expected `Box` because of return type +LL | Example::generate(t) + | ^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Box` + found associated type `::Output` + = help: consider constraining the associated type `::Output` to `Box` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-types.rs b/tests/ui/specialization/specialization-default-types.rs index 346471f11e4a8..77817abea127c 100644 --- a/tests/ui/specialization/specialization-default-types.rs +++ b/tests/ui/specialization/specialization-default-types.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + // It should not be possible to use the concrete value of a defaulted // associated type in the impl defining it -- otherwise, what happens // if it's overridden? diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed deleted file mode 100644 index f5dbf0c8b6f4e..0000000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix - -use std::fmt; - -struct Hello; - -impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon - write!(f, "hello") - } -} - -fn main() { - let _ = Hello; -} diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs deleted file mode 100644 index c41880a26f6ec..0000000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix - -use std::fmt; - -struct Hello; - -impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon - write!(f, "hello") - } -} - -fn main() { - let _ = Hello; -} diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr deleted file mode 100644 index 713b071a625a0..0000000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: path separator must be a double colon - --> $DIR/argument-list-from-path-sep-error-129273.rs:8:30 - | -LL | fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { - | ^ - | -help: use a double colon instead - | -LL | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - | + - -error: aborting due to 1 previous error - diff --git a/tests/ui/suggestions/raw-c-string-prefix.rs b/tests/ui/suggestions/raw-c-string-prefix.rs new file mode 100644 index 0000000000000..6af72df40242c --- /dev/null +++ b/tests/ui/suggestions/raw-c-string-prefix.rs @@ -0,0 +1,10 @@ +// `rc` and `cr` are easy to confuse; check that we issue a suggestion to help. + +//@ edition:2021 + +fn main() { + rc"abc"; + //~^ ERROR: prefix `rc` is unknown + //~| HELP: use `cr` for a raw C-string + //~| ERROR: expected one of +} diff --git a/tests/ui/suggestions/raw-c-string-prefix.stderr b/tests/ui/suggestions/raw-c-string-prefix.stderr new file mode 100644 index 0000000000000..5341e3d04e8a1 --- /dev/null +++ b/tests/ui/suggestions/raw-c-string-prefix.stderr @@ -0,0 +1,21 @@ +error: prefix `rc` is unknown + --> $DIR/raw-c-string-prefix.rs:6:5 + | +LL | rc"abc"; + | ^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: use `cr` for a raw C-string + | +LL - rc"abc"; +LL + cr"abc"; + | + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `"abc"` + --> $DIR/raw-c-string-prefix.rs:6:7 + | +LL | rc"abc"; + | ^^^^^ expected one of 8 possible tokens + +error: aborting due to 2 previous errors + diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.rs b/tests/ui/suggestions/struct-field-type-including-single-colon.rs index a3111028895dd..482641fc7cac9 100644 --- a/tests/ui/suggestions/struct-field-type-including-single-colon.rs +++ b/tests/ui/suggestions/struct-field-type-including-single-colon.rs @@ -7,14 +7,14 @@ mod foo { struct Foo { a: foo:A, - //~^ ERROR path separator must be a double colon - //~| ERROR struct `A` is private + //~^ ERROR found single colon in a struct field type path + //~| ERROR expected `,`, or `}`, found `:` } struct Bar { b: foo::bar:B, - //~^ ERROR path separator must be a double colon - //~| ERROR module `bar` is private + //~^ ERROR found single colon in a struct field type path + //~| ERROR expected `,`, or `}`, found `:` } fn main() {} diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr index b9302b0453d5b..5ffc5b40849b6 100644 --- a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr +++ b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr @@ -1,51 +1,40 @@ -error: path separator must be a double colon +error: found single colon in a struct field type path --> $DIR/struct-field-type-including-single-colon.rs:9:11 | LL | a: foo:A, | ^ | -help: use a double colon instead +help: write a path separator here | LL | a: foo::A, | + -error: path separator must be a double colon +error: expected `,`, or `}`, found `:` + --> $DIR/struct-field-type-including-single-colon.rs:9:11 + | +LL | struct Foo { + | --- while parsing this struct +LL | a: foo:A, + | ^ + +error: found single colon in a struct field type path --> $DIR/struct-field-type-including-single-colon.rs:15:16 | LL | b: foo::bar:B, | ^ | -help: use a double colon instead +help: write a path separator here | LL | b: foo::bar::B, | + -error[E0603]: struct `A` is private - --> $DIR/struct-field-type-including-single-colon.rs:9:12 - | -LL | a: foo:A, - | ^ private struct - | -note: the struct `A` is defined here - --> $DIR/struct-field-type-including-single-colon.rs:2:5 - | -LL | struct A; - | ^^^^^^^^^ - -error[E0603]: module `bar` is private - --> $DIR/struct-field-type-including-single-colon.rs:15:13 +error: expected `,`, or `}`, found `:` + --> $DIR/struct-field-type-including-single-colon.rs:15:16 | +LL | struct Bar { + | --- while parsing this struct LL | b: foo::bar:B, - | ^^^ - struct `B` is not publicly re-exported - | | - | private module - | -note: the module `bar` is defined here - --> $DIR/struct-field-type-including-single-colon.rs:3:5 - | -LL | mod bar { - | ^^^^^^^ + | ^ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/syntax-extension-minor.rs b/tests/ui/syntax-extension-minor.rs index cdd572b50fcce..826990a89a533 100644 --- a/tests/ui/syntax-extension-minor.rs +++ b/tests/ui/syntax-extension-minor.rs @@ -1,6 +1,7 @@ //@ run-pass #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated pub fn main() { struct Foo; diff --git a/tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr b/tests/ui/target-cpu/explicit-target-cpu.amdgcn_nocpu.stderr similarity index 100% rename from tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr rename to tests/ui/target-cpu/explicit-target-cpu.amdgcn_nocpu.stderr diff --git a/tests/ui/target-cpu/explicit-target-cpu.avr_nocpu.stderr b/tests/ui/target-cpu/explicit-target-cpu.avr_nocpu.stderr new file mode 100644 index 0000000000000..7480a8ed38f15 --- /dev/null +++ b/tests/ui/target-cpu/explicit-target-cpu.avr_nocpu.stderr @@ -0,0 +1,4 @@ +error: target requires explicitly specifying a cpu with `-C target-cpu` + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-cpu/explicit-target-cpu.rs b/tests/ui/target-cpu/explicit-target-cpu.rs new file mode 100644 index 0000000000000..cd4c2384bc1df --- /dev/null +++ b/tests/ui/target-cpu/explicit-target-cpu.rs @@ -0,0 +1,37 @@ +//! Check that certain target *requires* the user to specify a target CPU via `-C target-cpu`. + +//@ revisions: amdgcn_nocpu amdgcn_cpu + +//@[amdgcn_nocpu] compile-flags: --target=amdgcn-amd-amdhsa +//@[amdgcn_nocpu] needs-llvm-components: amdgpu +//@[amdgcn_nocpu] build-fail + +//@[amdgcn_cpu] compile-flags: --target=amdgcn-amd-amdhsa +//@[amdgcn_cpu] needs-llvm-components: amdgpu +//@[amdgcn_cpu] compile-flags: -Ctarget-cpu=gfx900 +//@[amdgcn_cpu] build-pass + +//@ revisions: avr_nocpu avr_cpu + +//@[avr_nocpu] compile-flags: --target=avr-none +//@[avr_nocpu] needs-llvm-components: avr +//@[avr_nocpu] build-fail + +//@[avr_cpu] compile-flags: --target=avr-none +//@[avr_cpu] needs-llvm-components: avr +//@[avr_cpu] compile-flags: -Ctarget-cpu=atmega328p +//@[avr_cpu] build-pass + +#![crate_type = "rlib"] + +// FIXME(#140038): this can't use `minicore` yet because `minicore` doesn't currently propagate the +// `-C target-cpu` for targets that *require* a `target-cpu` being specified. +#![feature(no_core, lang_items)] +#![no_core] + +#[lang="sized"] +trait Sized {} + +pub fn foo() {} + +//[amdgcn_nocpu,avr_nocpu]~? ERROR target requires explicitly specifying a cpu with `-C target-cpu` diff --git a/tests/ui/traits/deep-norm-pending.rs b/tests/ui/traits/deep-norm-pending.rs new file mode 100644 index 0000000000000..f56c3cfa3eab4 --- /dev/null +++ b/tests/ui/traits/deep-norm-pending.rs @@ -0,0 +1,24 @@ +trait Foo { + type Assoc; +} + +trait Bar { + fn method() -> impl Sized; + //~^ ERROR the trait bound `T: Foo` is not satisfied +} +impl Bar for T +//~^ ERROR the trait bound `T: Foo` is not satisfied +//~| ERROR the trait bound `T: Foo` is not satisfied +where + ::Assoc: Sized, +{ + fn method() {} + //~^ ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/deep-norm-pending.stderr b/tests/ui/traits/deep-norm-pending.stderr new file mode 100644 index 0000000000000..b95b9d7f4aec7 --- /dev/null +++ b/tests/ui/traits/deep-norm-pending.stderr @@ -0,0 +1,130 @@ +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:9:1 + | +LL | / impl Bar for T +LL | | +LL | | +LL | | where +LL | | ::Assoc: Sized, + | |_____________________________^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:9:1 + | +LL | / impl Bar for T +LL | | +LL | | +LL | | where +... | +LL | | } + | |_^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | +note: required for `T` to implement `Bar` + --> $DIR/deep-norm-pending.rs:9:9 + | +LL | impl Bar for T + | ^^^ ^ +... +LL | ::Assoc: Sized, + | ----- unsatisfied trait bound introduced here +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:6:20 + | +LL | fn method() -> impl Sized; + | ^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | +note: required for `T` to implement `Bar` + --> $DIR/deep-norm-pending.rs:9:9 + | +LL | impl Bar for T + | ^^^ ^ +... +LL | ::Assoc: Sized, + | ----- unsatisfied trait bound introduced here +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:8 + | +LL | fn method() {} + | ^^^^^^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/coerce-depth.rs b/tests/ui/traits/next-solver/coerce-depth.rs new file mode 100644 index 0000000000000..c8fc3fcab59d8 --- /dev/null +++ b/tests/ui/traits/next-solver/coerce-depth.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Ensure that a stack of coerce predicates doesn't end up overflowing when they get procesed +// in *reverse* order, which may require O(N) iterations of the fulfillment loop. + +#![recursion_limit = "16"] + +fn main() { + match 0 { + 0 => None, + 1 => None, + 2 => None, + 3 => None, + 4 => None, + 5 => None, + 6 => None, + 7 => None, + 8 => None, + 9 => None, + 10 => None, + 11 => None, + 12 => None, + 13 => None, + 14 => None, + 15 => None, + 16 => None, + 17 => None, + _ => Some(1u32), + }; +} diff --git a/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs new file mode 100644 index 0000000000000..ea18ac54c0557 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs @@ -0,0 +1,44 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// A regression test for trait-system-refactor-initiative#184. +// +// When adding nested goals we replace aliases with infer vars +// and add `AliasRelate` goals to constrain them. When doing this +// for `NormalizesTo` goals, we then first tries to prove the +// `NormalizesTo` goal and then normalized the nested aliases. + +trait Trait { + type Assoc; +} +impl Trait for T { + type Assoc = (); +} + +trait Id { + type This; +} +impl Id for T { + type This = T; +} +trait Relate { + type Alias; +} +impl Relate for T { + type Alias = ::This>>::Assoc; +} + + +fn guide_me>() { + // Normalizing `>::Alias` relates the associated type with an unconstrained + // term. This resulted in a `NormalizesTo(::This>>::Assoc, ?x)` goal. + // We replace `::This` with an infer var `?y`, resulting in the following goals: + // - `NormalizesTo(::Assoc, ?x)` + // - `AliasRelate(::This, ?y)` + // + // When proving the `NormalizesTo` goal first, we incompletely constrain `?y` to `u32`, + // causing an unexpected type mismatch. + let _: >::Alias; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs index 5284220ac38fe..3150d9a88d0e8 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs +++ b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver // When canonicalizing a response in the trait solver, we bail with overflow // if there are too many non-region inference variables. Doing so in normalizes-to diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 376fa22ae1923..f1447cd6a9e2e 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -10,11 +10,8 @@ trait Default { impl Default for T { default type Id = T; - // This will be fixed by #111994 fn intu(&self) -> &Self::Id { - //~^ ERROR type annotations needed - //~| ERROR cannot normalize `::Id: '_` - self //~ ERROR cannot satisfy + self //~ ERROR mismatched types } } @@ -25,6 +22,7 @@ fn transmute, U: Copy>(t: T) -> U { use std::num::NonZero; fn main() { - let s = transmute::>>(0); //~ ERROR cannot satisfy + let s = transmute::>>(0); + //~^ ERROR type mismatch resolving `::Id == Option>` assert_eq!(s, None); } diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index eeb101911c435..8bd290ea19707 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -8,37 +8,32 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error: cannot normalize `::Id: '_` - --> $DIR/specialization-transmute.rs:14:5 +error[E0308]: mismatched types + --> $DIR/specialization-transmute.rs:14:9 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:14:23 - | -LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot infer type for reference `&::Id` - -error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to T` - --> $DIR/specialization-transmute.rs:17:9 - | + | --------- expected `&::Id` because of return type LL | self - | ^^^^ cannot satisfy `::Id normalizes-to T` + | ^^^^ types differ + | + = note: expected reference `&::Id` + found reference `&T` -error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to Option>` - --> $DIR/specialization-transmute.rs:28:13 +error[E0271]: type mismatch resolving `::Id == Option>` + --> $DIR/specialization-transmute.rs:25:50 | LL | let s = transmute::>>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id normalizes-to Option>` + | ------------------------------------ ^ types differ + | | + | required by a bound introduced by this call | note: required by a bound in `transmute` - --> $DIR/specialization-transmute.rs:21:25 + --> $DIR/specialization-transmute.rs:18:25 | LL | fn transmute, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -Some errors have detailed explanations: E0282, E0284. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs index 2bbe784011071..6835c0764d6c8 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs @@ -18,5 +18,5 @@ fn test, U>() {} fn main() { test::(); - //~^ ERROR cannot satisfy `::Id normalizes-to ()` + //~^ ERROR type mismatch resolving `::Id == ()` } diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index e1d785b554bd3..1bcf5eddb5bca 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -8,11 +8,11 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to ()` - --> $DIR/specialization-unconstrained.rs:20:5 +error[E0271]: type mismatch resolving `::Id == ()` + --> $DIR/specialization-unconstrained.rs:20:12 | LL | test::(); - | ^^^^^^^^^^^^^^^^^ cannot satisfy `::Id normalizes-to ()` + | ^^^ types differ | note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:17:20 @@ -22,4 +22,4 @@ LL | fn test, U>() {} error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs b/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs new file mode 100644 index 0000000000000..0f273a6f536e0 --- /dev/null +++ b/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --remap-path-prefix=/=/non-existent +// helper for ../unnecessary-transmute-path-remap-ice-140277.rs + +#[macro_export] +macro_rules! transmute { + ($e:expr) => {{ + let e = $e; + std::mem::transmute(e) + }}; +} diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.fixed new file mode 100644 index 0000000000000..1a0df143cc5ff --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.fixed @@ -0,0 +1,85 @@ +//@ run-rustfix +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { u32::to_ne_bytes(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = u16::from_ne_bytes(*b"01"); + //~^ ERROR + let x: [u8; 2] = u16::to_ne_bytes(x); + //~^ ERROR + let x: u32 = u32::from_ne_bytes(*b"0123"); + //~^ ERROR + let x: [u8; 4] = u32::to_ne_bytes(x); + //~^ ERROR + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = u64::to_ne_bytes(x); + //~^ ERROR + + let y: i16 = i16::from_ne_bytes(*b"01"); + //~^ ERROR + let y: [u8; 2] = i16::to_ne_bytes(y); + //~^ ERROR + let y: i32 = i32::from_ne_bytes(*b"0123"); + //~^ ERROR + let y: [u8; 4] = i32::to_ne_bytes(y); + //~^ ERROR + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = i64::to_ne_bytes(y); + //~^ ERROR + + let z: f32 = f32::from_ne_bytes(*b"0123"); + //~^ ERROR + let z: [u8; 4] = f32::to_ne_bytes(z); + //~^ ERROR + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = f64::to_ne_bytes(z); + //~^ ERROR + + let y: u32 = u32::from('🦀'); + //~^ ERROR + let y: char = char::from_u32_unchecked(y); + //~^ ERROR + + let x: u16 = i16::cast_unsigned(8i16); + //~^ ERROR + let x: i16 = u16::cast_signed(x); + //~^ ERROR + let x: u32 = i32::cast_unsigned(4i32); + //~^ ERROR + let x: i32 = u32::cast_signed(x); + //~^ ERROR + let x: u64 = i64::cast_unsigned(7i64); + //~^ ERROR + let x: i64 = u64::cast_signed(x); + //~^ ERROR + + let y: f32 = f32::from_bits(1u32); + //~^ ERROR + let y: u32 = f32::to_bits(y); + //~^ ERROR + let y: f64 = f64::from_bits(3u64); + //~^ ERROR + let y: u64 = f64::to_bits(2.0); + //~^ ERROR + + let z: bool = (1u8 == 1); + //~^ ERROR + let z: u8 = (z) as u8; + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = (z) as i8; + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs new file mode 100644 index 0000000000000..6b979263c56ca --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -0,0 +1,85 @@ +//@ run-rustfix +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { transmute(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = transmute(*b"01"); + //~^ ERROR + let x: [u8; 2] = transmute(x); + //~^ ERROR + let x: u32 = transmute(*b"0123"); + //~^ ERROR + let x: [u8; 4] = transmute(x); + //~^ ERROR + let x: u64 = transmute(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = transmute(x); + //~^ ERROR + + let y: i16 = transmute(*b"01"); + //~^ ERROR + let y: [u8; 2] = transmute(y); + //~^ ERROR + let y: i32 = transmute(*b"0123"); + //~^ ERROR + let y: [u8; 4] = transmute(y); + //~^ ERROR + let y: i64 = transmute(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = transmute(y); + //~^ ERROR + + let z: f32 = transmute(*b"0123"); + //~^ ERROR + let z: [u8; 4] = transmute(z); + //~^ ERROR + let z: f64 = transmute(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = transmute(z); + //~^ ERROR + + let y: u32 = transmute('🦀'); + //~^ ERROR + let y: char = transmute(y); + //~^ ERROR + + let x: u16 = transmute(8i16); + //~^ ERROR + let x: i16 = transmute(x); + //~^ ERROR + let x: u32 = transmute(4i32); + //~^ ERROR + let x: i32 = transmute(x); + //~^ ERROR + let x: u64 = transmute(7i64); + //~^ ERROR + let x: i64 = transmute(x); + //~^ ERROR + + let y: f32 = transmute(1u32); + //~^ ERROR + let y: u32 = transmute(y); + //~^ ERROR + let y: f64 = transmute(3u64); + //~^ ERROR + let y: u64 = transmute(2.0); + //~^ ERROR + + let z: bool = transmute(1u8); + //~^ ERROR + let z: u8 = transmute(z); + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = transmute(z); + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.stderr new file mode 100644 index 0000000000000..b661aa13c985d --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.stderr @@ -0,0 +1,235 @@ +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:7:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +note: the lint level is defined here + --> $DIR/unnecessary-transmutation.rs:2:9 + | +LL | #![deny(unnecessary_transmutes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:13:22 + | +LL | let x: u16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `u16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:15:26 + | +LL | let x: [u8; 2] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u16::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:17:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `u32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:19:26 + | +LL | let x: [u8; 4] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:21:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `u64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:23:26 + | +LL | let x: [u8; 8] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u64::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:26:22 + | +LL | let y: i16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `i16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:28:26 + | +LL | let y: [u8; 2] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i16::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:30:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `i32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:32:26 + | +LL | let y: [u8; 4] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i32::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:34:22 + | +LL | let y: i64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `i64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:36:26 + | +LL | let y: [u8; 8] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i64::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:39:22 + | +LL | let z: f32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `f32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:41:26 + | +LL | let z: [u8; 4] = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `f32::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:43:22 + | +LL | let z: f64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `f64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:45:26 + | +LL | let z: [u8; 8] = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `f64::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:48:22 + | +LL | let y: u32 = transmute('🦀'); + | ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🦀')` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:50:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(y)` + | + = help: consider `char::from_u32(…).unwrap()` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:53:22 + | +LL | let x: u16 = transmute(8i16); + | ^^^^^^^^^^^^^^^ help: replace this with: `i16::cast_unsigned(8i16)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:55:22 + | +LL | let x: i16 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u16::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:57:22 + | +LL | let x: u32 = transmute(4i32); + | ^^^^^^^^^^^^^^^ help: replace this with: `i32::cast_unsigned(4i32)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:59:22 + | +LL | let x: i32 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u32::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:61:22 + | +LL | let x: u64 = transmute(7i64); + | ^^^^^^^^^^^^^^^ help: replace this with: `i64::cast_unsigned(7i64)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:63:22 + | +LL | let x: i64 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u64::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:66:22 + | +LL | let y: f32 = transmute(1u32); + | ^^^^^^^^^^^^^^^ help: replace this with: `f32::from_bits(1u32)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:68:22 + | +LL | let y: u32 = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `f32::to_bits(y)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:70:22 + | +LL | let y: f64 = transmute(3u64); + | ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(3u64)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:72:22 + | +LL | let y: u64 = transmute(2.0); + | ^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(2.0)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:75:23 + | +LL | let z: bool = transmute(1u8); + | ^^^^^^^^^^^^^^ help: replace this with: `(1u8 == 1)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:77:21 + | +LL | let z: u8 = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `(z) as u8` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:82:21 + | +LL | let z: i8 = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `(z) as i8` + +error: aborting due to 32 previous errors + diff --git a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs new file mode 100644 index 0000000000000..756ce7b3d5074 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs @@ -0,0 +1,10 @@ +//@ aux-crate: zerocopy=unnecessary-transmute-path-remap-ice-140277-trans.rs +//@ check-pass +// tests for a regression in linting for unnecessary transmutes +// where a span was inacessible for snippet procuring, +// when remap-path-prefix was set, causing a panic. + +fn bytes_at_home(x: [u8; 4]) -> u32 { + unsafe { zerocopy::transmute!(x) } +} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs index 4391bf01dc91f..7a540d2a57495 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs @@ -22,7 +22,7 @@ impl Trait for Out { type Out = Out; #[define_opaque(Bar)] fn convert(_i: In) -> Self::Out { - //[next]~^ ERROR: cannot satisfy `Bar == _` + //[next]~^ ERROR: type annotations needed: cannot satisfy `Bar == _` //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}` unreachable!(); } diff --git a/tests/ui/type-alias/type-param.rs b/tests/ui/type-alias/type-param.rs new file mode 100644 index 0000000000000..f8e73518bad6a --- /dev/null +++ b/tests/ui/type-alias/type-param.rs @@ -0,0 +1,11 @@ +//@ run-pass +// This is a smoke test to ensure that type aliases with type parameters +// are accepted by the compiler and that the parameters are correctly +// resolved in the aliased item type. + +#![allow(dead_code)] + +type Foo = extern "C" fn(T) -> bool; +type Bar = fn(T) -> bool; + +fn main() {} diff --git a/tests/ui/type-param.rs b/tests/ui/type-param.rs deleted file mode 100644 index e7cf0e5446bcf..0000000000000 --- a/tests/ui/type-param.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] -#![allow(dead_code)] - - - -type lteq = extern "C" fn(T) -> bool; - -pub fn main() { } diff --git a/tests/ui/typeck/minus-string.rs b/tests/ui/typeck/minus-string.rs new file mode 100644 index 0000000000000..1c0f73a37132c --- /dev/null +++ b/tests/ui/typeck/minus-string.rs @@ -0,0 +1,7 @@ +// Regression test for issue #813. +// This ensures that the unary negation operator `-` cannot be applied to an owned `String`. +// Previously, due to a type-checking bug, this was mistakenly accepted by the compiler. + +fn main() { + -"foo".to_string(); //~ ERROR cannot apply unary operator `-` to type `String` +} diff --git a/tests/ui/minus-string.stderr b/tests/ui/typeck/minus-string.stderr similarity index 69% rename from tests/ui/minus-string.stderr rename to tests/ui/typeck/minus-string.stderr index 153965c810ea7..d2ebcd01ff9c1 100644 --- a/tests/ui/minus-string.stderr +++ b/tests/ui/typeck/minus-string.stderr @@ -1,8 +1,8 @@ error[E0600]: cannot apply unary operator `-` to type `String` - --> $DIR/minus-string.rs:1:13 + --> $DIR/minus-string.rs:6:5 | -LL | fn main() { -"foo".to_string(); } - | ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-` +LL | -"foo".to_string(); + | ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-` | note: the foreign item type `String` doesn't implement `Neg` --> $SRC_DIR/alloc/src/string.rs:LL:COL diff --git a/tests/ui/typeck/pointer-arith-assign.fixed b/tests/ui/typeck/pointer-arith-assign.fixed new file mode 100644 index 0000000000000..907208579e7c6 --- /dev/null +++ b/tests/ui/typeck/pointer-arith-assign.fixed @@ -0,0 +1,20 @@ +//@ run-rustfix +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn test_add_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr = _ptr.wrapping_add(2); //~ ERROR binary assignment operation `+=` cannot be applied to type `*mut u8` [E0368] +} + +fn test_sub_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr = _ptr.wrapping_sub(2); //~ ERROR binary assignment operation `-=` cannot be applied to type `*mut u8` [E0368] +} + +fn main() {} diff --git a/tests/ui/typeck/pointer-arith-assign.rs b/tests/ui/typeck/pointer-arith-assign.rs new file mode 100644 index 0000000000000..0f4ef6ab74c0c --- /dev/null +++ b/tests/ui/typeck/pointer-arith-assign.rs @@ -0,0 +1,20 @@ +//@ run-rustfix +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn test_add_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr += 2; //~ ERROR binary assignment operation `+=` cannot be applied to type `*mut u8` [E0368] +} + +fn test_sub_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr -= 2; //~ ERROR binary assignment operation `-=` cannot be applied to type `*mut u8` [E0368] +} + +fn main() {} diff --git a/tests/ui/typeck/pointer-arith-assign.stderr b/tests/ui/typeck/pointer-arith-assign.stderr new file mode 100644 index 0000000000000..a2bebe5e24757 --- /dev/null +++ b/tests/ui/typeck/pointer-arith-assign.stderr @@ -0,0 +1,31 @@ +error[E0368]: binary assignment operation `+=` cannot be applied to type `*mut u8` + --> $DIR/pointer-arith-assign.rs:10:5 + | +LL | _ptr += 2; + | ----^^^^^ + | | + | cannot use `+=` on type `*mut u8` + | +help: consider using `add` or `wrapping_add` to do pointer arithmetic + | +LL - _ptr += 2; +LL + _ptr = _ptr.wrapping_add(2); + | + +error[E0368]: binary assignment operation `-=` cannot be applied to type `*mut u8` + --> $DIR/pointer-arith-assign.rs:17:5 + | +LL | _ptr -= 2; + | ----^^^^^ + | | + | cannot use `-=` on type `*mut u8` + | +help: consider using `sub` or `wrapping_sub` to do pointer arithmetic + | +LL - _ptr -= 2; +LL + _ptr = _ptr.wrapping_sub(2); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0368`. diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs index 4d1f12e349001..5697f615b9791 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/expanded-exhaustive.rs @@ -12,7 +12,6 @@ #![feature(dyn_star)] #![feature(explicit_tail_calls)] #![feature(gen_blocks)] -#![feature(let_chains)] #![feature(more_qualified_paths)] #![feature(never_patterns)] #![feature(never_type)] @@ -840,6 +839,7 @@ mod types { } /// TyKind::MacCall + #[expect(deprecated)] // concat_idents is deprecated fn ty_mac_call() { let _: concat_idents!(T); let _: concat_idents![T]; diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index d8da941a34077..841edf63c9191 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -13,7 +13,6 @@ #![feature(dyn_star)] #![feature(explicit_tail_calls)] #![feature(gen_blocks)] -#![feature(let_chains)] #![feature(more_qualified_paths)] #![feature(never_patterns)] #![feature(never_type)] @@ -360,6 +359,7 @@ mod expressions { + // concat_idents is deprecated @@ -675,6 +675,7 @@ mod types { /*! there is no syntax for this */ } /// TyKind::MacCall + #[expect(deprecated)] fn ty_mac_call() { let _: T; let _: T; let _: T; } /// TyKind::CVarArgs fn ty_c_var_args() { diff --git a/tests/ui/unpretty/expanded-interpolation.rs b/tests/ui/unpretty/expanded-interpolation.rs index 0c447ae669dd9..95280f97dac14 100644 --- a/tests/ui/unpretty/expanded-interpolation.rs +++ b/tests/ui/unpretty/expanded-interpolation.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Zunpretty=expanded +//@ edition:2024 //@ check-pass -//@ edition: 2015 // This test covers the AST pretty-printer's insertion of parentheses in some // macro metavariable edge cases. Synthetic parentheses (i.e. not appearing in @@ -8,7 +8,6 @@ // Rust syntax. We also test negative cases: the pretty-printer should not be // synthesizing parentheses indiscriminately; only where necessary. -#![feature(let_chains)] #![feature(if_let_guard)] macro_rules! expr { diff --git a/tests/ui/unpretty/expanded-interpolation.stdout b/tests/ui/unpretty/expanded-interpolation.stdout index 10729a96ef595..d46b46b67f41f 100644 --- a/tests/ui/unpretty/expanded-interpolation.stdout +++ b/tests/ui/unpretty/expanded-interpolation.stdout @@ -1,8 +1,7 @@ #![feature(prelude_import)] -#![no_std] //@ compile-flags: -Zunpretty=expanded +//@ edition:2024 //@ check-pass -//@ edition: 2015 // This test covers the AST pretty-printer's insertion of parentheses in some // macro metavariable edge cases. Synthetic parentheses (i.e. not appearing in @@ -10,10 +9,9 @@ // Rust syntax. We also test negative cases: the pretty-printer should not be // synthesizing parentheses indiscriminately; only where necessary. -#![feature(let_chains)] #![feature(if_let_guard)] #[prelude_import] -use ::std::prelude::rust_2015::*; +use std::prelude::rust_2024::*; #[macro_use] extern crate std; diff --git a/triagebot.toml b/triagebot.toml index 0f17d022fbb84..422996cb200dd 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -585,6 +585,11 @@ trigger_files = [ "src/tools/compiletest" ] +[autolabel."A-test-infra-minicore"] +trigger_files = [ + "tests/auxiliary/minicore.rs", +] + [autolabel."A-rustc-dev-guide"] trigger_files = [ "src/doc/rustc-dev-guide", @@ -869,6 +874,10 @@ cc = ["@GuillaumeGomez"] message = "This PR modifies `run-make` tests." cc = ["@jieyouxu"] +[mentions."tests/auxiliary/minicore.rs"] +message = "This PR modifies `tests/auxiliary/minicore.rs`." +cc = ["@jieyouxu"] + [mentions."src/rustdoc-json-types"] message = """ rustdoc-json-types is a **public** (although nightly-only) API. \ @@ -1120,6 +1129,11 @@ cc = ["@ZuseZ4"] [mentions."compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs"] cc = ["@ZuseZ4"] +[mentions."library/core/src/fmt/rt.rs"] +cc = ["@m-ou-se"] +[mentions."compiler/rustc_ast_lowering/src/format.rs"] +cc = ["@m-ou-se"] + [assign] warn_non_default_branch.enable = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"